Demo, all content is generated
Question

Node script for importing from an API keeps getting 429 after about 100 requests

Open · 452 views · asked by ahmed_elsayed · edited

Importing ~5000 records from a third party REST API with a Node script ChatGPT wrote. It does await Promise.all(ids.map(fetchOne)). After ~100 requests everything is 429 Too Many Requests and the script dies. Their docs say "60 requests per minute".

What I’ve tried

Added a setTimeout of 100ms between requests, still 429. ChatGPT then suggested rotating API keys, which I'm pretty sure is against their terms.

Comment

3 answers

chidi_eze · edited

Promise.all over 5000 ids fires 5000 requests at once, the setTimeout doesn't change that. 60/min is one per second, so this import takes ~85 minutes no matter what. Accept that and make it robust:

for (const id of ids) {
  const res = await fetchWithRetry(id);
  await save(res);
  await new Promise(r => setTimeout(r, 1100));
}

async function fetchWithRetry(id, tries = 5) {
  const res = await fetch(url(id), { headers });
  if (res.status === 429 && tries > 0) {
    const wait = Number(res.headers.get("retry-after") ?? 10) * 1000;
    await new Promise(r => setTimeout(r, wait));
    return fetchWithRetry(id, tries - 1);
  }
  return res.json();
}

Save progress as you go so a crash doesn't restart from zero. And no key rotation, you're right that it violates most ToS.

Comment
the retry-after part is nice, didn't know that header exists ahmed_elsayed · edited
old_school_raj · edited

Check if they have a list/bulk endpoint first. Lots of APIs that limit per request also let you fetch 100 records per page. 50 requests instead of 5000.

Comment
Everyone does it once. old_school_raj · edited
they do have ?page_size=100 ... I feel silly ahmed_elsayed · edited
nils_tw · edited

If you do this kind of import more often, bottleneck on npm handles "N per minute" limits declaratively (reservoir + reservoirRefreshInterval) so you don't have to hand-roll the sleeps.

Comment