Skip to main content
Contacts

Search contacts

Search contacts (customers and leads) with a structured filter AST, sorting, and cursor pagination.

POST
/v2/contacts/search

Structured query AST

Each clause has the shape { field, operator, value }. You can pass a single clause or wrap up to 15 in a top-level AND group:

{
  "query": {
    "operator": "AND",
    "value": [
      { "field": "type", "operator": "=", "value": "customer" },
      { "field": "lastActivity", "operator": ">", "value": 1735689600 }
    ]
  },
  "sort": "lastActivity:desc",
  "limit": 20
}

Top-level OR groups and nested groups are not supported in this version.

Supported fields and operators

Field names are camelCase, matching the rest of the v2 public API. Snake_case names that this endpoint originally shipped with (external_id, company_ids, posts_created, comments_created, last_activity, created_at) are still accepted for back-compat but are deprecated — please migrate to camelCase in new code.

FieldTypeOperators
externalIdstring (caller-provided user id)=, !=, IN, NIN
emailstring=, !=, IN, NIN
namestring=, !=, IN, NIN, ~
typeenum (customer, lead)=, !=, IN, NIN
companyIdsid (Featurebase company id)=, !=, IN, NIN
postsCreatednumber=, !=, >, <
commentsCreatednumber=, !=, >, <
lastActivityunix seconds=, !=, >, <
createdAtunix seconds=, !=, >, <

The name ~ "..." operator runs a word-aware substring search via the underlying text index. Other string operators (!~, ^, $) are reserved for future use and currently return 400.

A query consisting only of != or NIN clauses on unbounded fields is rejected with query_too_broad to prevent full-org scans. Combine the negation with at least one positive clause (=, IN, >, <) instead. The closed-enum field type is exempt from this guard.

Contacts that have been merged into another contact (lead-to-customer rollup) are always excluded from results.

Sort

Allowed values:

  • lastActivity:desc (default), lastActivity:asc
  • createdAt:desc, createdAt:asc
  • postsCreated:desc, postsCreated:asc
  • commentsCreated:desc, commentsCreated:asc

Snake_case sort axes (last_activity:desc, created_at:asc, etc.) are accepted for back-compat — the cursor encodes the canonical camelCase identity, so a caller can switch naming conventions mid-pagination without restarting.

The chosen sort axis is encoded in the cursor; switching sort mid-pagination returns 400 invalid_cursor. Restart pagination without a cursor when changing sort.

Pagination

Cursor-based, limit between 1 and 100 (default 10). totalCount is approximate and capped at 5000; totalCountCapped is true when the real count may be higher.

Response

Returns a standard list envelope with contact rows identical to GET /v2/contacts/{id}.

Version Availability

This endpoint is only available in API version 2026-01-01.nova and newer.

Authorizationstringheaderrequired

API key as Bearer token. Use: Authorization: Bearer sk_...

Featurebase-Versionenum<string>header

API version for this request. Defaults to your organization's configured API version if not specified.

Available options: 2026-08-19.orbit, 2026-01-01.nova, 2025-12-12.clover
Example: 2026-08-19.orbit

Body

application/json
cursorstring

An opaque cursor for pagination. Use the nextCursor value from a previous response to fetch the next page of results.

Maximum string length: 512
Example: eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9
limitintegerdefault:10

A limit on the number of objects to be returned, between 1 and 100.

Required range: 1 <= x <= 100
Example: 10
queryobject

Structured filter AST. Either a single filter clause or one top-level AND group (max 15 clauses). Top-level OR groups are not yet supported.

sortenum<string>

Sort field + direction. Defaults to lastActivity:desc (matches the dashboard's default contact ordering). The chosen sort axis is encoded in the cursor; switching sort mid-pagination returns 400 invalid_cursor. Snake_case names (last_activity:desc, posts_created:asc, …) are accepted for back-compat — prefer camelCase in new code.

Available options: createdAt:desc, createdAt:asc, lastActivity:desc, lastActivity:asc, postsCreated:desc, postsCreated:asc, commentsCreated:desc, commentsCreated:asc, created_at:desc, created_at:asc, last_activity:desc, last_activity:asc, posts_created:desc, posts_created:asc, comments_created:desc, comments_created:asc
Example: lastActivity:desc

Response

application/json

Success

dataobject[]required

Array of search results

nextCursorstring | nullrequired

Cursor for fetching the next page (null if no more results)

Example: eyJpZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMSJ9
objectenum<string>required

Object type identifier

Available options: list
Example: list
totalCountinteger

Total number of contacts matching the query, capped at 5000. When the actual total is at or above the cap, totalCountCapped is true and the value is exactly the cap.

Example: 42
totalCountCappedboolean

True when totalCount is exactly the cap and the real count may be higher.

Example: false