Skip to main content
Every deep, filter, and lookalike search produces a search_id. These endpoints read its results: poll a task with GET /search/status/{task_id}, page through rows with POST /search/query, build a CSV with POST /search/export, download it with GET /search/download, and read your result ceiling with GET /search/limits.

API Reference

See the full request/response schema and parameters in the API Reference.

Get search status

Poll the progress and result of a deep search or an export.
task_id is the value returned by Start Deep Search (agentic_search_{search_id}) or by Export (search_export_{search_id}). Lookalike tasks use GET /search/lookalike/{task_id}; this endpoint refuses them. GET /search/deep-search-status/{task_id} is a deprecated alias of this endpoint, past its May 12, 2026 sunset date. It still returns the same response, with Deprecation and Sunset headers.

Task statuses

running is the only in-progress status. Poll every 10 seconds or slower, and stop on any other status, including values not listed here. A search that finds nothing does not always end completed:
  • A query_only deep search with no matches ends completed with total_results: 0.
  • A CSV deep search or an export with no rows to write ends failed, with a message in error. This includes searches whose every row was excluded.
A deep search started above your organization’s concurrency limit also ends failed. See Concurrency and run time.

Status response

Fields without a value are returned as null.
For a complete polling loop that honors Retry-After and stops at a deadline, see Deep Search → Example usage.

Query search results

Page through the results of a completed search as JSON.
Send the search_id on the first request, then send only {"cursor": "..."} for each next page:
POST /search/query also runs new searches from filters or a natural language query. For those inputs, see Filter Search → Search query. A request with search_id runs the saved search’s query again and starts a new set of pages under the same search_id. A replay carries:
  • The saved query and its mode.
  • For a lookalike search, its ranking and its seed people, who stay excluded from results.
A replay does not carry:
  • max_results. It defaults to 1,000 for API keys, whatever the original search used. Send it again.
  • page_size, output_shape, and exclusions. Send them on the first request.
The saved search must be completed. Replaying a search that is still running, or one that failed, returns an error. A replay does not save a new search.

Paginate with cursors

Each page returns has_more and, while more rows remain, a next_cursor. Send {"cursor": "..."} alone to get the next page. Mode, page size, output shape, and exclusions carry over.
  • Stop when has_more is false. The last page omits next_cursor and cursor_expires_in_seconds; they are not returned as null.
  • A page can hold fewer rows than page_size, even zero, while has_more is true. Keep paging.
  • Each cursor works once. Sending a cursor again after its page was served fails with Cursor expired. Start a new search.
  • Send each cursor only after the previous response arrives. While a request with a cursor is still running, a second request with the same cursor returns 409.
  • A cursor expires cursor_expires_in_seconds after it is issued (30 minutes). It stops working sooner if an exclusion list it applies is deleted, and it can expire early in other cases. When a cursor fails, start again from the search_id.
  • If a cursor request returns 429 or 503, wait for Retry-After and send the same cursor again. If that fails with Cursor expired, start again from the search_id.
  • A cursor request accepts only cursor. Sending a query, paging, or exclusion field with it fails the request. mode, persist_history, and the location-expansion fields are ignored, as are fields sent as null or false.
  • A cursor works only for the organization that created it.

Query response

Fields without a value are omitted from the response, not returned as null. Inside results, rows keep their null values.

Fields that change what results mean

Check these fields on every page. When present, the results do not cover everything the query asked for. Results and fields differ by account, and these fields appear only on some accounts. GET /search/filter-capabilities lists the fields your account can filter on.

Export search results

Generate a CSV from any completed search: deep, filter, or lookalike.
An export runs the saved query again, as a replay does, so its rows can differ from the CSV a deep search wrote.
  1. Call POST /search/export and save the task_id.
  2. Poll GET /search/status/{task_id} until it is completed, then read resource_handle_id.
  3. Download the CSV with GET /search/download.
One export per search_id runs at a time. While one runs, another export request for the same search fails; poll search_export_{search_id} instead. An export with parsed_query gets a new ID each time.

CSV columns

A CSV has one row per result, with the same columns as output_shape: "workflow" rows. On accounts where lookalike results carry ranking fields, lookalike CSVs add similarityScore (0–100), similarityRank, and similaritySignals.

Download search results

Get a signed URL for the CSV of a completed CSV deep search or export.
The response contains url plus the storage bucket and path of the file.
Fetch the returned url with your x-api-key header; the signed token does not replace authentication. Disable redirects when sending your API key.
Signed URLs expire after 15 minutes. Download the file right away or request a new URL.

Spreadsheet-safe CSV

By default, the file is escaped so that opening it in a spreadsheet never runs a formula:
  • Every field is quoted.
  • A cell that starts with =, +, -, @, a tab, or a line break (or a full-width =, +, -, or @) gets a leading '. A phone number +14155550123 arrives as '+14155550123. Negative numbers such as -12.5 are left as they are.
  • The response carries the header X-Spreadsheet-Safe: 1.
To read the file as data, append ?spreadsheet_safe=false to the url. The file then arrives exactly as stored, without the header. A file that cannot be escaped, because it is not valid CSV or is too large to escape, is refused by default and downloads with ?spreadsheet_safe=false.

Search limits

Return the maximum number of results your credential can request, retrieve, or export in one search.
API keys get 5,000 unless your organization has a custom ceiling. The ceiling applies to max_results on POST /search/start-deep-search, POST /search/query, and POST /search/export; a higher value fails the request.

Rate limits

For per-endpoint limits, see Rate Limits. File downloads from the signed url share one budget with other file downloads; see Limits by endpoint.

Pricing

See Credits & Pricing Guide for more information.

Errors

For error responses (400, 403, 404, 409, 429, etc.), see Handling Errors.