openaqi docs

Errors

Every failure carries a machine-readable code, a message, and usually a hint about what to do.

Branch on code, not on the message. Messages get reworded; codes do not.

{
  "error": {
    "code": "no_coverage",
    "message": "Nobody is reporting within 2 cells of that location in the last 60 minutes.",
    "hint": "Coverage is a mosaic, not a grid. Widen `window`, raise `max_rings`, or call /v1/cells to see where data exists.",
    "docs": "https://openaqi.net/docs/errors",
    "request_id": "req_ce3715fc8042498fa21b"
  }
}
CodeHTTPRetry?What to do
unauthorized401noCheck the key. Read keys start with oaq_; a sensor key will not work here.
forbidden_origin403noThe key is origin-locked. Add the origin, or use an unlocked key from a server.
invalid_request400noA parameter was missing or out of range. The message says which.
no_coverage404noNobody is reporting nearby. Widen window or max_rings, or show an empty state.
rate_limited429yesWait Retry-After. Caching is usually the real fix.
internal500yesOurs. Retry with backoff and quote request_id if it persists.

no_coverage is normal

It is the most common error and it is not a failure. Roughly half the planet has no contributor within 12 km, and the honest answer to "what is the air like here" is sometimes "nobody knows".

Treat it as an empty state rather than an exception:

try {
  const { data } = await openaqi.air({ lat, lon });
  render(data);
} catch (err) {
  if (err instanceof OpenaqiError && err.code === "no_coverage") {
    return renderNoDataNearby();     // not an error screen
  }
  throw err;
}

Request IDs

Every response, success or failure, carries X-Request-Id. Quote it and we can find the exact request rather than asking you to reproduce it.

Retrying

Only rate_limited and internal are worth retrying. Retrying a 401 three times is three wasted round trips and a slower error message.

The SDK does this for you, with exponential backoff, jitter, and Retry-After honoured exactly.