
Exa Search
- 619 installs
- 5 repo stars
- Updated April 21, 2026
- benedictking/exa-search
exa-search is a Claude and Cursor agent skill with a Node.js CLI wrapper around Exa API endpoints for semantic web search, content retrieval, and research tasks from custom agents.
About
exa-search bundles exa-api.js, a Node CLI that calls https://api.exa.ai with commands for search, contents, findsimilar, answer, and research. Developers pass JSON payloads inline, via --file, or stdin—for example node exa-api.js search. EXA_API_KEY loads from environment or a local .env file documented in the repo. Reach for exa-search when agents need higher-quality semantic retrieval than generic search scrapers during technical research, competitive scans, or literature reviews. The skill fits agent pipelines that must fetch page contents or similar documents programmatically rather than manual browser research.
- CLI wrapper for five Exa endpoints: search, contents, findsimilar, answer, research
- Accepts JSON payloads via argument, stdin, or file for complex queries
- Automatically loads EXA_API_KEY from environment variable or local .env file
- Designed as a reusable integration skill for agentic research workflows
Exa Search by the numbers
- 619 all-time installs (skills.sh)
- Ranked #1,555 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/benedictking/exa-search --skill exa-searchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 619 |
|---|---|
| repo stars | ★ 5 |
| Security audit | 1 / 3 scanners passed |
| Last updated | April 21, 2026 |
| Repository | benedictking/exa-search ↗ |
How do you run semantic web search from Claude agents?
Run high-quality semantic web searches and research tasks directly from Claude, Cursor or custom agents.
Who is it for?
Developers wiring Claude, Cursor, or custom agents to Exa semantic search and research endpoints via a small Node CLI.
Skip if: Offline documentation search or private codebase grep should use local ripgrep or docs MCP tools instead of Exa web search.
When should I use this skill?
A developer or agent needs semantic web search, similar-page discovery, or research answers via the Exa API from a skill workflow.
What you get
Exa API JSON results for search, page contents, similar pages, answers, or research tasks.
- Exa search JSON results
- Retrieved page contents
- Research answer payloads
By the numbers
- CLI supports 5 Exa commands: search, contents, findsimilar, answer, research
- API base URL https://api.exa.ai
Files
Exa Search Skill
Trigger Conditions & Endpoint Selection
Choose Exa endpoint based on user intent:
- search: Need semantic search / find web pages / research topics
- contents: Given result IDs, need to extract full content
- findsimilar: Given URL, need to find similar pages
- answer: Need direct answer to a question
- research: Need structured research output following given
output_schema
Recommended Architecture (Main Skill + Sub-skill)
This skill uses a two-phase architecture:
1. Main skill (current context): Understand user question → Choose endpoint → Assemble JSON payload 2. Sub-skill (fork context): Only responsible for HTTP call execution, avoiding conversation history token waste
Execution Method
Use Task tool to invoke exa-fetcher sub-skill, passing command and JSON (stdin):
Task parameters:
- subagent_type: Bash
- description: "Call Exa API"
- prompt: cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs <search|contents|findsimilar|answer|research>
{ ...payload... }
JSONPayload Examples
1) Search
cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs search
{
"query": "Latest research in LLMs",
"type": "auto",
"numResults": 10,
"category": "research paper",
"includeDomains": [],
"excludeDomains": [],
"startPublishedDate": "2025-01-01",
"endPublishedDate": "2025-12-31",
"includeText": [],
"excludeText": [],
"context": true,
"contents": {
"text": true,
"highlights": true,
"summary": true
}
}
JSONSearch Types:
neural: Semantic search using embeddingsfast: Quick keyword-based searchauto: Automatically choose best method (default)deep: Comprehensive deep search
Categories:
company,people,research paper,news,pdf,github,tweet, etc.
2) Contents
cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs contents
{
"ids": ["result-id-1", "result-id-2"],
"text": true,
"highlights": true,
"summary": true
}
JSON3) Find Similar
cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs findsimilar
{
"url": "https://example.com/article",
"numResults": 10,
"category": "news",
"includeDomains": [],
"excludeDomains": [],
"startPublishedDate": "2025-01-01",
"contents": {
"text": true,
"summary": true
}
}
JSON4) Answer
cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs answer
{
"query": "What is the capital of France?",
"numResults": 5,
"includeDomains": [],
"excludeDomains": []
}
JSON5) Research
cat <<'JSON' | node .claude/skills/exa-search/exa-api.cjs research
{
"input": "What are the latest developments in AI?",
"model": "auto",
"stream": false,
"output_schema": {
"properties": {
"topic": {
"type": "string",
"description": "The main topic"
},
"key_findings": {
"type": "array",
"description": "List of key findings",
"items": {
"type": "string"
}
}
},
"required": ["topic"]
},
"citation_format": "numbered"
}
JSONEnvironment Variables & API Key
Two ways to configure API Key (priority: environment variable > .env):
1. Environment variable: EXA_API_KEY 2. .env file: Place in .claude/skills/exa-search/.env, can copy from .env.example
Response Format
All endpoints return JSON with:
requestId: Unique request identifierresults: Array of search resultssearchType: Type of search performed (for search endpoint)context: LLM-friendly context string (if requested)costDollars: Detailed cost breakdown
# Exa API Key Configuration
# Get your API key from: https://dashboard.exa.ai/api-keys
EXA_API_KEY=your_api_key_here
.env
node_modules/
#!/usr/bin/env node
/**
* Exa API Helper Script
* Provides a CLI wrapper around Exa endpoints for skill integration.
*
* Usage:
* node exa-api.js <search|contents|findsimilar|answer|research> [<json-string>]
* cat payload.json | node exa-api.js search
* node exa-api.js search --file ./payload.json
*/
const https = require('https');
const fs = require('fs');
const path = require('path');
const API_BASE = 'https://api.exa.ai';
function loadApiKey() {
if (process.env.EXA_API_KEY) {
return process.env.EXA_API_KEY;
}
const envPath = path.join(__dirname, '.env');
if (!fs.existsSync(envPath)) {
return null;
}
const envContent = fs.readFileSync(envPath, 'utf8');
const match = envContent.match(/EXA_API_KEY\s*=\s*(.+)/);
if (!match) {
return null;
}
return match[1].trim().replace(/^[\"']|[\"']$/g, '');
}
function usage() {
const cmd = path.basename(process.argv[1] || 'exa-api.js');
console.error(
[
'Usage:',
` node ${cmd} <search|contents|findsimilar|answer|research> [<json-string>]`,
` cat payload.json | node ${cmd} search`,
` node ${cmd} search --file ./payload.json`,
'',
'Env:',
' EXA_API_KEY (env var) or .env file next to this script',
].join('\n'),
);
}
function readStdin() {
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => {
data += chunk;
});
process.stdin.on('end', () => resolve(data));
process.stdin.on('error', reject);
});
}
async function readPayload(args) {
const fileFlagIndex = args.findIndex((arg) => arg === '--file');
if (fileFlagIndex !== -1) {
const filePath = args[fileFlagIndex + 1];
if (!filePath) {
throw new Error('Missing value for --file');
}
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content);
}
const dataFlagIndex = args.findIndex((arg) => arg === '--data');
if (dataFlagIndex !== -1) {
const json = args[dataFlagIndex + 1];
if (!json) {
throw new Error('Missing value for --data');
}
return JSON.parse(json);
}
if (args[0] && !args[0].startsWith('-')) {
return JSON.parse(args[0]);
}
if (process.stdin.isTTY) {
throw new Error('No payload provided (pass JSON arg, --data, --file, or pipe via stdin)');
}
const stdin = await readStdin();
if (!stdin.trim()) {
throw new Error('Empty stdin payload');
}
return JSON.parse(stdin);
}
function postJson(endpointPath, apiKey, payload) {
return new Promise((resolve, reject) => {
const body = JSON.stringify(payload);
const url = new URL(endpointPath, API_BASE);
const req = https.request(
url,
{
method: 'POST',
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
'User-Agent': 'Exa-Skill/1.0',
},
timeout: 60_000,
},
(res) => {
let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
const ok = res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
if (!ok) {
reject(new Error(`API Error ${res.statusCode}: ${data}`));
return;
}
try {
resolve(JSON.parse(data));
} catch {
resolve(data);
}
});
},
);
req.on('error', reject);
req.on('timeout', () => {
req.destroy(new Error('Request timed out'));
});
req.write(body);
req.end();
});
}
const ENDPOINT_BY_COMMAND = {
search: '/search',
contents: '/contents',
findsimilar: '/findSimilar',
answer: '/answer',
research: '/research',
};
(async () => {
const command = process.argv[2];
if (!command || command === '--help' || command === '-h') {
usage();
process.exit(command ? 0 : 1);
}
const endpoint = ENDPOINT_BY_COMMAND[command];
if (!endpoint) {
usage();
process.exit(1);
}
const apiKey = loadApiKey();
if (!apiKey) {
console.error('Missing Exa API key: set EXA_API_KEY or create .env next to exa-api.js');
process.exit(1);
}
try {
const payload = await readPayload(process.argv.slice(3));
const result = await postJson(endpoint, apiKey, payload);
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
})();
Exa Fetcher Sub-skill
Note: This is an internal sub-skill, invoked by the exa-search main skill through the Task tool.Purpose
Execute Exa API calls in an independent context with context: fork, avoiding carrying main conversation context, reducing token consumption.
Received Parameters
Receives complete command through Task's prompt, using stdin for JSON:
cat <<'JSON' | node .claude/skills/exa-search/exa-api.js <search|contents|findsimilar|answer|research>
{ ...payload... }
JSONOutput
Returns Exa API's JSON response as-is (pretty printed).
Related skills
How it compares
Use exa-search for semantic web retrieval in agents; use browser MCP tools when you need interactive page automation instead of API search.
FAQ
Which Exa endpoints does exa-search support?
exa-search exposes search, contents, findsimilar, answer, and research via node exa-api.js, passing JSON payloads to https://api.exa.ai for agent-driven semantic web retrieval.
How do you authenticate exa-search?
exa-search reads EXA_API_KEY from the process environment or a repo .env file, obtained from dashboard.exa.ai/api-keys, before calling Exa HTTPS endpoints.
Is Exa Search safe to install?
skills.sh reports 1 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.