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-jsnpm 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
Repository
jeremylongshore…s-skillsGitHub Stars
1.6K
First Seen
Feb 18, 2026
Security Audits
Installed on
codex15
mcpjam14
claude-code14
junie14
windsurf14
zencoder14