jackwener/OpenCLI · warning · CommandExecutionError
coingecko returned HTTP 429 (rate limited)
Error message
coingecko returned HTTP 429 (rate limited)
What it means
CoinGecko responded with HTTP 429, meaning the client exceeded the public API's rate limit (~30 calls/minute on the free tier). The library special-cases 429 and throws a CommandExecutionError with a hint to wait and retry rather than treating it as a generic HTTP error.
Source
Thrown at clis/coingecko/exchanges.js:45
if (limit > 250) {
throw new ArgumentError('coingecko limit must be <= 250 (per_page upper bound)');
}
const page = Number(args.page ?? 1);
if (!Number.isInteger(page) || page <= 0) {
throw new ArgumentError('coingecko page must be a positive integer');
}
const url = new URL('https://api.coingecko.com/api/v3/exchanges');
url.searchParams.set('per_page', String(limit));
url.searchParams.set('page', String(page));
let resp;
try {
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
}
catch (err) {
throw new CommandExecutionError(`coingecko exchanges request failed: ${err?.message ?? err}`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
'coingecko returned HTTP 429 (rate limited)',
'Free tier allows ~30 calls/min. Wait and retry.',
);
}
if (!resp.ok) {
throw new CommandExecutionError(`coingecko exchanges returned HTTP ${resp.status}`);
}
let data;
try {
data = await resp.json();
}
catch (err) {
throw new CommandExecutionError(`coingecko exchanges returned malformed JSON: ${err?.message ?? err}`);
}
if (!Array.isArray(data) || !data.length) {
throw new EmptyResultError('coingecko exchanges', 'CoinGecko returned no exchange data.');
}
return data.map((ex, i) => ({View on GitHub (pinned to 49907e53dc)
Solutions
- Wait ~60 seconds and retry; the free-tier window resets per minute.
- Add throttling/backoff between calls (e.g. 2-3 seconds sleep, or exponential backoff on 429).
- Cache CoinGecko responses locally instead of re-fetching unchanged data.
- Reduce call volume: fetch larger pages (limit up to 250) instead of many small pages.
- Move to a paid/demo CoinGecko plan with an API key and higher limits.
Example fix
// before
for (const p of pages) await exchanges.func({ page: p });
// after
for (const p of pages) {
await exchanges.func({ page: p });
await new Promise(r => setTimeout(r, 2500)); // stay under ~30 req/min
} Defensive patterns
Strategy: retry
Try / catch
async function fetchWithBackoff(fn, tries = 4) {
for (let i = 0; i < tries; i++) {
try { return await fn(); }
catch (e) {
if (!/429/.test(e.message) || i === tries - 1) throw e;
await sleep(Math.min(60000, 2 ** i * 1000));
}
}
} Prevention
- Stay under ~30 calls/minute: sleep 2-3s between CoinGecko calls.
- Batch with larger per_page (up to 250) to reduce request count.
- Cache responses; global stats and exchange lists change slowly.
- Never retry 429 in a tight loop — that prolongs the ban.
- Consider a paid plan with an API key for production workloads.
When it happens
Trigger: Making more than ~30 requests per minute to api.coingecko.com from this key/IP — e.g. tight loops over `coingecko exchanges` pages or `coingecko global` calls without delay, or multiple scripts sharing the same egress IP.
Common situations: Batch jobs paginating through all exchange pages with no sleep, CI pipelines re-running frequently, several developers behind one NAT, or retry loops that hammer the API faster than the limit resets.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- coingecko returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
- stack exchange 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/db6275f0c5d87bcc.
Report an issue: GitHub.