jackwener/OpenCLI · warning · CommandExecutionError
${label} rate-limited (HTTP 429); back off and retry.
Error message
${label} rate-limited (HTTP 429); back off and retry. What it means
openFDA enforces rate limits (240 requests/minute without an API key); when exceeded it returns HTTP 429 and openfdaFetch converts that to this CommandExecutionError. It tells the caller to back off rather than hammering the API. The label identifies which command's request was throttled.
Source
Thrown at clis/openfda/utils.js:38
if (!Number.isInteger(n) || n < 1 || n > max) {
throw new ArgumentError(`--${name} must be an integer between 1 and ${max}`);
}
return n;
}
export async function openfdaFetch(url, label) {
let resp;
try {
resp = await fetch(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });
} catch (err) {
throw new CommandExecutionError(`${label} request failed: ${err.message}`);
}
if (resp.status === 404) {
// openFDA returns 404 for "no matches" instead of an empty results array.
throw new EmptyResultError(label, `${label} returned 404 (no matches).`);
}
if (resp.status === 429) {
throw new CommandExecutionError(`${label} rate-limited (HTTP 429); back off 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 non-JSON body: ${err.message}`);
}
return body;
}
// openFDA returns most string fields as `[string]` arrays — collapse to first
// element. Preserves `null` (not coerced to empty string) when the slot is
// missing entirely.
export function firstOrNull(arr) {
if (!Array.isArray(arr) || !arr.length) return null;View on GitHub (pinned to 49907e53dc)
Solutions
- Add a delay between requests (e.g. 300-500ms) or use exponential backoff when 429 appears.
- Register for a free openFDA API key and include it, raising the allowance.
- Reduce concurrency — serialize requests instead of parallel fan-out.
- Cache responses for repeated queries to cut request volume.
Example fix
// before
for (const drug of drugs) await fetchDrugLabel({ generic: drug }); // bursts -> 429
// after
for (const drug of drugs) {
await fetchDrugLabel({ generic: drug });
await new Promise(r => setTimeout(r, 500)); // throttle to stay under limit
} Defensive patterns
Strategy: retry
Validate before calling
class RateLimiter {
constructor(minIntervalMs = 300) { this.min = minIntervalMs; this.last = 0; }
async wait() {
const now = Date.now();
const delta = now + this.min - this.last;
if (delta > 0) await new Promise(r => setTimeout(r, delta));
this.last = Date.now();
}
} Type guard
function isRateLimitError(e) {
return e instanceof Error && /429/.test(e.message);
} Try / catch
try {
const body = await openfdaFetch(url, label);
} catch (e) {
if (/rate-limited \(HTTP 429\)/.test(e.message)) {
await new Promise(r => setTimeout(r, 30_000)); // long backoff
return openfdaFetch(url, label);
}
throw e;
} Prevention
- Throttle requests to under 240/min (delay ~300ms between calls).
- Get a free openFDA API key to raise the limit.
- Serialize batch jobs instead of parallel fan-out.
- Cache repeated queries to reduce request volume.
When it happens
Trigger: A script loops over many drug/recall queries faster than 240/min without an api.fda.gov API key; multiple concurrent workers share one IP; a burst of retries after earlier failures compounds the throttling.
Common situations: Batch job enumerating hundreds of drug names overnight; CI pipeline running the CLI in a tight loop; shared office/NAT IP where combined traffic crosses the limit; missing OPENFDA_API_KEY configured into the request.
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)
- coingecko returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
- hf spaces returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7e17f9cd68b0097d.
Report an issue: GitHub.