Filtering
POST …/records/query/ and POST …/records/aggregate/ take a filter tree built
from three node types: $and, $or, and a leaf.
A tree is scoped to the one object type in the URL, so every property in it
must belong to that type. This example queries ahoy_company:
{ "filter": { "$and": [ { "property": "ahoy_industry", "operator": "eq", "value": "Software" }, { "$or": [ { "property": "ahoy_country", "operator": "in", "value": ["Ireland", "United Kingdom"] }, { "property": "ahoy_annual_revenue", "operator": "gte", "value": "50000" } ]} ] }, "sort": { "property": "ahoy_created_at", "direction": "desc" }, "properties": ["ahoy_name", "ahoy_domain", "ahoy_annual_revenue"]}Filters are typed
Section titled “Filters are typed”Operators and values are validated against each property’s field type — not
accepted blindly and failed later. A bare number against a currency property, or
an unparseable date, is rejected as
request_invalid with
reason: "invalid_filter" and a field pointer to the offending node.
GET /rest/v1/field_types/ is the authority on what each type supports: its
value shape, valid operators, available aggregations, and whether it can be
sorted on.
Related failures:
reason | Cause |
|---|---|
unknown_property | No such property on this object type |
operator_not_supported | Operator invalid for that property’s field type |
invalid_filter | Value didn’t match the field type |
unsupported_sort | That property can’t be sorted on |
unsupported_aggregation | That aggregation isn’t available for the field type |
Limits
Section titled “Limits”The tree is capped, deliberately, so one query can’t degrade the service:
- Nesting depth: 8. Deeper returns
reason: "filter_too_deep". in/not_inlists: 100 values. Longer returnsreason: "invalid_filter".- Group count is capped. Too many
$and/$orgroups returnsreason: "too_many_filter_groups".
A structurally malformed tree — an unknown key, an empty $and, a missing
value, an operator outside the vocabulary — is a 400. A tree that is
well-formed but semantically wrong is a 422. Both carry request_invalid.
Search
Section titled “Search”search runs a free-text match alongside the filter tree, and composes with it:
{ "search": "acme", "filter": { "property": "ahoy_industry", "operator": "eq", "value": "Software" } }Currency needs exchange rates
Section titled “Currency needs exchange rates”Sorting or filtering on a currency property requires exchange rates for that
day. On the rare occasions they aren’t available, the request returns
service_unavailable with
reason: "exchange_rate_unavailable" and a Retry-After header. Retry rather
than treating it as a bad request.