jackwener/OpenCLI · error · CommandExecutionError

coingecko returned HTTP 429 (rate limited)

Error message

coingecko returned HTTP 429 (rate limited)

What it means

CoinGecko responded with HTTP 429 and the CLI short-circuits into a CommandExecutionError with guidance. The free tier of the CoinGecko public API is rate limited (~30 calls/min).

Source

Thrown at clis/coingecko/categories.js:47

            );
        }
        const limit = Number(args.limit ?? 20);
        if (!Number.isInteger(limit) || limit <= 0) {
            throw new ArgumentError('coingecko limit must be a positive integer');
        }
        if (limit > 100) {
            throw new ArgumentError('coingecko limit must be <= 100');
        }
        const url = `https://api.coingecko.com/api/v3/coins/categories?order=${encodeURIComponent(sort)}`;
        let resp;
        try {
            resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
        }
        catch (err) {
            throw new CommandExecutionError(`coingecko categories 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 categories returned HTTP ${resp.status}`);
        }
        let data;
        try {
            data = await resp.json();
        }
        catch (err) {
            throw new CommandExecutionError(`coingecko categories returned malformed JSON: ${err?.message ?? err}`);
        }
        if (!Array.isArray(data) || !data.length) {
            throw new EmptyResultError('coingecko categories', 'CoinGecko returned no category data.');
        }
        return data.slice(0, limit).map((cat, i) => ({

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait a minute and retry with backoff
  2. Add throttling/delay between calls (<=30 req/min)
  3. Move to a CoinGecko paid/demo API plan with a key and higher limits

Example fix

// before
for (const s of sorts) await run(`categories.js --sort ${s}`);
// after
for (const s of sorts) {
  await run(`categories.js --sort ${s}`);
  await sleep(2500); // stay under ~30 req/min
}
Defensive patterns

Strategy: retry

Validate before calling

// client-side throttle: max ~30 req/min
let lastCall = 0;
async function throttled(fn, minGapMs = 2100) {
  const wait = lastCall + minGapMs - Date.now();
  if (wait > 0) await sleep(wait);
  lastCall = Date.now();
  return fn();
}

Type guard

function isRateLimited(err) {
  return err instanceof CommandExecutionError && /HTTP 429|rate limited/i.test(err.message);
}

Try / catch

try {
  await throttled(() => runCategories(args));
} catch (err) {
  if (isRateLimited(err)) {
    await sleep(60_000);
    return runCategories(args);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the categories command more than ~30 times per minute from one IP, or sharing an IP with other CoinGecko API consumers.

Common situations: Loops/scripts hammering CoinGecko without delays; shared CI runner IPs already rate limited; multiple team members polling from the same egress IP.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/7b39549d6079ae3c. Report an issue: GitHub.