jackwener/OpenCLI · warning · CommandExecutionError

coingecko derivatives returned HTTP 429 (rate limited)

Error message

coingecko derivatives returned HTTP 429 (rate limited)

What it means

This CommandExecutionError is thrown when the CoinGecko /derivatives endpoint responds with HTTP 429, meaning the client has exceeded the free-tier rate limit (~30 calls/min). A remediation hint is included as the error's second argument.

Solutions

  1. Wait at least 60 seconds and retry with backoff
  2. Add throttling (e.g. 2s sleep between calls) or an exponential backoff/retry loop in scripts
  3. Upgrade to a paid CoinGecko plan with an API key for higher limits
  4. Cache responses to reduce duplicate calls

Example fix

// before
while (true) { cli derivatives } // hammers API
// after
// throttle calls
await sleep(2000);
cli derivatives --limit 20
Defensive patterns

Strategy: retry

Try / catch

// detect 429 and back off
if (resp.status === 429) {
  const retryAfter = Number(resp.headers.get('retry-after') ?? 60);
  await new Promise(r => setTimeout(r, retryAfter * 1000));
  resp = await fetch(ENDPOINT); // retry once
}

Prevention

When it happens

Trigger: Making more than ~30 requests per minute to CoinGecko's free API; tight polling loops or many concurrent invocations of the derivatives command.

Common situations: Scripts polling in a loop without delay, CI jobs running many lookups in parallel, shared IP (VPN/CI runner) already rate-limited by other users.

Understand the failure class

Related errors


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

Appendix: source

Thrown at clis/coingecko/derivatives.js:44

    columns: ['rank', 'market', 'symbol', 'indexId', 'contractType', 'price', 'change24hPct', 'fundingRate', 'openInterestUsd', 'volume24hUsd', 'expired'],
    func: async (args) => {
        const limit = Number(args.limit ?? 20);
        if (!Number.isInteger(limit) || limit <= 0) {
            throw new ArgumentError('coingecko derivatives limit must be a positive integer');
        }
        if (limit > 500) {
            throw new ArgumentError('coingecko derivatives limit must be <= 500');
        }
        const filter = args.symbol == null ? '' : String(args.symbol).trim().toUpperCase();
        let resp;
        try {
            resp = await fetch(ENDPOINT, { headers: { 'User-Agent': 'Mozilla/5.0' } });
        }
        catch (err) {
            throw new CommandExecutionError(`coingecko derivatives request failed: ${err?.message ?? err}`);
        }
        if (resp.status === 429) {
            throw new CommandExecutionError(
                'coingecko derivatives returned HTTP 429 (rate limited)',
                'Free tier allows ~30 calls/min. Wait and retry.',
            );
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`coingecko derivatives returned HTTP ${resp.status}`);
        }
        let data;
        try {
            data = await resp.json();
        }
        catch (err) {
            throw new CommandExecutionError(`coingecko derivatives returned malformed JSON: ${err?.message ?? err}`);
        }
        if (!Array.isArray(data) || !data.length) {
            throw new EmptyResultError('coingecko derivatives', 'CoinGecko returned no derivative tickers.');
        }
        let rows = data;

View on GitHub (pinned to 49907e53dc)