jackwener/OpenCLI · warning · CommandExecutionError
coingecko returned HTTP 429 (rate limited)
Error message
coingecko returned HTTP 429 (rate limited)
What it means
This CommandExecutionError is thrown when CoinGecko responds with HTTP 429, indicating the client exceeded the public API rate limit. The free/unauthenticated tier allows roughly 30 calls per minute, so bursts of CLI invocations (loops over many coins) commonly trip it. The remediation hint is attached as a second argument.
Source
Thrown at clis/coingecko/coin.js:57
const url = new URL(`https://api.coingecko.com/api/v3/coins/${id}`);
url.searchParams.set('localization', 'false');
url.searchParams.set('tickers', 'false');
url.searchParams.set('market_data', 'true');
url.searchParams.set('community_data', 'false');
url.searchParams.set('developer_data', 'false');
url.searchParams.set('sparkline', 'false');
let resp;
try {
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
} catch (error) {
throw new CommandExecutionError(`coingecko coin request failed: ${error?.message || error}`);
}
if (resp.status === 404) {
throw new EmptyResultError('coingecko coin', `coingecko has no coin with id "${id}".`);
}
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 coin failed: HTTP ${resp.status}`);
}
let data;
try {
data = await resp.json();
} catch (error) {
throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
}
if (data?.error) {
throw new CommandExecutionError(`coingecko returned error: ${data.error}`);
}
const md = data.market_data || {};
const pick = (obj, key) => (obj && obj[key] != null ? obj[key] : null);
const isoFromMaybe = (s) => (s ? String(s).slice(0, 10) : '');
const price = pick(md.current_price, currency);View on GitHub (pinned to 49907e53dc)
Solutions
- Wait ~60 seconds and retry with backoff
- Add throttling (e.g. 2+ second sleep between calls) when looping over multiple coins
- Get a free demo/paid CoinGecko API key and use the pro endpoint for higher limits
Example fix
// before
for (const id of ids) await run(['coingecko', 'coin', id]);
// after
for (const id of ids) { await run(['coingecko', 'coin', id]); await new Promise(r => setTimeout(r, 2500)); } Defensive patterns
Strategy: retry
Validate before calling
// throttle before each call in loops await new Promise(r => setTimeout(r, 2500)); // keeps under ~30 calls/min
Type guard
null
Try / catch
try { await run(['coingecko', 'coin', id]); } catch (e) { if (/HTTP 429/.test(e.message)) { await sleep(60000); return retryOnce(); } throw e; } Prevention
- Serialize CoinGecko calls with 2-3s delays in scripts
- Cache results instead of re-fetching unchanged data
- Use a paid/demo API key for higher rate limits
When it happens
Trigger: Calling `coingecko coin` more than ~30 times per minute from a shared IP; scripted loops over many coins; multiple processes/users behind one NAT; the 404/429 branch order means this only fires on a 429 status.
Common situations: Batch scripts fetching prices for a portfolio of hundreds of coins without delays; CI jobs hitting the shared public API; other apps on the same network consuming the same rate budget.
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)
- coingecko derivatives returned HTTP 429 (rate limited)
- coingecko returned HTTP 429 (rate limited)
- 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/224971ea84d6a574.
Report an issue: GitHub.