jackwener/OpenCLI · error · CommandExecutionError
${label} returned HTTP 429 (rate limited)
Error message
${label} returned HTTP 429 (rate limited) What it means
s2Fetch retries once automatically on HTTP 429 (only for anonymous, key-less traffic, after a 1.5s pause); if the second response is still 429 — or the first when an API key is set — it throws CommandExecutionError '... returned HTTP 429 (rate limited)'. Semantic Scholar caps anonymous traffic at ~100 requests/5 minutes, so this signals the client is exceeding the rate limit.
Source
Thrown at clis/semanticscholar/utils.js:116
} catch (err) {
throw new CommandExecutionError(
`${label} request failed: ${err?.message ?? err}`,
'Check that api.semanticscholar.org is reachable from this network.',
);
}
if (resp.status === 429 && attempt === 0 && !apiKey) {
attempt += 1;
await new Promise(resolve => setTimeout(resolve, 1500));
continue;
}
break;
}
if (resp.status === 404) {
throw new EmptyResultError(label, `Semantic Scholar returned 404 for ${url}.`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
`${label} returned HTTP 429 (rate limited)`,
'Semantic Scholar throttles anonymous traffic; set SEMANTIC_SCHOLAR_API_KEY (free at https://www.semanticscholar.org/product/api) or wait a minute and retry.',
);
}
if (!resp.ok) {
throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
}
let body;
try {
body = await resp.json();
} catch (err) {
throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
}
if (body && typeof body === 'object' && body.error) {
throw new CommandExecutionError(`${label} returned an error: ${body.error}`);
}
return body;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Request a free API key and set it: export SEMANTIC_SCHOLAR_API_KEY=<key> (raised limits).
- Wait about a minute for the 5-minute window to reset, then retry.
- Throttle your loop: add sleep/delay between calls and cap concurrency to 1-2.
- Add exponential backoff with jitter around the CLI in scripts, and cache results to avoid repeat fetches.
Example fix
// before
for (const id of ids) await run(`opencli semanticscholar paper ${id}`);
// after
for (const id of ids) {
await run(`opencli semanticscholar paper ${id}`);
await new Promise(r => setTimeout(r, 3500)); // stay under ~100 req/5min
} Defensive patterns
Strategy: retry
Validate before calling
// client-side throttle: max ~90 requests per 5 minutes
const MIN_INTERVAL_MS = 3400;
let last = 0;
async function throttle() {
const wait = last + MIN_INTERVAL_MS - Date.now();
if (wait > 0) await new Promise(r => setTimeout(r, wait));
last = Date.now();
} Try / catch
async function withRateLimitRetry(fn, attempts = 4) {
for (let i = 0; ; i++) {
try { return await fn(); }
catch (err) {
if (!/HTTP 429/.test(String(err.message)) || i >= attempts - 1) throw err;
await new Promise(r => setTimeout(r, 60000 * (i + 1)));
}
}
} Prevention
- Set a free SEMANTIC_SCHOLAR_API_KEY to lift the anonymous ~100 req/5min cap.
- Serialize batch requests with a >=3.4s delay and avoid parallel workers on one IP.
- Cache paper lookups locally so reruns do not refetch the same ids.
- Add exponential backoff with jitter around 429s; stop hammering once throttled.
When it happens
Trigger: Looping over many `paper`/`citations` calls without delay and exhausting the anonymous quota; parallel workers hammering the API; a shared IP (CI runner, university NAT) already throttled; requests made with an over-quota or invalid SEMANTIC_SCHOLAR_API_KEY (keyed requests are not retried).
Common situations: Batch scripts fetching hundreds of papers; CI pipelines running concurrently on shared egress IPs; expired/exceeded API key tier; retry loops elsewhere re-triggering the limit.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- coingecko derivatives returned HTTP 429 (rate limited)
- API_ERROR
- API_ERROR
- coingecko returned HTTP 429 (rate limited)
- coingecko returned HTTP 429 (rate limited)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/0b7da042855b6eeb.
Report an issue: GitHub.