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
-
Exa API Documentation
-
Exa JavaScript SDK
-
Exa Pricing
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