exa-multi-env-setup

SKILL.md

Exa Multi-Environment Setup

Overview

Exa's neural search API (api.exa.ai) charges per search request. Multi-environment setup focuses on API key isolation, request caching to reduce costs in staging/production, and controlling numResults and text.maxCharacters per environment (higher values cost more).

Prerequisites

  • Exa API key(s) from dashboard.exa.ai
  • exa-js npm package (npm install exa-js)
  • Optional: Redis for search result caching in staging/production

Environment Strategy

Environment Key Isolation numResults Cache TTL Rate Limit
Development Shared dev key 3 (low cost) None Low
Staging Staging key 5 5 minutes Moderate
Production Prod key 5-10 per query 1 hour Full

Instructions

Step 1: Configuration Structure

// config/exa.ts
import Exa from "exa-js";

type Env = "development" | "staging" | "production";

interface ExaConfig {
  apiKey: string;
  defaultNumResults: number;
  maxCharacters: number;       // per result content length
  cacheEnabled: boolean;
  cacheTtlSeconds: number;
}

const configs: Record<Env, ExaConfig> = {
  development: {
    apiKey: process.env.EXA_API_KEY!,
    defaultNumResults: 3,       // fewer results = lower cost in dev
    maxCharacters: 500,  # HTTP 500 Internal Server Error
    cacheEnabled: false,        // don't bother caching in dev
    cacheTtlSeconds: 0,
  },
  staging: {
    apiKey: process.env.EXA_API_KEY_STAGING!,
    defaultNumResults: 5,
    maxCharacters: 1000,  # 1000: 1 second in ms
    cacheEnabled: true,
    cacheTtlSeconds: 300,       // 5-minute cache in staging  # 300: timeout: 5 minutes
  },
  production: {
    apiKey: process.env.EXA_API_KEY_PROD!,
    defaultNumResults: 5,
    maxCharacters: 1000,  # 1 second in ms
    cacheEnabled: true,
    cacheTtlSeconds: 3600,      // 1-hour cache for repeated queries  # 3600: timeout: 1 hour
  },
};

export function getExaConfig(): ExaConfig {
  const env = (process.env.NODE_ENV || "development") as Env;
  const config = configs[env] || configs.development;
  if (!config.apiKey) {
    throw new Error(`EXA_API_KEY not set for ${env} environment`);
  }
  return config;
}

export function getExaClient(): Exa {
  return new Exa(getExaConfig().apiKey);
}

Step 2: Search Service with Caching

// lib/exa-search.ts
import { getExaClient, getExaConfig } from "../config/exa";
import { Redis } from "ioredis";

const redis = process.env.REDIS_URL ? new Redis(process.env.REDIS_URL) : null;

export async function search(query: string, numResults?: number) {
  const exa = getExaClient();
  const cfg = getExaConfig();
  const n = numResults ?? cfg.defaultNumResults;

  // Check cache if enabled
  if (cfg.cacheEnabled && redis) {
    const cacheKey = `exa:${Buffer.from(`${query}:${n}`).toString("base64")}`;
    const cached = await redis.get(cacheKey);
    if (cached) return JSON.parse(cached);

    const results = await exa.searchAndContents(query, {
      type: "neural",
      numResults: n,
      text: { maxCharacters: cfg.maxCharacters },
    });

    await redis.set(cacheKey, JSON.stringify(results), "EX", cfg.cacheTtlSeconds);
    return results;
  }

  return exa.searchAndContents(query, {
    type: "neural",
    numResults: n,
    text: { maxCharacters: cfg.maxCharacters },
  });
}

Step 3: Environment Variable Setup

# .env.local (development)
EXA_API_KEY=exa-dev-abc123

# GitHub Actions - Staging
EXA_API_KEY_STAGING=exa-staging-def456

# GitHub Actions - Production
EXA_API_KEY_PROD=exa-prod-xyz789
REDIS_URL=redis://prod-redis:6379  # 6379: Redis port

Step 4: Health Check Per Environment

// lib/exa-health.ts
export async function checkExaHealth(): Promise<{ status: string; env: string }> {
  try {
    const exa = getExaClient();
    await exa.search("test connectivity", { numResults: 1 });
    return { status: "healthy", env: process.env.NODE_ENV || "development" };
  } catch (err: any) {
    return { status: "unhealthy", env: process.env.NODE_ENV || "development" };
  }
}

Step 5: CI/CD Configuration

# .github/workflows/deploy.yml
jobs:
  deploy-staging:
    environment: staging
    env:
      EXA_API_KEY_STAGING: ${{ secrets.EXA_API_KEY_STAGING }}
      NODE_ENV: staging
    steps:
      - run: npm run build && npm run deploy:staging

  deploy-production:
    environment: production
    env:
      EXA_API_KEY_PROD: ${{ secrets.EXA_API_KEY_PROD }}
      NODE_ENV: production
    steps:
      - run: npm run deploy:prod

Error Handling

Issue Cause Solution
401 Unauthorized Wrong API key for environment Verify EXA_API_KEY in env vars
429 rate_limit_exceeded Too many requests Implement caching and request queuing
High API costs in staging No caching enabled Enable Redis cache with 5-minute TTL
Empty results Query too narrow Broaden query terms for neural search

Examples

Check Active Configuration

import { getExaConfig } from "./config/exa";

const cfg = getExaConfig();
console.log(`Results per query: ${cfg.defaultNumResults}`);
console.log(`Cache enabled: ${cfg.cacheEnabled}, TTL: ${cfg.cacheTtlSeconds}s`);

Resources

Next Steps

For deployment configuration, see exa-deploy-integration.

Output

  • Configuration files or code changes applied to the project
  • Validation report confirming correct implementation
  • Summary of changes made and their rationale
Weekly Installs
15
GitHub Stars
1.6K
First Seen
Feb 18, 2026
Installed on
codex15
mcpjam14
claude-code14
junie14
windsurf14
zencoder14