DEV Community

Jonas Hämmerle
Jonas Hämmerle

Posted on

Why a currency API should return 502, not a stale rate, when the upstream is down

A tempting shortcut when your rate source is unreachable: serve the last cached rate and let the request succeed. For most APIs that's a reasonable degradation strategy. For anything touching money, it's the wrong default.

The problem isn't that a stale rate is wrong — it's that a stale rate looks identical to a correct one from the caller's side. A 200 response with a slightly-off number is far more dangerous than an honest failure, because nothing downstream knows to treat the value with suspicion. A checkout flow that silently converts at yesterday's rate doesn't fail loudly; it just quietly overcharges or undercharges, and nobody notices until a reconciliation problem shows up much later.

async function getRateOrFail(base, target) {
  try {
    const res = await fetch(upstreamUrl(base, target));
    if (!res.ok) throw new Error(`upstream returned ${res.status}`);
    return await res.json();
  } catch (err) {
    // Explicit failure, not a stale fallback — the caller needs to know
    // "we don't have a trustworthy number right now," not get a wrong one.
    throw new UpstreamError(err);
  }
}
Enter fullscreen mode Exit fullscreen mode

The tradeoff this makes explicit: you're accepting worse availability (a 502 during an upstream outage) in exchange for a hard guarantee that every successful response is a real, current-as-of-its-timestamp rate — never a guess dressed up as data.

This is the one place Currency API's "no fabricated data" principle gets tested in practice: if Frankfurter (the ECB rate proxy it uses) is unreachable, the API returns a clean 502 upstream_error instead of quietly serving whatever it last had cached. Same principle sibling API Validate applies to its VIES/MX lookups — fail honestly rather than silently degrade on anything where the caller can't tell the difference between fresh and stale. Third sibling API on the same account: QR API.

Top comments (0)