exa-multi-env-setup
Multi-environment setup guide for Exa API keys, caching, and request limits.
Install
mkdir -p .claude/skills/exa-multi-env-setup && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8841" && unzip -o skill.zip -d .claude/skills/exa-multi-env-setup && rm skill.zipInstalls to .claude/skills/exa-multi-env-setup
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.
Configure Exa across development, staging, and production environments.Key capabilities
- →Isolate API keys across development, staging, and production
- →Configure environment-specific search limits and content settings
- →Implement Redis caching for staging and production environments
- →Perform health checks per environment
- →Manage secrets via CI/CD environment variables
How it works
It uses environment-specific configuration objects to adjust API keys, result limits, and cache TTLs based on the current deployment environment.
Inputs & outputs
When to use exa-multi-env-setup
- →Isolate API keys across environments
- →Implement environment-aware search limits
- →Setup Redis caching for staging and prod
- →Configure Exa request parameters per tier
About this skill
Exa Multi-Environment Setup
Overview
Exa charges per search request at api.exa.ai. Multi-environment setup focuses on API key isolation per environment, request limits and caching to control costs in staging, and appropriate numResults/content settings per tier.
Prerequisites
- Exa API key(s) from dashboard.exa.ai
exa-jsinstalled (npm install exa-js)- Optional: Redis for search result caching in staging/production
Environment Strategy
| Environment | Key Isolation | numResults | Content | Cache TTL |
|---|---|---|---|---|
| Development | Shared dev key | 3 | highlights only | None |
| Staging | Staging key | 5 | text (1000 chars) | 5 min |
| Production | Prod key | 10 | text (2000 chars) | 1 hour |
Instructions
Step 1: Environment-Aware Configuration
// config/exa.ts
import Exa from "exa-js";
type Env = "development" | "staging" | "production";
interface ExaEnvConfig {
apiKey: string;
defaultNumResults: number;
maxCharacters: number;
searchType: "auto" | "neural" | "keyword";
cacheEnabled: boolean;
cacheTtlSeconds: number;
}
const configs: Record<Env, Omit<ExaEnvConfig, "apiKey"> & { keyVar: string }> = {
development: {
keyVar: "EXA_API_KEY",
defaultNumResults: 3,
maxCharacters: 500,
searchType: "auto",
cacheEnabled: false,
cacheTtlSeconds: 0,
},
staging: {
keyVar: "EXA_API_KEY_STAGING",
defaultNumResults: 5,
maxCharacters: 1000,
searchType: "auto",
cacheEnabled: true,
cacheTtlSeconds: 300, // 5 minutes
},
production: {
keyVar: "EXA_API_KEY_PROD",
defaultNumResults: 10,
maxCharacters: 2000,
searchType: "neural",
cacheEnabled: true,
cacheTtlSeconds: 3600, // 1 hour
},
};
export function getExaConfig(): ExaEnvConfig {
const env = (process.env.NODE_ENV || "development") as Env;
const config = configs[env] || configs.development;
const apiKey = process.env[config.keyVar];
if (!apiKey) {
throw new Error(`${config.keyVar} not set for ${env} environment`);
}
return { ...config, apiKey };
}
export function getExaClient(): Exa {
return new Exa(getExaConfig().apiKey);
}
Step 2: Search Service with Config-Driven Defaults
// lib/exa-search.ts
import { getExaClient, getExaConfig } from "../config/exa";
export async function search(query: string, numResults?: number) {
const exa = getExaClient();
const cfg = getExaConfig();
const n = numResults ?? cfg.defaultNumResults;
return exa.searchAndContents(query, {
type: cfg.searchType,
numResults: n,
text: { maxCharacters: cfg.maxCharacters },
});
}
Step 3: Redis Cache Layer (Staging/Production)
// lib/exa-cache.ts
import { Redis } from "ioredis";
import { getExaClient, getExaConfig } from "../config/exa";
const redis = process.env.REDIS_URL ? new Redis(process.env.REDIS_URL) : null;
export async function cachedSearch(query: string, numResults?: number) {
const exa = getExaClient();
const cfg = getExaConfig();
const n = numResults ?? cfg.defaultNumResults;
if (cfg.cacheEnabled && redis) {
const cacheKey = `exa:${Buffer.from(`${query}:${n}:${cfg.searchType}`).toString("base64")}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const results = await exa.searchAndContents(query, {
type: cfg.searchType,
numResults: n,
text: { maxCharacters: cfg.maxCharacters },
});
await redis.set(cacheKey, JSON.stringify(results), "EX", cfg.cacheTtlSeconds);
return results;
}
return exa.searchAndContents(query, {
type: cfg.searchType,
numResults: n,
text: { maxCharacters: cfg.maxCharacters },
});
}
Step 4: Environment Variables
# .env.local (development)
EXA_API_KEY=exa-dev-key-here
# .env.staging
EXA_API_KEY_STAGING=exa-staging-key-here
REDIS_URL=redis://staging-redis:6379
# .env.production
EXA_API_KEY_PROD=exa-prod-key-here
REDIS_URL=redis://prod-redis:6379
Step 5: CI/CD Secret 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 ci && 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 ci && npm run build && npm run deploy:prod
Step 6: Health Check Per Environment
export async function checkExaHealth(): Promise<{
status: string;
env: string;
latencyMs: number;
}> {
const start = performance.now();
try {
const exa = getExaClient();
await exa.search("health check", { numResults: 1 });
return {
status: "healthy",
env: process.env.NODE_ENV || "development",
latencyMs: Math.round(performance.now() - start),
};
} catch {
return {
status: "unhealthy",
env: process.env.NODE_ENV || "development",
latencyMs: Math.round(performance.now() - start),
};
}
}
Error Handling
| Issue | Cause | Solution |
|---|---|---|
401 Unauthorized | Wrong API key for environment | Verify correct env var name |
429 rate_limit_exceeded | Too many requests | Enable caching and request queuing |
| High API costs in staging | No caching enabled | Enable Redis cache with 5-min TTL |
| Empty results in dev | numResults too low | Increase from 3 to 5 |
Resources
Next Steps
For deployment configuration, see exa-deploy-integration.
When not to use it
- →When environment variables are missing for the target tier
- →When Redis is not available for caching
Prerequisites
Limitations
- →Development environment does not support caching
- →Requires separate API keys for each environment
How it compares
This approach enforces environment-specific constraints and caching policies programmatically, rather than using a single global configuration.
Compared to similar skills
exa-multi-env-setup side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| exa-multi-env-setup (this skill) | 0 | 25d | Review | Intermediate |
| smithery-mcp-deployment | 8 | 8mo | Review | Intermediate |
| apollo-deploy-integration | 1 | 25d | Caution | Intermediate |
| firecrawl-deploy-integration | 1 | 25d | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
smithery-mcp-deployment
CaullenOmdahl
Best practices for creating, optimizing, and deploying MCP servers to Smithery. Use this skill when:(1) Creating new MCP servers for Smithery deployment(2) Optimizing quality scores (achieving 90/100)(3) Troubleshooting deployment issues (0/0 tools, missing annotations, low scores)(4) Migrating existing MCP servers to Smithery format(5) Understanding Smithery's schema format requirements(6) Adding workflow prompts, tool annotations, or documentation resources(7) Configuring smithery.yaml and package.json for deployment
apollo-deploy-integration
jeremylongshore
Deploy Apollo.io integrations to production. Use when deploying Apollo integrations, configuring production environments, or setting up deployment pipelines. Trigger with phrases like "deploy apollo", "apollo production deploy", "apollo deployment pipeline", "apollo to production".
firecrawl-deploy-integration
jeremylongshore
Deploy FireCrawl integrations to Vercel, Fly.io, and Cloud Run platforms. Use when deploying FireCrawl-powered applications to production, configuring platform-specific secrets, or setting up deployment pipelines. Trigger with phrases like "deploy firecrawl", "firecrawl Vercel", "firecrawl production deploy", "firecrawl Cloud Run", "firecrawl Fly.io".
higress-clawdbot-integration
alibaba
Deploy and configure Higress AI Gateway for Clawdbot/OpenClaw integration. Use when: (1) User wants to deploy Higress AI Gateway, (2) User wants to configure Clawdbot/OpenClaw to use Higress as a model provider, (3) User mentions 'higress', 'ai gateway', 'model gateway', 'AI网关', (4) User wants to set up model routing or auto-routing, (5) User needs to manage LLM provider API keys, (6) User wants to track token usage and conversation history.
posthog-deploy-integration
jeremylongshore
Deploy PostHog integrations to Vercel, Fly.io, and Cloud Run platforms. Use when deploying PostHog-powered applications to production, configuring platform-specific secrets, or setting up deployment pipelines. Trigger with phrases like "deploy posthog", "posthog Vercel", "posthog production deploy", "posthog Cloud Run", "posthog Fly.io".
vercel-hello-world
jeremylongshore
Create a minimal working Vercel example. Use when starting a new Vercel integration, testing your setup, or learning basic Vercel API patterns. Trigger with phrases like "vercel hello world", "vercel example", "vercel quick start", "simple vercel code".