Getting started

The response envelope

The shape every list endpoint returns, field by field — including the fields most clients ignore and shouldn't.

The envelope is the fixed set of fields wrapped around every answer the API gives you: the results themselves, plus the fields that tell you how to read them. How many matches exist, whether there are more pages, how your query was interpreted, and what (if anything) limited the answer.

Every list-returning endpoint returns this same shape, with the route's own rows in results[] and its own extra side-fields beside them:

  • /v1/search and /v1/similar
  • the four /v1/relationships* routes
  • /v1/org-chart*
  • all nine /v1/entities/* routes
  • an agent run's results, GET /v1/agents/runs/{run_id}/results
  • GET /v1/usage/history

Learn it once and every response on the platform reads the same.

{
  "results": [ { "id": "solarisgroup.com", "category": "company", "score": 0.93, "account_name": "Solaris SE", "...": "..." } ],
  "total": 10000,
  "total_is_estimate": true,
  "has_more": true,
  "next_cursor": "eyJmIjoiMDNjMjUzNzk3MjM3MTI3ZSIsIm8iOjEs...",
  "resolved_search": { "parser_version": "none", "mode": "fast", "category": "company", "query": "fintech companies in berlin", "filters": {}, "filters_source": "none", "semantic_query": "fintech companies in berlin", "semantic_scope": null, "sort": [], "conflicts": [], "overrides": [] },
  "suggestion": null,
  "coverage": null,
  "attribution": [ { "source_corpus": "gtm_index", "domain": null, "as_of": "2026-08-12T19:52:31Z", "url": null, "quote": null } ],
  "degradation": [],
  "columns": null,
  "meta": { "request_id": "fc8735204043c339af9e9cbffaff2b21", "environment": "live", "as_of": "2026-08-12T19:52:31Z", "credential": "api_key", "credits_charged": 0.5 }
}

Why fields are never omitted#

Success responses always serialize every envelope field, even where the content lands later. This is the additive-only contract: adding a field to a response clients already parse is free, but a field that appears later is a breaking change for every client that must branch on it.

Field by field#

results[]

The hits. On search and similar, record fields are flattened onto the row beside three guaranteed keys:

KeyMeaning
idStable row identity. For a company with a domain, the domain itself ("solarisgroup.com"); everything else gets a stable 16-hex one-way hash. Safe as a de-dupe or render key; the same real-world row always gets the same id. Never the retrieval engine's internal record id
categoryAlways the category you searched
scoreRelevance/similarity score

Everything else varies by category: a company row carries account_name, domain, industry, employee_count, and ~25 more; a patent row carries title, abstract, applicants, …

total and total_is_estimate

total is the match count. total_is_estimate says whether it is exact. It is always serialized, never defaulted away, because a field clients can forget exists is a field that renders an estimate as exact.

has_more and next_cursor

next_cursor is opaque and signed; pass it back as cursor to get the next page.

  • Invariant on every response: has_more == (next_cursor != null).
  • On a cursor route there is no page or offset parameter: not accepted, not deprecated, never present. A few routes that aren't cursor-paged page differently: the filter-values routes take page, GET /v1/feedback takes offset, and GET /v1/agents/runs takes limit only.

Full semantics (fingerprint freezing, 24h expiry, 10,000-result depth): Pagination.

What the API decided your request meant. Present on search and similar, null on entities. It lets you re-run exactly the search you got, and explains result changes after a parser retune.

FieldMeaning
parser_versionThe extraction agent's version when it ran; "none" when no LLM was in the path (all fast and similar calls, and cursor pages)
mode, category, queryThe request as interpreted
filtersThe effective filters actually searched
filters_sourcecaller | parser | caller+parser (yours win on conflict) | none
semantic_queryThe text used for semantic retrieval, when any
semantic_scopeWhat semantic_query describes. company on a person search means the description was matched against each person's employer (people have no embeddings of their own). null otherwise, including when there is no description. The schema also allows person, which is not sent today
sortThe ordering actually applied: the clauses you sent, or one inferred from your question when you sent none. [] means the engine chose the order itself. See Sort fields
conflictsContradictions between your filters and parsed ones
overridesWhere your explicit filter overrode an inferred one

suggestion

A better way to have asked this question, or null, which is the common case.

{ "kind": "agentic", "category": null, "message": "This needs several steps joined — run it as an agent run." }

The split is search vs agentic, because that is the bigger question and the one you answer first:

kindMeanscategoryAct on it with
searchA plain POST /v1/search still answers this, just not in the category it was asked inThe one that fits; drops straight into the next requestPOST /v1/search
agenticNo single search answers this, whatever the category: it needs several steps, or several corpora joinednull, because the category is not the problemPOST /v1/agents/runs

message is one ready-to-show sentence.

Why there is no mode field

The customer-facing agentic surface is its own endpoint, and a suggestion naming a parameter would point at the knob rather than at the thing that answers the question.

coverage

How much of the addressable set this answer represents:

  • universe: matching records, counted rather than sampled
  • enumerated: how many this answer carries
  • enumeration_recall, universe_exhausted, truncated_by and constraints_unmet[]

Two fields are worth reading carefully:

  • enumeration_recall is null whenever the universe was not exact, because a ratio over an estimate is not a recall figure. It measures coverage of the resolved query, not of your intent: a wrong predicate can be exhaustively enumerated.
  • constraints_unmet[] names, in your own words, the requirements the search could not express. Non-empty means part of the question was not answered.

columns

The answer table's columns, in order, when an agent run's results carry extra columns beyond the entity's own fields because the run was asked for enrichments: a job-opening link, a value joined from another dataset. Each entry is {name, description, source}, and each column's value sits on every row in results keyed by the column name.

null means a plain entity table, which is what an ordinary search returns.

attribution[]

Where the response's content came from: response-level in v1, a source rather than a citation. Each entry is {source_corpus, domain, as_of, url, quote}, and as_of is per-corpus freshness.

degradation[]

What the API could not do for this request, and what it cost the answer. An empty list means "we checked and nothing degraded", never "unknown". Each entry:

{ "reason": "vector_coverage_incomplete", "detail": "…", "impact": "recall", "source_corpus": "gtm_index" }

impact is recall | precision | freshness | fields. The reason values and how to handle each are listed in the search guide.

meta

Present on every 2xx, including unpaginated ones (/v1/credits, /v1/keys/usage carry meta alone, without the list fields).

FieldMeaning
request_idTrace id — also in the X-Request-Id header. Log it
environmentlive or test, echoed on every response so a test-key result can never be mistaken for a live one
as_ofWhen the response was produced
credits_chargedWhat this request cost, in credits, to at most three decimals. 0 when it cost nothing: a Basic or AI search that found nothing, rows this search already paid for (a cursor sent again, or a request repeated with the same Idempotency-Key on the same UTC day), or a test key. null on a free endpoint. See Credits & usage
credentialapi_key or session — which credential served the request. session means the dashboard playground ran it: billed to the user, interactively throttled, no integration compatibility guarantee

EntityHit — the entities result shape#

On /v1/entities/* routes, each element of results[] is an EntityHit rather than a flattened record:

{
  "id": "adyen.com",
  "category": "company",
  "found": true,
  "matches": [{ "account_name": "Adyen", "industry": "Finance", "...": "..." }]
}
  • id: the input id, echoed back (order and duplicates preserved).
  • found: false for a miss. Misses appear in results[] rather than being dropped: you are billed against what you asked for, so you must see exactly what you got.
  • matches: always a list, and [] on a miss. The record is typed for companies and persons, and the vertical's own vocabulary for document categories.

/v1/entities/* envelopes are never paginated: total is the exact hit count, has_more is false, next_cursor and resolved_search are null.