Recipe

Build a resilient API client

A production client needs five habits. Set timeouts, retry only transient errors with jittered backoff, honour Retry-After, attach idempotency keys to writes, and branch on error.code. Put together, they turn flaky networks and rate limits into non-events.

2 min read

5 habitsTimeouts…branching
Transient onlyWhat to retry
codeBranch on it

The checklist

Every strong client does these five things:

  1. Set a request timeout so a hung call fails fast.
  2. Retry only 429 and 5xx, with jittered backoff.
  3. Honour Retry-After on a 429.
  4. Attach an idempotency key to every write.
  5. Branch on error.code, not the HTTP status alone.

Wire it together

A thin wrapper composes them:

async function call(method, path, body, key) {
  return withRetry(() => fetch(BASE + path, {
    method, signal: AbortSignal.timeout(8000),
    headers: { 'Content-Type':'application/json',
               ...(key ? {'Idempotency-Key': key} : {}) },
    body: body && JSON.stringify(body),
  }));
}

Branch on the outcome

Map error.code to user-facing behaviour using the error code catalogue. Fix-the-request codes surface a clear message; transient codes are handled silently by the retry layer.

Frequently asked questions

Should I retry every failure?

No — only 429 and 5xx. Retrying a 4xx wastes your rate-limit budget on a request that will always fail. See Retrying failed requests.

Do reads need idempotency keys?

No — reads are naturally idempotent. Keys matter for writes (POSTs), where a retried success could otherwise duplicate. See idempotency.

Funding for UK limited companies

Credicorp lends to your company, not to you personally — short-term working capital with no personal guarantee. See what your business could access.