Blog
Apollo.io vs a two-endpoint enrichment API: when a GTM platform fits and when a keyed lookup does
Apollo.io matches partial fields against a 240M-contact GTM database. A two-endpoint enrichment API answers a keyed lookup with provenance in the headers.
If you are weighing Apollo.io against a standalone enrichment API, the question that actually decides it is not which database is bigger. It is whether you are matching a partial identity to a record or looking up a record you can already name. Apollo is built to do the first at the scale of a whole go-to-market motion. An enrichment API like Triguna does the second, keyed by a stored identifier, and puts the provenance in the response headers. Those are different jobs.
What Apollo.io is
Apollo describes itself as “The AI GTM System for Go-to-Market Teams”, one connected platform spanning a B2B database, sequences, a dialer, and CRM sync. Its own homepage advertises access to “240M+ contacts and 30M+ accounts”. Enrichment is one API inside that platform, not the whole product.
The people enrichment call is a POST to /api/v1/people/match, authenticated with an
x-api-key header, and it accepts a set of identity fields to match on, from
its documentation:
curl -X POST "https://api.apollo.io/api/v1/people/match?email=jane@example.com" \
-H "x-api-key: ${APOLLO_API_KEY}"
Two things in that shape matter for the comparison. You hand it attributes you already have, an
email, a name and domain, or a linkedin_url, and Apollo returns its best match from the database.
It also documents a bulk people enrichment
endpoint that enriches “up to ten people
with a single API call”, and an organization enrichment
endpoint at
GET /api/v1/organizations/enrich keyed on a company domain. If your job is to find people and
companies you cannot yet name, run sequences against them, and dial them, that breadth is exactly
the reason to buy Apollo, and a two-endpoint API is not your tool.
What an enrichment API is instead
Triguna is a GET you key by a stored identifier, and it answers in the same call. There is no
match step and no best-guess: you name the record, and you get that record.
curl "https://api.triguna.ai/v1/people/profile?entity_urn=ACoAAB1234" \
-H "ApiKey: $TRIGUNA_KEY" -i
HTTP/1.1 200 OK
X-Data-Source: live
X-Fetched-At: 2026-09-21T11:02:41Z
X-Data-Age-Seconds: 0
X-Credits-Charged: 1
X-RateLimit-Remaining: 9
The whole surface is two enrichment endpoints, GET /v1/people/profile and
GET /v1/companies/details, plus GET /v1/credits for balance. There is no search path, no
people or company discovery, and no sequences, so enriching N entities is N calls. That is the
honest trade: Apollo covers a whole GTM motion, and this covers two lookups and asks you to hold
the entire contract in your head. For the case where you have a list and no bulk endpoint, the
pattern is in enrich a list without a bulk
endpoint.
The real fork: match by attributes or look up by identifier
This is the difference you feel on every call, not just the first one.
Apollo takes the fields you have and returns the record it decides is the best match. That is the right model when you are starting from a messy CRM row, an email with no profile, or a company name with no domain, and you want the platform to resolve it for you.
Triguna takes an identifier you have already stored and returns that exact record, with the source
and age reported in the headers. X-Data-Source says whether the row was fetched live, served from
a store under 24 hours old, or returned as a stale_fallback during an outage. X-Fetched-At and
X-Data-Age-Seconds say precisely how old it is. X-Credits-Charged and X-RateLimit-Remaining
report cost and headroom. Provenance lives in the headers and never in the body, so you can log the
source and age of a record without deserializing the payload. The full set is in what the
enrichment response headers tell you.
| Axis | Apollo.io | Triguna |
|---|---|---|
| Product | A full GTM platform: database, sequences, dialer, CRM sync | Two enrichment endpoints, plus a balance check |
| Enrichment model | Match partial fields to a database record | Look up a record by a stored identifier |
| People verb | POST /api/v1/people/match | GET /v1/people/profile |
| Input | Email, name and domain, or linkedin_url | A stored identifier (entity_urn, company_id slug) |
| Bulk | Up to ten per call | None: N entities is N calls |
| Provenance | In the body | In the headers, never the body |
| Auth | x-api-key header | ApiKey header |
Cost is not the axis here
Apollo prices enrichment in credits that vary by plan and by whether you reveal emails or phone numbers. Triguna is $0.005 per credit, one credit per answered lookup, with five credits on signup and no card. Those are different pricing shapes for different jobs, and picking a data source on headline price alone is how you end up migrating twice. Compete instead on what is documented and stable: whether a miss costs you, when a cached copy is served, and whether an error tells you to retry.
On that last point, the contract matters more than the rate card. Triguna charges one credit for a
404, because a lookup that turns out to have no record is still a real retrieval that ran, and it
charges nothing for a 400, 401, 402, 429, 502, or 503. A miss reads like this:
HTTP/1.1 404 Not Found
X-Credits-Charged: 1
That rule tells you exactly what to retry: back off on 429, 502, and 503, and never retry a
404, because a retried 404 costs a credit every time. The reasoning is in which enrichment
errors to retry.
The input trap that catches everyone
Apollo resolves fuzzy input for you, so a slightly wrong name or a stale email still tends to match
something. Triguna does not resolve, it retrieves, and that makes the identifier your
responsibility. Store the entity_urn value; the urn:li: prefixed form is rejected as input. For
companies, company_id takes the URL slug, so acme-corp resolves and “Acme Corporation” returns
a 404 that still costs a credit. If you are moving from an attribute-match model to a keyed
lookup, budget for the identifier mapping before your first batch. The traps worth fixing up front
are in why your enrichment lookup returns 404.
A short decision list
- You need to find, sequence, or dial people you cannot name yet. Apollo’s platform is your tool, and a two-endpoint API is not.
- You are starting from partial identity fields and want a match. Apollo resolves an email or a name and domain to a record; that is the model it is built for.
- You already hold the identifier and want its current state. Enrich on demand, keyed by identifier, and read the source and age out of the response headers.
- You want a surface small enough to hand to an agent. Two documented endpoints is a contract you can read in one sitting, which is the whole idea behind the about page.
Start on the five free credits before you wire up billing. The pattern for calling the API once at account creation without slowing signup down is in enrich on signup.
Next
- Person API: what
GET /v1/people/profilereturns and the identifier it expects. - Company API: the company fields, and why
company_idtakes the slug rather than the name. - Enrich a list without a bulk endpoint: pacing N calls when there is no batch path.
- Enrich on signup: calling the API once at account creation without blocking a new user.
- Pricing: one credit per answered lookup, and the status codes that cost nothing. Sign up for five free credits at app.triguna.ai/signup.