jackwener/OpenCLI · warning · CommandExecutionError
coingecko returned HTTP 429 (rate limited)
Error message
coingecko returned HTTP 429 (rate limited)
What it means
CoinGecko's /api/v3/global endpoint responded with HTTP 429, indicating the free-tier rate limit (~30 calls/minute per IP/key) has been exceeded. The library raises a dedicated CommandExecutionError with the suggestion to wait and retry, distinguishing it from other HTTP failures.
Source
Thrown at clis/coingecko/global.js:31
browser: false,
args: [
{ name: 'currency', type: 'string', default: 'usd', help: 'Quote currency for total market cap / volume (usd, cny, eur, jpy, ...)' },
],
columns: ['currency', 'totalMarketCap', 'totalVolume24h', 'marketCapChange24hPct', 'btcDominancePct', 'ethDominancePct', 'activeCryptocurrencies', 'markets', 'ongoingIcos', 'updatedAt'],
func: async (args) => {
const currency = String(args.currency ?? 'usd').trim().toLowerCase();
if (!/^[a-z0-9-]{2,20}$/.test(currency)) {
throw new ArgumentError(`coingecko currency must look like a currency slug (got "${args.currency}")`);
}
let resp;
try {
resp = await fetch('https://api.coingecko.com/api/v3/global', { headers: { 'User-Agent': 'Mozilla/5.0' } });
}
catch (err) {
throw new CommandExecutionError(`coingecko global 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 global returned HTTP ${resp.status}`);
}
let body;
try {
body = await resp.json();
}
catch (err) {
throw new CommandExecutionError(`coingecko global returned malformed JSON: ${err?.message ?? err}`);
}
const data = body?.data;
if (!data) {
throw new CommandExecutionError('coingecko global returned no data envelope');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Wait about 60 seconds for the per-minute window to reset, then retry.
- Throttle polling: CoinGecko global stats change slowly — poll at most every 1-5 minutes.
- Implement exponential backoff with jitter on 429 in automation.
- Cache the last successful result and serve from cache between refreshes.
- Upgrade to a CoinGecko paid/demo plan with an API key for higher limits.
Example fix
// before
setInterval(() => globalCmd.func({}), 5000);
// after
setInterval(() => globalCmd.func({}).catch(e => log(e)), 5 * 60 * 1000); // 5 min Defensive patterns
Strategy: retry
Try / catch
async function getGlobalSafe() {
try { return await globalCmd.func({ currency: 'usd' }); }
catch (e) {
if (/429/.test(e.message)) {
await sleep(60000); // wait out the per-minute window
return globalCmd.func({ currency: 'usd' });
}
throw e;
}
} Prevention
- Poll global stats at most every 1-5 minutes — the data changes slowly.
- Share one scheduler for all CoinGecko calls so quotas aren't consumed independently.
- Cache last successful results and serve stale data on 429.
- Use exponential backoff with jitter; never retry immediately in a loop.
- Move to an API-key plan for anything approaching production frequency.
When it happens
Trigger: More than ~30 requests per minute to api.coingecko.com from the same IP — e.g. monitoring loops polling `coingecko global` every second, shared CI egress IPs, or other CoinGecko tooling on the same network consuming the quota.
Common situations: Dashboards auto-refreshing market-cap data too often, cron jobs running every few seconds, retry storms amplifying load after an initial failure.
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/580de17b0f4dc37d.
Report an issue: GitHub.