Rate limits

Each key may make 60 requests a minute, across every endpoint together — a call to GET/v1/networks and a call to GET/v1/outages come out of the same minute.

The limit is counted per key, not per IP address. An integration running behind a shared NAT or a cloud provider's egress shares its IP with strangers; counting by key means nobody else's traffic can use up yours. If one integration needs more headroom than another, give each its own key.

GET/v1/status needs no key and is limited per address instead: 60 requests a minute, cached for 30 seconds, so polling it faster gains nothing.

Requests without a valid key

A request with no key, a key that is not valid, or a key in the URL is answered 401. Twenty of those in a minute from one address, and every request from that address — a valid key included — is answered 429 until the minute is up. A misconfigured client retrying in a loop stops being refused one request at a time; fix the key, wait the minute, and carry on.

The headers

Every response reports where the key stands:

HeaderMeans
x-ratelimit-limitRequests allowed per minute on this key.
x-ratelimit-remainingRequests left in the current minute.
x-ratelimit-resetSeconds until the minute resets.
retry-afterOn a 429 only: seconds to wait before trying again.

When you are limited

Past the limit, the API answers 429 rate_limited and a retry-after header. A refused call is not billed, so a burst that hits the limit costs nothing — but it does not get you the data either.

Wait for retry-after seconds, then carry on. Retrying immediately only earns another 429.

async function get(url) {
  for (;;) {
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AOT_API_KEY}` } });
    if (res.status !== 429) return res;
    const wait = Number(res.headers.get('retry-after') ?? 5);
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

You rarely need to go fast

The networks themselves update every few minutes, and meta.data_as_of on every response says when the latest poll finished. Asking more often than that returns the same data. To follow changes, poll GET/v1/outages with changed_since every few minutes rather than re-reading everything — see Keeping a copy in sync.