> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blnkfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search via Database Filtering

> Learn how to filter and query your data with precision using Blnk's Filter API.

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

<Info>Available in version 0.13.2 and later.</Info>

Blnk's Filter API lets you retrieve exactly the data you need from any collection. Use server-side filters with a clean JSON interface to build precise, composable queries.

The Filter API is available for all collections: ledgers, balances, transactions, and identities.

***

## Filter endpoints

Each collection has a dedicated filter endpoint:

| Collection   | Endpoint                    |
| ------------ | --------------------------- |
| Ledgers      | `POST /ledgers/filter`      |
| Balances     | `POST /balances/filter`     |
| Transactions | `POST /transactions/filter` |
| Identities   | `POST /identities/filter`   |

***

## Request format

Send a POST request with a JSON body containing your filters, sorting, and pagination options:

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  curl -X POST "https://YOUR_BLNK_INSTANCE_URL/transactions/filter" \
    -H "X-Blnk-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "logical_operator": "and",
      "filters": [
        {
          "field": "status",
          "operator": "eq",
          "value": "APPLIED"
        },
        {
          "field": "currency",
          "operator": "in",
          "values": ["USD", "EUR"]
        }
      ],
      "sort_by": "created_at",
      "sort_order": "desc",
      "include_count": true,
      "limit": 20,
      "offset": 0
    }'
  ```

  ```go Go wrap theme={"system"}
  results, resp, err := client.Transaction.Filter(blnkgo.FilterParams{
    LogicalOperator: "and",
    Filters: []blnkgo.Filter{
      {
        Field:    "status",
        Operator: blnkgo.OpEqual,
        Value:    "APPLIED",
      },
      {
        Field:    "currency",
        Operator: blnkgo.OpIn,
        Values:   []string{"USD", "EUR"},
      },
    },
    SortBy:       "created_at",
    SortOrder:    "desc",
    IncludeCount: true,
    Limit:        20,
    Offset:       0,
  })
  ```
</CodeGroup>

| Parameter          | Type    | Required | Description                                        |
| ------------------ | ------- | -------- | -------------------------------------------------- |
| `filters`          | array   | Yes      | Array of filter objects                            |
| `sort_by`          | string  | No       | Field to sort by (default: `created_at`)           |
| `sort_order`       | string  | No       | Sort direction: `asc` or `desc` (default: `desc`)  |
| `include_count`    | boolean | No       | Include total count in response (default: `false`) |
| `limit`            | integer | No       | Max records to return (default: `20`, max: `100`)  |
| `offset`           | integer | No       | Records to skip (default: `0`)                     |
| `logical_operator` | string  | No       | Combine filters with `"and"` (default) or `"or"`   |

***

## Response format

The response format depends on whether `include_count` is enabled.

<CodeGroup>
  ```json include_count: true wrap theme={"system"}
  {
    "data": [
      {
        "transaction_id": "txn_abc123",
        "amount": 15000,
        "currency": "USD",
        "status": "APPLIED",
        "source": "bln_source123",
        "destination": "bln_dest456",
        "created_at": "2024-01-15T10:30:00Z"
      }
    ],
    "total_count": 150
  }
  ```

  ```json include_count: false (default) wrap theme={"system"}
  [
    {
      "transaction_id": "txn_abc123",
      "amount": 15000,
      "currency": "USD",
      "status": "APPLIED",
      "source": "bln_source123",
      "destination": "bln_dest456",
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
  ```
</CodeGroup>

<Warning>
  If `include_count` is `true`, an additional query will run to fetch the total count, which may impact performance on large datasets.
</Warning>

***

## Filter object

Each filter in the `filters` array follows this structure:

| Field      | Type   | Required             | Description                           |
| ---------- | ------ | -------------------- | ------------------------------------- |
| `field`    | string | Yes                  | The field to filter on                |
| `operator` | string | Yes                  | The comparison operator               |
| `value`    | any    | For single-value ops | The value to compare against          |
| `values`   | array  | For in/between ops   | Array of values (for those operators) |

***

## Supported operators

Blnk provides a range of operators to match, compare, and filter your data precisely:

| Operator    | Description                      | Example                                                              |
| ----------- | -------------------------------- | -------------------------------------------------------------------- |
| `eq`        | Equal to                         | `{"field": "status", "operator": "eq", "value": "APPLIED"}`          |
| `ne`        | Not equal to                     | `{"field": "status", "operator": "ne", "value": "VOID"}`             |
| `gt`        | Greater than                     | `{"field": "amount", "operator": "gt", "value": 1000}`               |
| `gte`       | Greater than or equal            | `{"field": "amount", "operator": "gte", "value": 1000}`              |
| `lt`        | Less than                        | `{"field": "amount", "operator": "lt", "value": 5000}`               |
| `lte`       | Less than or equal               | `{"field": "amount", "operator": "lte", "value": 5000}`              |
| `in`        | In a set of values               | `{"field": "currency", "operator": "in", "values": ["USD", "EUR"]}`  |
| `between`   | Between two values               | `{"field": "amount", "operator": "between", "values": [1000, 5000]}` |
| `like`      | Pattern match (case-sensitive)   | `{"field": "name", "operator": "like", "value": "%savings%"}`        |
| `ilike`     | Pattern match (case-insensitive) | `{"field": "name", "operator": "ilike", "value": "%USD%"}`           |
| `isnull`    | Field is null                    | `{"field": "identity_id", "operator": "isnull"}`                     |
| `isnotnull` | Field is not null                | `{"field": "identity_id", "operator": "isnotnull"}`                  |

<Tip>
  For `like` and `ilike` operators, use `%` as a wildcard. `%savings%` matches any value containing "savings". For `isnull`/`isnotnull`, the `value` property is ignored.
</Tip>

***

## Sorting

Control how results are ordered using the `sort_by` and `sort_order` parameters:

| Parameter    | Values            | Default      | Description      |
| ------------ | ----------------- | ------------ | ---------------- |
| `sort_by`    | Any indexed field | `created_at` | Field to sort by |
| `sort_order` | `asc`, `desc`     | `desc`       | Sort direction   |

<Warning>
  Only indexed fields (e.g., `created_at`, `balance_id`, etc.) are sortable. Sorting on non-indexed fields returns a 400 error.
</Warning>

For information on which fields are indexed and how to add custom indexes, see [Performance Tuning](/search/db/performance).

***

## Pagination

Manage large result sets with pagination:

| Parameter | Default | Max   | Description           |
| --------- | ------- | ----- | --------------------- |
| `limit`   | `20`    | `100` | Max records to return |
| `offset`  | `0`     | -     | Records to skip       |

<Tip>
  To iterate through all pages, increment the `offset` by your `limit` value with each request. For example, with `limit=50`: page 1 uses `offset=0`, page 2 uses `offset=50`, page 3 uses `offset=100`, and so on.
</Tip>

***

## Filterable fields

Each collection exposes specific fields you can filter on. Select a tab below to see the available fields for each collection.

<Tabs>
  <Tab title="Transactions">
    | Field            | Type      | Description                            |
    | ---------------- | --------- | -------------------------------------- |
    | `transaction_id` | string    | Unique transaction identifier          |
    | `reference`      | string    | Custom reference                       |
    | `amount`         | number    | Transaction amount                     |
    | `currency`       | string    | Currency code                          |
    | `status`         | string    | QUEUED, APPLIED, INFLIGHT, VOID        |
    | `source`         | string    | Source balance ID                      |
    | `destination`    | string    | Destination balance ID                 |
    | `created_at`     | timestamp | Creation time                          |
    | `description`    | string    | Transaction description                |
    | `balance_id`     | string    | Related balance (for internal mapping) |
    | `meta_data.*`    | any       | Custom metadata fields                 |
  </Tab>

  <Tab title="Balances">
    | Field            | Type      | Description            |
    | ---------------- | --------- | ---------------------- |
    | `balance_id`     | string    | Balance identifier     |
    | `ledger_id`      | string    | Parent ledger          |
    | `identity_id`    | string    | Associated identity    |
    | `indicator`      | string    | Balance alias          |
    | `currency`       | string    | Currency code          |
    | `balance`        | number    | Current balance        |
    | `credit_balance` | number    | Total credits          |
    | `debit_balance`  | number    | Total debits           |
    | `created_at`     | timestamp | Creation time          |
    | `meta_data.*`    | any       | Custom metadata fields |
  </Tab>

  <Tab title="Ledgers">
    | Field         | Type      | Description            |
    | ------------- | --------- | ---------------------- |
    | `ledger_id`   | string    | Ledger identifier      |
    | `name`        | string    | Ledger name            |
    | `created_at`  | timestamp | Creation time          |
    | `meta_data.*` | any       | Custom metadata fields |
  </Tab>

  <Tab title="Identities">
    | Field           | Type      | Description            |
    | --------------- | --------- | ---------------------- |
    | `identity_id`   | string    | Identity identifier    |
    | `first_name`    | string    | First name             |
    | `last_name`     | string    | Last name              |
    | `email_address` | string    | Email                  |
    | `identity_type` | string    | Identity type          |
    | `category`      | string    | Category               |
    | `country`       | string    | Country                |
    | `created_at`    | timestamp | Creation time          |
    | `meta_data.*`   | any       | Custom metadata fields |
  </Tab>
</Tabs>

***

## Filtering metadata

To filter on custom metadata, use dot notation for the field name:

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  curl -X POST "https://YOUR_BLNK_INSTANCE_URL/transactions/filter" \
    -H "X-Blnk-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "filters": [
        {
          "field": "meta_data.customer_type",
          "operator": "eq",
          "value": "premium"
        }
      ]
    }'
  ```

  ```typescript TypeScript wrap theme={"system"}
  const response = await blnk.Search.filter(
    {
      filters: [
        {
          field: 'meta_data.customer_type',
          operator: 'eq',
          value: 'premium',
        },
      ],
    },
    'transactions',
  );
  ```

  ```go Go wrap theme={"system"}
  results, resp, err := client.Transaction.Filter(blnkgo.FilterParams{
    Filters: []blnkgo.Filter{
      {
        Field:    "meta_data.customer_type",
        Operator: blnkgo.OpEqual,
        Value:    "premium",
      },
    },
  })
  ```
</CodeGroup>

```json Response wrap theme={"system"}
{
  "data": [
    {
      "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
      "meta_data": {
        "customer_type": "premium"
      }
    }
  ]
}
```

***

## Error handling

<Info>
  Structured errors are available from Blnk Core 0.15.0 and later.
</Info>

Invalid filter requests return `400 Bad Request` with `error_detail.code` set to `GEN_BAD_REQUEST`. The `error` message includes details about the field, operator, or value that failed validation.

| Code                    | When it happens                                                                                                                                        |
| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GEN_BAD_REQUEST`       | Unknown field for the collection, unsupported operator, wrong value shape, unsupported operator on a restricted field, or an invalid `meta_data.*` key |
| `GEN_MALFORMED_REQUEST` | Request body is not valid JSON                                                                                                                         |

```json 400 Bad Request wrap theme={"system"}
{
  "error": "Invalid filter: invalid operator 'resembles' for field 'amount': must be one of eq, ne, gt, gte, lt, lte, in, between, like, ilike, isnull, isnotnull",
  "error_detail": {
    "code": "GEN_BAD_REQUEST",
    "message": "Invalid filter: invalid operator 'resembles' for field 'amount': must be one of eq, ne, gt, gte, lt, lte, in, between, like, ilike, isnull, isnotnull",
    "details": {}
  }
}
```

To resolve the error:

| Code                    | What to do                                                                                                                                                              |
| :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GEN_BAD_REQUEST`       | Check the field name against [Filterable fields](#filterable-fields), use a [supported operator](#supported-operators), and match the value shape the operator expects. |
| `GEN_MALFORMED_REQUEST` | Fix JSON syntax in the request body before resubmitting.                                                                                                                |

For the full error catalogue, see [API error codes](/advanced/error-codes).

***

## Best practices

1. **Use specific filters**: The more specific your filters, the faster your queries. Combine multiple filters to narrow results.

2. **Paginate large results**: Always use pagination when expecting many results. This improves performance and reduces response times.

3. **Use `include_count` sparingly**: Only request counts when necessary, as it adds overhead on large datasets.

4. **Use appropriate operators**: Use `eq` for exact matches, `ilike` for case-insensitive searches, and `in` for multiple values.

5. **Consider Typesense for complex queries**: For advanced operations like grouping, joins, or full-text search, consider using the [Search API](/search/typesense/introduction) instead.

6. **Optimize indexes**: For frequently filtered or sorted fields, ensure proper indexes are in place. See [Performance Tuning](/search/db/performance) for details.

***

## Need help?

We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool.

To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx).

<CtaCallout title="Connect your ledger to Blnk Cloud" href="https://cloud.blnkfinance.com/auth/sign-up?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=need-help" buttonLabel="Open Blnk Cloud" trackingEvent="clicked_cloud_signup">
  Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard.
</CtaCallout>
