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:
| 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:
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");| 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. |
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.
{
"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:
| 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"} |
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 |
For information on which fields are indexed and how to add custom indexes, see Performance Tuning.
Pagination
Manage large result sets with pagination:
| Parameter | Default | Max | Description |
|---|---|---|---|
limit | 20 | 100 | Max records to return. 0 means 20. Above 100 or below 0 returns 400. |
offset | 0 | - | 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.
| 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 |
effective_date | timestamp | Financial date of the transaction. Defaults to created_at when unset |
description | string | Transaction description |
balance_id | string | Related balance (for internal mapping) |
meta_data.* | any | Custom metadata fields |
| 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 |
| Field | Type | Description |
|---|---|---|
ledger_id | string | Ledger identifier |
name | string | Ledger name |
created_at | timestamp | Creation time |
meta_data.* | any | Custom metadata fields |
| Field | Type | Description |
|---|---|---|
identity_id | string | Identity identifier |
first_name | string | First name |
last_name | string | Last name |
email_address | string | |
identity_type | string | Identity type |
category | string | Category |
country | string | Country |
created_at | timestamp | Creation time |
meta_data.* | any | Custom 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.
| 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 |
{
"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, use a supported operator, 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.
Best practices
-
Use specific filters: The more specific your filters, the faster your queries. Combine multiple filters to narrow results.
-
Paginate large results: Always use pagination when expecting many results. This improves performance and reduces response times.
-
Use
include_countsparingly: Only request counts when necessary, as it adds overhead on large datasets. -
Use appropriate operators: Use
eqfor exact matches,ilikefor case-insensitive searches, andinfor multiple values. -
Consider Typesense for complex queries: For advanced operations like grouping, joins, or full-text search, consider using the Search API instead.
-
Optimize indexes: For frequently filtered or sorted fields, ensure proper indexes are in place. See Performance Tuning for details.