Search & metadata

Search via database filtering

Filter ledgers, balances, transactions, and identities with the Filter API.

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:

CollectionEndpoint
LedgersPOST /ledgers/filter
BalancesPOST /balances/filter
TransactionsPOST /transactions/filter
IdentitiesPOST /identities/filter

Request format

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

curl -X POST "http://localhost:5001/transactions/filter" \
  -H "X-blnk-key: <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
  }'
const response = await blnk.Search.filter(
  {
    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,
  },
  'transactions',
);
results, _, _ := 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,
})
response = blnk.search.filter(
  {
    "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,
  },
  "transactions",
)
ApiResponse<JsonNode> response = blnk.search().filter(
    FilterParams.create()
        .logicalOperator("and")
        .filters(List.of(
            FilterCondition.create()
            .field("status")
            .operator("eq")
            .value("APPLIED"),
            FilterCondition.create()
            .field("currency")
            .operator("in")
            .values(List.of("USD", "EUR"))
        ))
        .sortBy("created_at")
        .sortOrder("desc")
        .includeCount(true)
        .limit(20)
        .offset(0),
    "transactions");
ParameterTypeRequiredDescription
filtersarrayYesArray of filter objects
sort_bystringNoField to sort by (default: created_at)
sort_orderstringNoSort direction: asc or desc (default: desc)
include_countbooleanNoInclude total count in response (default: false)
limitintegerNoMax records to return.
offsetintegerNoRecords to skip (default: 0)
logical_operatorstringNoCombine filters with "and" (default) or "or"

Response format

The response format depends on whether include_count is enabled.

{
  "data": [
    {
      "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
      "amount": 15000,
      "currency": "USD",
      "status": "APPLIED",
      "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
      "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
      "created_at": "2024-01-15T10:30:00Z"
    }
  ],
  "total_count": 150
}
[
  {
    "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
    "amount": 15000,
    "currency": "USD",
    "status": "APPLIED",
    "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
    "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
    "created_at": "2024-01-15T10:30:00Z"
  }
]

Filter object

Each filter in the filters array follows this structure:

FieldTypeRequiredDescription
fieldstringYesThe field to filter on
operatorstringYesThe comparison operator
valueanyFor single-value opsThe value to compare against
valuesarrayFor in/between opsArray of values (for those operators)

Supported operators

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

OperatorDescriptionExample
eqEqual to{"field": "status", "operator": "eq", "value": "APPLIED"}
neNot equal to{"field": "status", "operator": "ne", "value": "VOID"}
gtGreater than{"field": "amount", "operator": "gt", "value": 1000}
gteGreater than or equal{"field": "amount", "operator": "gte", "value": 1000}
ltLess than{"field": "amount", "operator": "lt", "value": 5000}
lteLess than or equal{"field": "amount", "operator": "lte", "value": 5000}
inIn a set of values{"field": "currency", "operator": "in", "values": ["USD", "EUR"]}
betweenBetween two values{"field": "amount", "operator": "between", "values": [1000, 5000]}
likePattern match (case-sensitive){"field": "name", "operator": "like", "value": "%savings%"}
ilikePattern match (case-insensitive){"field": "name", "operator": "ilike", "value": "%USD%"}
isnullField is null{"field": "identity_id", "operator": "isnull"}
isnotnullField is not null{"field": "identity_id", "operator": "isnotnull"}

Sorting

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

ParameterValuesDefaultDescription
sort_byAny indexed fieldcreated_atField to sort by
sort_orderasc, descdescSort direction

For information on which fields are indexed and how to add custom indexes, see Performance Tuning.

Pagination

Manage large result sets with pagination:

ParameterDefaultMaxDescription
limit20100Max records to return. 0 means 20. Above 100 or below 0 returns 400.
offset0-Records to skip

Filterable fields

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

FieldTypeDescription
transaction_idstringUnique transaction identifier
referencestringCustom reference
amountnumberTransaction amount
currencystringCurrency code
statusstringQUEUED, APPLIED, INFLIGHT, VOID
sourcestringSource balance ID
destinationstringDestination balance ID
created_attimestampCreation time
effective_datetimestampFinancial date of the transaction. Defaults to created_at when unset
descriptionstringTransaction description
balance_idstringRelated balance (for internal mapping)
meta_data.*anyCustom metadata fields
FieldTypeDescription
balance_idstringBalance identifier
ledger_idstringParent ledger
identity_idstringAssociated identity
indicatorstringBalance alias
currencystringCurrency code
balancenumberCurrent balance
credit_balancenumberTotal credits
debit_balancenumberTotal debits
created_attimestampCreation time
meta_data.*anyCustom metadata fields
FieldTypeDescription
ledger_idstringLedger identifier
namestringLedger name
created_attimestampCreation time
meta_data.*anyCustom metadata fields
FieldTypeDescription
identity_idstringIdentity identifier
first_namestringFirst name
last_namestringLast name
email_addressstringEmail
identity_typestringIdentity type
categorystringCategory
countrystringCountry
created_attimestampCreation time
meta_data.*anyCustom metadata fields

Filtering metadata

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

curl -X POST "http://localhost:5001/transactions/filter" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      {
        "field": "meta_data.customer_type",
        "operator": "eq",
        "value": "premium"
      }
    ]
  }'
const response = await blnk.Search.filter(
  {
    filters: [
      {
        field: 'meta_data.customer_type',
        operator: 'eq',
        value: 'premium',
      },
    ],
  },
  'transactions',
);
results, _, _ := client.Transaction.Filter(blnkgo.FilterParams{
    Filters: []blnkgo.Filter{
        {
            Field: "meta_data.customer_type",
            Operator: blnkgo.OpEqual,
            Value: "premium",
        },
    },
})
response = blnk.search.filter(
  {
    "filters": [
      {
        "field": "meta_data.customer_type",
        "operator": "eq",
        "value": "premium",
      },
    ],
  },
  "transactions",
)
ApiResponse<JsonNode> response = blnk.search().filter(
    FilterParams.create()
        .filters(List.of(
            FilterCondition.create()
            .field("meta_data.customer_type")
            .operator("eq")
            .value("premium")
        )),
    "transactions");
{
  "data": [
    {
      "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
      "meta_data": {
        "customer_type": "premium"
      }
    }
  ]
}

Error handling

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.

CodeWhen it happens
GEN_BAD_REQUESTUnknown field for the collection, unsupported operator, wrong value shape, unsupported operator on a restricted field, or an invalid meta_data.* key
GEN_MALFORMED_REQUESTRequest body is not valid JSON
{
  "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:

CodeWhat to do
GEN_BAD_REQUESTCheck the field name against Filterable fields, use a supported operator, and match the value shape the operator expects.
GEN_MALFORMED_REQUESTFix JSON syntax in the request body before resubmitting.

For the full error catalogue, see API 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 instead.

  6. Optimize indexes: For frequently filtered or sorted fields, ensure proper indexes are in place. See Performance Tuning for details.