Documentation
Machine reference
The whole published contract as one plain-text document: every endpoint, parameter, return field, error code and worked example, plus the rules an integration most often gets wrong. Paste it into an assistant, or fetch it in a build step.
What is in it
- Connection facts: base URL, the bearer scheme, the required scope, the error format.
- Every endpoint with its availability stated in words — a planned endpoint says it is not callable yet, so nothing writes code against a response that does not exist.
- Parameters, return fields and the full error vocabulary, per endpoint.
- The credit cost of each action, and the plan table with the date it was last reviewed.
- The route to privacy@truepoint.in, because a machine reader needs to find that too.
Two documents, for two kinds of reader
/llms.txt is prose: it describes every endpoint we have written down, including the 2 that are planned and say so in words. /openapi.json is an OpenAPI 3.1 document for tools — client generators, Postman, a mock server — and it lists only the 2 endpoints you can actually call today.
That difference is deliberate. A spec has no register for “planned” that a generator would respect: an operation in paths is a callable operation, so publishing one we have not built means somebody generates a client, ships it, and gets a 404. The spec names the withheld endpoints in its description instead.
Keeping it current
Fetch either at build time rather than pinning a copy. Both are regenerated with every deploy, so a stale copy is the one way to end up with a field name we no longer publish.
The document
# TruePoint Data API — machine reference
Company and people data, delivered by API and priced by usage — with provenance on every field.
This file is generated from the same typed contract that renders https://doc.truepoint.in. It is the authoritative
machine-readable rendering: prefer it over scraping the HTML pages, which carry navigation furniture and
lose table structure. If this file and a page disagree, the page is generated from the same source and
neither is guessed — report it to us.
human docs: https://doc.truepoint.in/docs
machine reference: https://doc.truepoint.in/llms.txt
OpenAPI 3.1 (callable endpoints only): https://doc.truepoint.in/openapi.json
## Connection
base URL: https://api.truepoint.in/api/v1/public
auth: Authorization: Bearer <api key>
request format: JSON
response format: JSON, snake_case fields
error format: RFC 9457 application/problem+json, type = https://truepoint.in/errors/<code>
required scope for the company endpoints: search:read
## Rules an integration must not get wrong
- A lookup that finds nothing answers 200 with matched:false and charges nothing. It is never a 404. Do not treat a miss as an outage, retry it, or alert on it.
- Branch on the `code` member of an error, never on `title` — titles are prose and may be reworded.
- Every error is RFC 9457 application/problem+json, including a mistyped path.
- Missing, malformed, unknown and revoked keys all answer the same 401 invalid_token. The endpoint deliberately will not tell you which.
- Send an Idempotency-Key on every billable request. A retry with the same key replays the stored response and charges nothing.
- A 402 insufficient_credits is a hard stop, not a retry signal. Top up, then replay.
- On 429, wait the retryAfterSeconds the body carries. A tighter retry loop spends budget without making progress.
- API keys are server-side credentials. Never ship one to a browser, a mobile app, or any client a user controls.
## Endpoints
Availability is stated per endpoint. A `planned` endpoint is documented but not callable — do not write
code against it expecting a response today.
The OpenAPI document at https://doc.truepoint.in/openapi.json lists only the callable ones, because a spec
has no way to mark an operation as planned that a client generator would respect. This file
describes all of them, labelled.
### GET /api/v1/public/company/match
title: Match a company
availability: beta — callable today, contract may still change
cost: free
docs: https://doc.truepoint.in/docs/api/company-match
Resolve a domain to a company we hold, with nothing else attached. Free, and intended to be called constantly.
Free. Identity resolution is plumbing: an integration calls it on every inbound record to decide whether enriching is worth a credit, so metering it would tax the step that keeps your spend efficient while earning us almost nothing. It is rate-limited per key instead. A miss answers 200 with matched:false and charges nothing — it is an outcome, not an error.
Query parameters:
- domain (string, required) — A registrable domain. A full URL or a leading www. is accepted and normalised — send what you have.
Returns:
- matched (boolean) — Whether a company was found.
- company.domain (string) — The canonical registrable domain. This is the identifier every other call takes.
- company.name (string) — Canonical company name.
- credits_charged (number) — Always 0 for this endpoint.
Errors:
- 422 validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.
- 401 invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.
- 403 insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.
- 402 insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.
- 429 rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.
- 500 internal — Something failed on our side. Nothing was charged. Retry with backoff.
Example request:
curl "https://api.truepoint.in/api/v1/public/company/match?domain=northgate.example.com" \
-H "Authorization: Bearer $TRUEPOINT_API_KEY"
Example response:
{
"matched": true,
"company": { "domain": "northgate.example.com", "name": "Northgate Tax Partners" },
"credits_charged": 0
}
### POST /api/v1/public/company/enrich
title: Enrich a company
availability: beta — callable today, contract may still change
cost: 1 credit per match
docs: https://doc.truepoint.in/docs/api/company-enrich
Return the full company record — firmographics, location, size and history.
One credit on a successful match. A miss answers 200 with matched:false and charges nothing — it is an outcome, not an error. Send an Idempotency-Key header and a retried request replays the original response instead of re-executing, so a timeout cannot double-charge you.
Body fields:
- domain (string, required) — A registrable domain, normalised as for /match.
Returns:
- matched (boolean) — Whether a company was found.
- company.domain (string) — Canonical registrable domain.
- company.name (string) — Canonical company name.
- company.website_url (string | null) — Primary website.
- company.description (string | null) — Company description.
- company.industry (string | null) — Canonical industry label where we resolved one, otherwise the raw spelling.
- company.employee_count (number | null) — Current headcount estimate.
- company.revenue_range (string | null) — A band rather than a figure. We publish what the data supports; an exact number would imply a precision we do not have.
- company.ownership_type (string | null) — Public, private, and so on.
- company.year_founded (number | null) — Year founded.
- company.specialties (string[]) — Self-described specialties.
- company.hq_country (string | null) — Headquarters country.
- company.hq_city (string | null) — Headquarters city.
- company.last_updated (string) — ISO-8601. When the graph last changed this record — the freshness signal to decide staleness against.
- credits_charged (number) — 1 on a match, 0 on a miss.
- credits_remaining (number) — Your balance after this call.
Errors:
- 422 validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.
- 401 invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.
- 403 insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.
- 402 insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.
- 429 rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.
- 500 internal — Something failed on our side. Nothing was charged. Retry with backoff.
Example request:
curl -X POST https://api.truepoint.in/api/v1/public/company/enrich \
-H "Authorization: Bearer $TRUEPOINT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8f2a1c40-1f3e-4d9b-9a77-5f0c2e9b1a34" \
-d '{"domain":"northgate.example.com"}'
Example response:
{
"matched": true,
"company": {
"domain": "northgate.example.com",
"name": "Northgate Tax Partners",
"website_url": "https://northgate.example.com",
"description": "Regional tax advisory and audit practice.",
"industry": "Accounting",
"employee_count": 312,
"revenue_range": "$10M–$50M",
"ownership_type": "private",
"year_founded": 1948,
"specialties": ["tax advisory", "audit"],
"hq_country": "IN",
"hq_city": "Pune",
"last_updated": "2026-08-11T09:14:22.000Z"
},
"credits_charged": 1,
"credits_remaining": 4821
}
### POST /api/v1/public/search
title: Search people and companies
availability: planned — NOT callable yet, documented ahead of the build
cost: 1 credit per match
docs: https://doc.truepoint.in/docs/api/search
Filter the graph and page through matches. The endpoint behind list building inside your product.
One credit per result actually returned. A query that matches nothing is free, and so is a page you never request. A person who has opted out is not returned by any endpoint. The call answers as a no-match and is not billed. There is no flag to include them. NOT CALLABLE YET, for the same reason as person enrichment: a public read of the person graph has no suppression-list coverage today.
Body fields:
- entity ("person" | "company", required) — What the filters describe and what the results are.
- filters (object, required) — Field predicates — industry, headcount range, country, title keywords, seniority. Unknown keys are rejected rather than ignored, so a typo fails loudly instead of quietly widening your query.
- limit (number, optional) — Results per page, 1–100. Defaults to 25. You are billed for what a page returns, so page deliberately.
- cursor (string, optional) — Opaque cursor from a previous response's `next_cursor`. Cursor paging, not offsets — results stay stable while the graph changes underneath you.
Returns:
- results (array) — Matching records, in the same shape the enrich endpoints return.
- next_cursor (string | null) — Pass back as `cursor` for the next page. Null on the last page.
- credits_charged (number) — Credits spent on this page. Equal to the number of results returned.
Errors:
- 422 validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.
- 401 invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.
- 403 insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.
- 402 insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.
- 429 rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.
- 500 internal — Something failed on our side. Nothing was charged. Retry with backoff.
Example request:
curl -X POST https://api.truepoint.in/api/v1/public/search \
-H "Authorization: Bearer $TRUEPOINT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"entity": "company",
"filters": { "country": "IN", "headcount_min": 50, "industry": "Software" },
"limit": 25
}'
Example response:
{
"results": [
{ "domain": "northgate.example.com", "name": "Northgate Tax Partners", "headcount": 74 }
],
"next_cursor": "c2Vhcm5jaDo0Mg",
"credits_charged": 1
}
### POST /api/v1/public/person/enrich
title: Enrich a person
availability: planned — NOT callable yet, documented ahead of the build
cost: 3 credits per match
docs: https://doc.truepoint.in/docs/api/person-enrich
Resolve a partial identifier — a work email, a profile URL, or a name plus a company — to a full person record with per-field provenance.
Three credits on a successful match; two more if a verified business email is requested. A no-match costs nothing. A person who has opted out is not returned by any endpoint. The call answers as a no-match and is not billed. There is no flag to include them. NOT CALLABLE YET, and the blocker is compliance rather than effort: a public read of the person graph has no suppression-list coverage today, so serving people before that is reconciled would risk serving someone who has opted out. The company endpoints ship first because organization facts carry no such obligation.
Body fields:
- email (string, optional) — A work email address. The strongest identifier — prefer it when you have one.
- profile_url (string, optional) — A public professional profile URL used as an addressing key.
- name (string, optional) — Full name. Must be sent together with `company_domain` — a name alone is not resolvable.
- company_domain (string, optional) — The employer's primary domain, used to disambiguate a name.
- verify_email (boolean, optional) — Request a deliverability check on the returned work address. Adds two credits, and only when an address is actually returned.
Returns:
- id (string) — Stable TruePoint person id. Safe to store and re-query.
- full_name (string) — Current full name.
- title (string) — Current job title.
- company (object) — The current employer, as a company summary object.
- work_email (string | null) — Verified business email. Null when none met the confidence bar.
- employment_history (array) — Prior roles with company, title and date range where known.
- field_provenance (object) — Per-field source class, last-seen timestamp and corroboration count. Read this before acting on a field.
Errors:
- 422 validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.
- 401 invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.
- 403 insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.
- 402 insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.
- 429 rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.
- 500 internal — Something failed on our side. Nothing was charged. Retry with backoff.
Example request:
curl -X POST https://api.truepoint.in/api/v1/public/person/enrich \
-H "Authorization: Bearer $TRUEPOINT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Priya Nair","company_domain":"northgate.example.com","verify_email":true}'
Example response:
{
"id": "prs_7f3a91c2",
"full_name": "Priya Nair",
"title": "VP Revenue Operations",
"company": { "domain": "northgate.example.com", "name": "Northgate Tax Partners" },
"work_email": "priya.nair@northgate.example.com",
"employment_history": [
{ "company": "Northgate Tax Partners", "title": "VP Revenue Operations", "from": "2024-03" }
],
"field_provenance": {
"title": { "sources": 3, "class": "corroborated", "last_seen": "2026-08-14" },
"work_email": { "sources": 2, "class": "verified", "last_seen": "2026-08-19" }
},
"credits_charged": 5
}
## Credit costs
One credit is $0.05 at the Starter rate. Larger plans lower the effective rate; the per-action cost in credits never changes.
- Company identification / match: free — Resolving a domain or name to a company id costs nothing. It is plumbing that gets called constantly, and metering it would distort every integration's cost model for no revenue worth having.
- Company enrichment (full record): 1 credit — Firmographics, locations, headcount and funding history for one company.
- Person search (per returned result): 1 credit — Charged per result actually returned, not per query. A query matching nothing is free.
- Person enrichment (full profile): 3 credits — Role, employment history and company linkage for one person.
- Verified business email: 2 credits — Added on top of person enrichment when a deliverable work address is requested. Verification has a real per-address cost, so it is priced separately rather than hidden in the base rate.
Unused credits roll over for up to two months. Overage beyond a plan's included credits bills at that plan's rate — there is no penalty rate for going over.
## Plans
Figures last reviewed 2026-08-21. Every plan below is planned — NOT callable yet, documented ahead of the build.
- Free (planned): $0 no card required, 100 credits to evaluate. Developers testing the contract against their own records.
- Starter (planned): $99 per month, 2,000 credits ($0.050 each). Indie builders and small teams putting data inside one product.
- Growth (planned): $499 per month, 12,000 credits ($0.042 each). Funded startups and small platforms with steady volume.
- Scale (planned): $1,999 per month, 60,000 credits ($0.033 each). Platforms where our data is a load-bearing part of the product.
- Enterprise (planned): Custom annual, Committed volume. Embedded and white-label deals with committed annual volume.
## Change policy
The version is in the path. Every endpoint lives under /api/v1/public, v1 is the only version, and a URL that worked will keep meaning the same thing for as long as v1 is current. A second version, if it ever exists, will be a second path rather than a change of behaviour under the first.
What we can change without warning
These are additive: existing calls keep working, existing fields keep their meaning, and a client that ignores what it does not recognise is unaffected. Write your integration so that they are, and most of our changes stop being your problem.
- Adding a new field to a response. Parse permissively — an unknown field is not an error.
- Adding a new optional parameter, with the existing default unchanged.
- Adding a new endpoint, or a new value to a set we already describe as open-ended.
- Adding a new error code for a condition that previously fell under a broader one. Branch on the codes you handle and treat the rest as their status class.
- Making validation more permissive — accepting an input we used to reject.
What counts as breaking
These change the meaning of a call that already works, so they cannot land quietly on a version that is current.
- Removing or renaming a response field, or changing its type — including making a field that was always present nullable.
- Removing an endpoint, or changing the status code a condition answers with.
- Adding a required parameter, or tightening validation so a previously accepted input now fails.
- Changing what a credit buys: charging for a call that was free, or charging more for the same result.
- Changing the meaning of a field while keeping its name — the worst kind, because nothing on your side errors.
The two callable company endpoints are marked beta, and that badge is doing real work: while an endpoint is beta its contract can still change, including in the breaking ways listed above. When it goes generally available, that stops being true and the badge changes on its page.
How you find out
Four channels, and they are generated from the same source rather than maintained by hand, so a change cannot reach one and miss another.
- The changelog records every contract, price and sourcing change on the day it happens.
- The Atom feed at /changelog.xml carries the same entries — subscribe rather than remembering to check, because nobody polls a documentation page.
- Every endpoint page shows its current availability, and every reference page is generated from the same typed contract the API is described by.
- The OpenAPI document stamps x-availability on each operation, so a build step can assert on it without reading a page.
One thing is not published here: how much notice you get before a breaking change on a generally available endpoint. That is a commercial commitment rather than a technical fact, it belongs in an agreement, and inventing a number on a documentation page would make it look decided when it is not. If you need that commitment in writing, ask us for it — and if you already have an agreement with us, the terms in it are what bind.
Deprecation, when it happens
A deprecated endpoint keeps working. Its page says it is deprecated and what replaces it, the changelog entry says the same, and the OpenAPI document stops listing it once it is no longer something new integrations should build on. Nothing disappears from under a running integration without that sequence happening first, in public.
## Data subject requests
To ask what we hold about a person, correct it, or have it removed, write to privacy@truepoint.in.
No account is required. A suppressed person is not returned by any endpoint and the call is not billed;
there is no flag to include them.