Skip to content

Rate limits

How many requests BatteryPass accepts, and how to back off after a 429.

BatteryPass limits requests to protect the platform and the other organisations on it. A request over a limit is answered with 429 rate_limited and a Retry-After header.

Limits

LimitCounted forWindowApplies to
600 requestsAll API keys of an organisation together1 minuteEvery request with a valid key, whatever its result
30 attemptsOne client address15 minutesRequests with an unknown or revoked key
300 requestsOne client address1 minutePublic passport reads, public documents and the GS1 resolver (/01/{gtin}/21/{serial})

The windows are fixed: a window starts, counts up and is reset when it ends. All keys of an organisation share the 600 requests; more keys do not raise the limit.

The 429 response

429
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 17
X-Request-Id: 8b1e6f03c2d94a7e5f0a1b2c

{
  "type": "about:blank",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "Too many attempts. Try again later.",
  "request_id": "8b1e6f03c2d94a7e5f0a1b2c"
}

Retry-After is the number of seconds until the current window ends. A request answered with 429 was not carried out, so you can repeat it after the wait, writes included.

Back off

Wait at least Retry-After seconds, add a little random jitter so that parallel workers do not all start at the same moment, and give up after a few attempts:

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Repeats a request after 429 responses, waiting as Retry-After says.
async function fetchWithBackoff(url: string, init: RequestInit = {}, attempts = 5): Promise<Response> {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(url, init);
    if (res.status !== 429 || attempt === attempts) return res;
    const seconds = Number(res.headers.get('Retry-After')) || 2 ** attempt;
    await sleep(seconds * 1000 + Math.random() * 1000);
  }
}

const res = await fetchWithBackoff(`${process.env.BATTERYPASS_URL}/api/v1/battery-models`, {
  headers: { Authorization: `Bearer ${process.env.BATTERYPASS_API_KEY}` },
});
console.log(res.status);

Stay below the limits

  • Share the budget. All systems of your organisation draw from the same 600 requests per minute. Give each job a share, for example by throttling a nightly sync to a few requests per second.
  • Read in pages of 200. One page of listBatteries replaces 200 single reads; see Pagination.
  • Use exports for bulk data. exportUnits returns up to 10 000 units with their unit-level values as one spreadsheet (XLSX or CSV).
  • Do not retry 401. Fix the key instead; failed attempts count towards the 30 per 15 minutes.
  • Cache public reads. Public passports are cached for five minutes; reading the same passport more often gains nothing.

If your integration needs more than these limits allow, contact support.

On this page