EX

exa-reliability-patterns

Add resilience to Exa search services through query fallbacks and fault-tolerant patterns.

Install

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

Installs to .claude/skills/exa-reliability-patterns

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 reliability patterns: query fallback chains, circuit breakers,
76 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Implement query fallback chains for narrow result sets
  • Configure circuit breakers to manage service failures
  • Apply exponential backoff for 429 rate limit errors
  • Execute graceful degradation for search failures
  • Monitor search quality via empty and low-score rates

How it works

It uses a tiered search strategy that falls back from neural to keyword search, combined with circuit breakers and retry logic to handle rate limits and service errors.

Inputs & outputs

You give it
Search query string and configuration options
You get back
Search results with metadata indicating source and degradation status

When to use exa-reliability-patterns

  • Build a query fallback chain for search
  • Implement graceful degradation for search failures
  • Add resilience to search-dependent services
  • Handle Exa 429 rate limit errors automatically

About this skill

Exa Reliability Patterns

Overview

Production reliability patterns for Exa neural search. Exa-specific failure modes include: empty result sets (query too narrow), content retrieval failures (sites block crawling), variable latency by search type, and 429 rate limits at 10 QPS default.

Instructions

Step 1: Query Fallback Chain

import Exa from "exa-js";

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

// If neural search returns too few results, fall back through search types
async function resilientSearch(
  query: string,
  minResults = 3,
  opts: any = {}
) {
  // Try 1: Neural search (best quality)
  let results = await exa.searchAndContents(query, {
    type: "neural",
    numResults: 10,
    ...opts,
  });
  if (results.results.length >= minResults) return results;

  // Try 2: Auto search (Exa picks best approach)
  results = await exa.searchAndContents(query, {
    type: "auto",
    numResults: 10,
    ...opts,
  });
  if (results.results.length >= minResults) return results;

  // Try 3: Keyword search (different index)
  results = await exa.searchAndContents(query, {
    type: "keyword",
    numResults: 10,
    ...opts,
  });
  if (results.results.length >= minResults) return results;

  // Try 4: Remove filters and broaden
  const broadOpts = { ...opts };
  delete broadOpts.startPublishedDate;
  delete broadOpts.endPublishedDate;
  delete broadOpts.includeDomains;
  delete broadOpts.includeText;

  return exa.searchAndContents(query, {
    type: "auto",
    numResults: 10,
    ...broadOpts,
  });
}

Step 2: Retry with Exponential Backoff

async function searchWithRetry(
  query: string,
  opts: any,
  maxRetries = 3
) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await exa.searchAndContents(query, opts);
    } catch (err: any) {
      const status = err.status || 0;

      // Only retry on rate limits (429) and server errors (5xx)
      if (status !== 429 && (status < 500 || status >= 600)) throw err;
      if (attempt === maxRetries) throw err;

      const delay = 1000 * Math.pow(2, attempt) + Math.random() * 500;
      console.log(`[Exa] ${status} retry ${attempt + 1}/${maxRetries} in ${delay.toFixed(0)}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error("Unreachable");
}

Step 3: Circuit Breaker

class ExaCircuitBreaker {
  private failures = 0;
  private lastFailure = 0;
  private state: "closed" | "open" | "half-open" = "closed";
  private readonly threshold = 5;       // failures before opening
  private readonly resetTimeMs = 30000; // 30s before half-open

  async execute<T>(fn: () => Promise<T>, fallback?: () => T): Promise<T> {
    // Check if circuit should reset
    if (this.state === "open") {
      if (Date.now() - this.lastFailure > this.resetTimeMs) {
        this.state = "half-open";
      } else if (fallback) {
        return fallback();
      } else {
        throw new Error("Exa circuit breaker is open");
      }
    }

    try {
      const result = await fn();
      if (this.state === "half-open") {
        this.state = "closed";
        this.failures = 0;
      }
      return result;
    } catch (err: any) {
      this.failures++;
      this.lastFailure = Date.now();

      if (this.failures >= this.threshold) {
        this.state = "open";
        console.warn(`[Exa] Circuit breaker OPEN after ${this.failures} failures`);
      }

      if (fallback && this.state === "open") return fallback();
      throw err;
    }
  }

  getState() {
    return { state: this.state, failures: this.failures };
  }
}

const circuitBreaker = new ExaCircuitBreaker();

// Usage with fallback to cached results
const result = await circuitBreaker.execute(
  () => exa.searchAndContents("query", { numResults: 5, text: true }),
  () => getCachedResults("query") // fallback when circuit is open
);

Step 4: Graceful Degradation

interface SearchResultWithMeta {
  results: any[];
  degraded: boolean;
  source: "live" | "cache" | "fallback";
  searchType: string;
}

async function degradableSearch(
  query: string,
  opts: any = {}
): Promise<SearchResultWithMeta> {
  // Level 1: Full search with contents
  try {
    const results = await searchWithRetry(query, {
      type: "neural",
      numResults: 10,
      text: { maxCharacters: 2000 },
      highlights: { maxCharacters: 500 },
      ...opts,
    }, 2);
    return { results: results.results, degraded: false, source: "live", searchType: "neural" };
  } catch {}

  // Level 2: Fast search without content (less expensive)
  try {
    const results = await exa.search(query, {
      type: "fast",
      numResults: 5,
    });
    return { results: results.results, degraded: true, source: "live", searchType: "fast" };
  } catch {}

  // Level 3: Return cached results
  const cached = getCachedResults(query);
  if (cached) {
    return { results: cached, degraded: true, source: "cache", searchType: "cached" };
  }

  // Level 4: Return empty with degradation flag
  return { results: [], degraded: true, source: "fallback", searchType: "none" };
}

Step 5: Result Quality Monitoring

class SearchQualityMonitor {
  private stats = { total: 0, empty: 0, lowScore: 0 };

  record(results: any[]) {
    this.stats.total++;
    if (results.length === 0) this.stats.empty++;
    if (results[0]?.score < 0.5) this.stats.lowScore++;
  }

  isHealthy(): boolean {
    if (this.stats.total < 10) return true; // not enough data
    const emptyRate = this.stats.empty / this.stats.total;
    const lowScoreRate = this.stats.lowScore / this.stats.total;
    return emptyRate < 0.2 && lowScoreRate < 0.3;
  }

  getReport() {
    return {
      ...this.stats,
      emptyRate: `${((this.stats.empty / this.stats.total) * 100).toFixed(1)}%`,
      lowScoreRate: `${((this.stats.lowScore / this.stats.total) * 100).toFixed(1)}%`,
      healthy: this.isHealthy(),
    };
  }
}

Error Handling

IssueCauseSolution
Empty resultsQuery too specificUse fallback chain with broader query
Slow responsesNeural on complex queryDegrade to fast type
429 rate limitBurst trafficCircuit breaker + backoff
Content retrieval failsSite blocks crawlingFall back to highlights or summary
Quality degradationQuery driftMonitor empty/low-score rates

Resources

Next Steps

For policy guardrails, see exa-policy-guardrails. For architecture variants, see exa-architecture-variants.

When not to use it

  • When high-latency neural search is the only acceptable result type
  • When the application cannot tolerate cached or degraded search results

Prerequisites

Exa API key

Limitations

  • Default rate limit is 10 QPS
  • Circuit breaker requires manual threshold and reset time configuration

How it compares

Unlike a standard direct API call, this approach wraps requests in resilience patterns to prevent cascading failures and handle common search-specific error modes.

Compared to similar skills

exa-reliability-patterns side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
exa-reliability-patterns (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