filter object. Write it once and send it to count, preview, insights, or exports on either surface without changes.
A filter is plain JSON — a nested tree of objects with a type field. It is not GraphQL, and there is no query language to learn or GraphQL endpoint to call. Build it with whatever your language uses for JSON.
The one difference is which columns each surface accepts: contact endpoints take person, company, and job fields, while company endpoints take company fields only. See Search for companies for what that rules out. Every column is documented in the Person-Organization-Insight data dictionary.
Start with one condition
A filter is agroup containing a list of children. The simplest one holds a single condition:
POST /v2/contact/count to check the result before adding complexity.
Combine conditions
Add more entries tochildren. The group’s op decides how they combine — AND requires all of them, OR requires any.
eq reads value, a single item. in reads valueList, an array. Sending the wrong one is the most common cause of a 422.
Nest groups for mixed logic
To express “in the United States and (software or financial services)”, nest a group inside a group:"negate": true on any group to exclude everything it matches.
Add SmartSearch nodes
SmartSearch nodes match on meaning rather than characters. They are ordinary children — they sit in the samechildren list as conditions, nest inside groups, and negate the same way.
Job title — the role a person holds, however it’s worded:
match presets — exact, similar, related, broad — or a raw maxDistance if you’d rather set your own threshold. Never both.
Node reference
Four node types, told apart bytype. Every node also accepts joinWithPrevious (AND or OR) to control how it binds to the preceding sibling, and an optional id of your own for round-tripping state from an interface.
A filter using everything
Conditions, a nestedOR, two SmartSearch types, and a negated exclusion — in one tree:
competitor.com.
See SmartSearch for how the presets are calibrated per target and more worked combinations.
Operator reference
Every node also accepts
joinWithPrevious (AND or OR) to control how it binds to the preceding sibling, and an optional id of your own for round-tripping state from an interface.
Find available columns and values
GET /v2/contact/columns returns every column, what it holds, and which ones offer a fixed set of values. Add ?include=values to receive those value lists in the same response. GET /v2/contact/company/columns does the same for the company surface, listing company fields only.
Request the catalog once at startup and cache it. Building filter inputs from
distinctValues avoids searches that silently match nothing because a value was spelled differently than stored.
GET /v2/contact/columns/{column}/values returns values for a single multiselect column, with an optional search parameter. Open-ended columns — job title, company name, email address — return 400; filter those with contains instead.
Filter limits
Handle filter errors
A rejected filter returns422. As with every validation error across the API, detail is an array identifying each problem, and code names the rule that was broken:
loc points at the part of the filter that failed, and msg explains what’s wrong. See Rate limits and error codes for the shared error format.
Include
request_id when contacting support@revenuebase.ai — it identifies the individual request in our logs.
