EX

Implement exponential backoff and retry patterns to handle Exa rate limiting effectively.

Install

mkdir -p .claude/skills/exa-rate-limits && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8158" && unzip -o skill.zip -d .claude/skills/exa-rate-limits && rm skill.zip

Installs to .claude/skills/exa-rate-limits

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Implement Exa rate limiting, exponential backoff, and request queuing.
70 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Implement exponential backoff with jitter for 429 errors
  • Manage request concurrency using p-queue
  • Execute adaptive rate limiting based on success rates
  • Process batch requests with configurable delays
  • Retry logic for 429 and 5xx server errors

How it works

The skill provides wrappers and queue configurations to manage request frequency, ensuring calls stay within the 10 QPS limit. It uses exponential backoff with jitter to handle 429 errors and concurrency control to prevent burst rejections.

Inputs & outputs

You give it
API operation function
You get back
API response or error after retry attempts

When to use exa-rate-limits

  • Handle API rate limits and 429 errors
  • Implement exponential backoff retries
  • Optimize request throughput
  • Monitor API QPS consumption

About this skill

Exa Rate Limits

Overview

Handle Exa API rate limits gracefully. Default limit is 10 QPS (queries per second) across all endpoints. Rate limit errors return HTTP 429 with a simple { "error": "rate limit exceeded" } response. For higher limits, contact [email protected] for Enterprise plans.

Rate Limit Structure

EndpointDefault QPSNotes
/search10Most endpoints share this limit
/find-similar10Same pool as search
/contents10Same pool
/answer10Same pool
Research APIConcurrent task limitLong-running operations

Prerequisites

  • exa-js SDK installed
  • Understanding of async/await patterns

Instructions

Step 1: Exponential Backoff with Jitter

import Exa from "exa-js";

const exa = new Exa(process.env.EXA_API_KEY);

async function withBackoff<T>(
  operation: () => Promise<T>,
  config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 32000 }
): Promise<T> {
  for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
    try {
      return await operation();
    } catch (err: any) {
      const status = err.status || err.response?.status;
      // Only retry on 429 (rate limit) and 5xx (server errors)
      if (status !== 429 && (status < 500 || status >= 600)) throw err;
      if (attempt === config.maxRetries) throw err;

      // Exponential delay with random jitter to prevent thundering herd
      const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
      const jitter = Math.random() * 500;
      const delay = Math.min(exponentialDelay + jitter, config.maxDelayMs);

      console.log(`[Exa] ${status} — retry ${attempt + 1}/${config.maxRetries} in ${delay.toFixed(0)}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error("Unreachable");
}

// Usage
const results = await withBackoff(() =>
  exa.searchAndContents("AI research", { numResults: 5, text: true })
);

Step 2: Request Queue with Concurrency Control

import PQueue from "p-queue";

// Limit to 8 concurrent requests (under the 10 QPS limit)
const exaQueue = new PQueue({
  concurrency: 8,
  interval: 1000,    // per second
  intervalCap: 10,   // max 10 per interval (matches Exa's QPS limit)
});

async function queuedSearch(query: string, opts: any = {}) {
  return exaQueue.add(() => exa.searchAndContents(query, opts));
}

// Batch many queries safely
async function batchSearch(queries: string[]) {
  const results = await Promise.all(
    queries.map(q => queuedSearch(q, { numResults: 5, text: true }))
  );
  return results;
}

Step 3: Adaptive Rate Limiter

class AdaptiveRateLimiter {
  private currentDelay = 100; // ms between requests
  private minDelay = 50;
  private maxDelay = 5000;
  private consecutiveSuccesses = 0;
  private lastRequestTime = 0;

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    const now = Date.now();
    const elapsed = now - this.lastRequestTime;
    if (elapsed < this.currentDelay) {
      await new Promise(r => setTimeout(r, this.currentDelay - elapsed));
    }

    try {
      this.lastRequestTime = Date.now();
      const result = await fn();
      this.consecutiveSuccesses++;

      // Speed up after 10 consecutive successes
      if (this.consecutiveSuccesses >= 10) {
        this.currentDelay = Math.max(this.minDelay, this.currentDelay * 0.8);
        this.consecutiveSuccesses = 0;
      }
      return result;
    } catch (err: any) {
      if (err.status === 429) {
        // Slow down on rate limit
        this.currentDelay = Math.min(this.maxDelay, this.currentDelay * 2);
        this.consecutiveSuccesses = 0;
        console.log(`[Exa] Rate limited. New delay: ${this.currentDelay}ms`);
      }
      throw err;
    }
  }
}

const limiter = new AdaptiveRateLimiter();

// Combine with backoff
const results = await withBackoff(() =>
  limiter.execute(() => exa.search("query", { numResults: 5 }))
);

Step 4: Batch Processing with Rate Awareness

async function processBatch(
  queries: string[],
  batchSize = 5,
  delayBetweenBatches = 1000
) {
  const allResults = [];

  for (let i = 0; i < queries.length; i += batchSize) {
    const batch = queries.slice(i, i + batchSize);

    // Process batch concurrently
    const batchResults = await Promise.all(
      batch.map(q => withBackoff(() =>
        exa.searchAndContents(q, { numResults: 3, text: true })
      ))
    );
    allResults.push(...batchResults);

    // Pause between batches to stay under rate limit
    if (i + batchSize < queries.length) {
      await new Promise(r => setTimeout(r, delayBetweenBatches));
    }

    console.log(`Processed ${Math.min(i + batchSize, queries.length)}/${queries.length}`);
  }

  return allResults;
}

Error Handling

IssueCauseSolution
429 errorsExceeding 10 QPSImplement backoff + queue
Burst rejectedToo many simultaneous requestsUse p-queue with intervalCap
Batch job failuresNo delay between batchesAdd delayBetweenBatches
Inconsistent throttlingNo jitter in retryAdd random jitter to prevent thundering herd

Examples

Simple Retry Wrapper

async function retrySearch(query: string, maxRetries = 3) {
  for (let i = 0; i <= maxRetries; i++) {
    try {
      return await exa.search(query, { numResults: 5 });
    } catch (err: any) {
      if (err.status !== 429 || i === maxRetries) throw err;
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
    }
  }
}

Resources

Next Steps

For security configuration, see exa-security-basics.

When not to use it

  • When the API is not Exa
  • When simple synchronous requests are sufficient without throttling

Prerequisites

exa-js SDKUnderstanding of async/await patterns

Limitations

  • Default limit is 10 QPS across all endpoints
  • Research API has a concurrent task limit

How it compares

Unlike manual retry loops, this approach incorporates jitter to prevent thundering herd problems and uses adaptive logic to dynamically adjust delays based on API performance.

Compared to similar skills

exa-rate-limits side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
exa-rate-limits (this skill)027dReviewIntermediate
deepgram-performance-tuning327dReviewIntermediate
graphql66moNo flagsAdvanced
guidewire-sdk-patterns227dReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

deepgram-performance-tuning

jeremylongshore

Optimize Deepgram API performance for faster transcription and lower latency. Use when improving transcription speed, reducing latency, or optimizing audio processing pipelines. Trigger with phrases like "deepgram performance", "speed up deepgram", "optimize transcription", "deepgram latency", "deepgram faster".

333

graphql

davila7

GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.

624

guidewire-sdk-patterns

jeremylongshore

Master Guidewire SDK patterns including Digital SDK, REST API Client, and Gosu best practices. Use when implementing integrations, building frontends with Jutro, or writing server-side Gosu code. Trigger with phrases like "guidewire sdk", "digital sdk", "jutro sdk", "guidewire patterns", "gosu best practices", "rest api client".

215

groq-performance-tuning

jeremylongshore

Optimize Groq API performance with caching, batching, and connection pooling. Use when experiencing slow API responses, implementing caching strategies, or optimizing request throughput for Groq integrations. Trigger with phrases like "groq performance", "optimize groq", "groq latency", "groq caching", "groq slow", "groq batch".

111

openrouter-streaming-setup

jeremylongshore

Implement streaming responses with OpenRouter. Use when building real-time chat interfaces or reducing time-to-first-token. Trigger with phrases like 'openrouter streaming', 'openrouter sse', 'stream response', 'real-time openrouter'.

111

perplexity-multi-env-setup

jeremylongshore

Configure Perplexity across development, staging, and production environments. Use when setting up multi-environment deployments, configuring per-environment secrets, or implementing environment-specific Perplexity configurations. Trigger with phrases like "perplexity environments", "perplexity staging", "perplexity dev prod", "perplexity environment setup", "perplexity config by env".

210

Search skills

Search the agent skills registry