Blog
Which enrichment API errors to retry, and which ones cost a credit if you do
Only 429, 502, and 503 are worth retrying. Retry a 404 and you pay a credit every time. The full status table, which codes are free, and the backoff that works.
This API returns eight status codes you will actually see in production. Two of them cost you a credit. The other six are free. Retrying the wrong one is how you pay twice for a single failure.
The only two billed statuses are the two that answered you
A credit buys a retrieval, not a freshness guarantee, so the price attaches to any lookup that ran
to an answer. A 200 from the Person API or
the Company API is an answered lookup. A 404 is also an answered lookup:
you asked whether a record exists at that identifier, and the answer came back “no”. Both charge one
credit and report it in X-Credits-Charged: 1. Everything else fails before it retrieves anything,
so it is free.
That single rule decides the whole retry policy. Retrying a free status wastes wall-clock. Retrying
a 404 wastes money, one credit per attempt, for an answer that will not change.
The whole table
Status Meaning Billed Retry?
200 record returned 1 credit no, you already have it
404 valid lookup, no record 1 credit never, it costs a credit each try
400 malformed identifier free no, fix the input
401 bad or missing ApiKey free no, fix the key
402 out of credits free no, top up
429 rate limited free yes, honour Retry-After
502 bad gateway free yes, bounded backoff
503 service unavailable free yes, bounded backoff
Never retry the client-side four
400, 401, 402, and 404 all describe a condition a retry cannot change. The identifier is
still malformed on the second attempt, the key is still wrong, the balance is still zero, the record
still does not exist. A 402 is loud about it:
HTTP/1.1 402 Payment Required
content-type: text/plain; charset=utf-8
insufficient credits
That body is free to receive, which is the one mercy here: running out of credits does not cost a
credit. Top up on the pricing page and continue. The 404 is the expensive trap,
because it looks retryable and is not. Most 404s come from four input mistakes, a display name
where a slug belongs being the common one, and each retry bills again. Resolve the identifier before
you spend, which is the whole subject of
why a lookup 404s when the identifier looks correct.
Retry the server-side three, on the server’s terms
429, 502, and 503 are transient and free. They are worth retrying because the same request can
succeed a moment later. The rule is to back off on the server’s schedule, not your own. A 429
carries the only signal that reflects real server state:
HTTP/1.1 429 Too Many Requests
Retry-After: 2
X-RateLimit-Remaining: 0
Honour Retry-After when it is present, fall back to exponential backoff when it is not, add jitter
so a throttled batch does not retry in lockstep, and bound the attempts so a persistent outage does
not become an infinite loop. In Node:
const RETRYABLE = new Set([429, 502, 503]);
// Retries only the three transient statuses. 400, 401, 402, 404 return immediately.
async function lookup(url, { max = 3 } = {}) {
for (let attempt = 0; ; attempt++) {
const res = await fetch(url, { headers: { ApiKey: process.env.TRIGUNA_KEY } });
if (!RETRYABLE.has(res.status) || attempt >= max) return res;
const after = Number(res.headers.get('Retry-After'));
const wait = Number.isFinite(after) && after > 0 ? after * 1000 : 2 ** attempt * 1000;
await new Promise((r) => setTimeout(r, wait + Math.random() * 250)); // jitter
}
}
The published rate is 10 requests per second, but as a separate post on
rate limits that ignore your request rate
documents, a 429 often has nothing to do with your throughput, so tuning a throttle against it is
usually wasted work. Back off, jitter, bound, and put the effort elsewhere.
Why the split matters at volume
On a single lookup the difference is invisible. On a backfill it is the whole cost model. A run that
retries every non-200 treats a 404 like a 502 and pays a credit for each doomed attempt; a run
that reads the table above spends exactly one credit per real answer and zero on the free failures.
The pattern for pacing a large job without overspending is in
running a nightly backfill, and the same free-versus-billed logic keeps
a bad identifier from blocking the request path when you
enrich a user on signup.
The full status conventions live in the errors reference. The short version fits on a sticky note: two codes are billed because they answered you, three are worth a bounded retry, and the rest are yours to fix before you send.
Next
- Why a lookup returns 404 when the identifier looks correct, and how each retry bills again.
- Rate limits that ignore your request rate, for the backoff behind the retryable three.
- Why a cache hit still costs a credit, the same “you pay for the retrieval” rule.
- Run a nightly backfill that spends one credit per answer, not one per attempt.
- Start with five free credits at app.triguna.ai/signup, no card.