
Exa Search
- 579 installs
- 14 repo stars
- Updated August 1, 2026
- benedictking/benedictking-skills
exa-search is a Claude Code skill at version 1.0.2 that delivers semantic web search, similar-page discovery, and structured Exa research for developers who need high-quality results without leaving the agent workflow.
About
exa-search is a BenedictKing Claude Code skill at version 1.0.2, MIT-licensed, requiring Node.js and network access to the Exa API. The skill maps user intent to specific Exa endpoints: semantic search with type auto by default, similar-page discovery, full result content retrieval, research-paper lookup, and GitHub discovery. Trigger conditions in the SKILL.md guide endpoint selection for deep structured research versus quick page finds. Developers reach for exa-search when Claude Code needs fresher, semantically ranked web evidence than built-in browsing alone, especially for technical research and repository discovery. Allowed tools are Bash and Read, keeping retrieval inside the standard agent shell workflow.
- Chooses the correct Exa endpoint (search, contents, findsimilar, answer) based on user intent
- Supports deep search, deep-reasoning, and custom outputSchema for structured research
- Two-phase main-skill + sub-skill architecture that prevents token waste from conversation history
- Replaces deprecated /research endpoints with current /search type: deep-reasoning patterns
- Designed for Claude Code with Node.js and network access
Exa Search by the numbers
- 579 all-time installs (skills.sh)
- Ranked #689 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/benedictking/benedictking-skills --skill exa-searchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 579 |
|---|---|
| repo stars | ★ 14 |
| Last updated | August 1, 2026 |
| Repository | benedictking/benedictking-skills ↗ |
How do you run semantic web search from Claude Code?
Get high-quality semantic web results, similar-page discovery, full content retrieval, and structured research without leaving the Claude Code workflow.
Who is it for?
Claude Code users who need Exa-powered semantic search, paper lookup, and GitHub discovery with Node.js already available.
Skip if: Environments without Node.js or Exa API network access, or tasks satisfied by local repository search alone.
When should I use this skill?
The user needs semantic web search, similar pages, research papers, GitHub discovery, or structured Exa research from within Claude Code.
What you get
Ranked Exa search results, similar-page lists, full retrieved page content, and structured research payloads
- Search result sets
- Retrieved page content
By the numbers
- Skill version 1.0.2 per SKILL.md metadata
Files
Exa Search Skill
Trigger Conditions & Endpoint Selection
Choose Exa endpoint based on user intent:
- search: Need semantic search / find web pages / research topics. Use
type: "auto"by default. - deep search / structured research: Use the search endpoint with
type: "deep"ortype: "deep-reasoning"and optionaloutputSchema. - 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 and /research/v1 are deprecated and were hard-removed on 2026-05-01. Do not use them for new calls; migrate research-style requests to /search with type: "deep-reasoning".
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 scripts/exa-api.cjs <search|contents|findsimilar|answer>
{ ...payload... }
JSONThe script still accepts the legacy research command for backwards compatibility, but it normalizes the payload and sends it to /search with type: "deep-reasoning".
Payload Examples
1) Search
cat <<'JSON' | node scripts/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",
"contents": {
"highlights": true,
"summary": true
}
}
JSONSearch Types:
auto: Balanced defaultfast: Low latencyinstant: Lowest latencydeep-lite: Lightweight synthesized outputdeep: Multi-step search with reasoning and structured outputsdeep-reasoning: Highest-effort deep search for complex research tasks
Treat older neural references as legacy terminology; prefer auto for normal searches.
Categories:
company,people,research paper,news,personal site,financial report, etc.
2) Contents
cat <<'JSON' | node scripts/exa-api.cjs contents
{
"ids": ["result-id-1", "result-id-2"],
"text": true,
"highlights": true,
"summary": true
}
JSON3) Find Similar
cat <<'JSON' | node scripts/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 scripts/exa-api.cjs answer
{
"query": "What is the capital of France?",
"numResults": 5,
"includeDomains": [],
"excludeDomains": []
}
JSON5) Structured Research via Search
Use /search with type: "deep-reasoning" and outputSchema for research-style synthesized output.
cat <<'JSON' | node scripts/exa-api.cjs search
{
"query": "What are the latest developments in AI?",
"type": "deep-reasoning",
"stream": false,
"systemPrompt": "Prefer official sources and provide specific, grounded findings.",
"outputSchema": {
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "The main topic"
},
"key_findings": {
"type": "array",
"description": "List of key findings",
"items": {
"type": "string"
}
}
},
"required": ["topic"]
}
}
JSON/search returns synthesized content in output.content and field-level citations/confidence in output.grounding when outputSchema is used. Do not add citation or confidence fields to the schema.
Environment Variables & API Key
Two ways to configure API Key (priority: environment variable > .env):
1. Environment variable: EXA_API_KEY 2. .env file: Place in .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
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/scripts/exa-api.cjs <search|contents|findsimilar|answer>
{ ...payload... }
JSONOutput
Returns Exa API's JSON response as-is (pretty printed).
#!/usr/bin/env node
/**
* Exa API Helper Script
* Provides a CLI wrapper around Exa endpoints for skill integration.
*
* Usage:
* node exa-api.cjs <search|contents|findsimilar|answer|research> [<json-string>]
* cat payload.json | node exa-api.cjs search
* node exa-api.cjs 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.cjs');
console.error(
[
'Usage:',
` node ${cmd} <search|contents|findsimilar|answer|research> [<json-string>]`,
` cat payload.json | node ${cmd} search`,
` node ${cmd} search --file ./payload.json`,
'',
'Research migration:',
' Exa removed /research on 2026-05-01. Prefer search with type "deep-reasoning".',
' The legacy research command is mapped to /search and normalizes old payload fields.',
'',
'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();
});
}
function normalizePayload(command, payload) {
if (command !== 'research') {
return payload;
}
const {
input,
instructions,
output_schema: outputSchemaSnake,
outputSchema,
query,
model: _model,
citation_format: _citationFormat,
...rest
} = payload;
const normalized = {
...rest,
query: query || input || instructions,
type: rest.type || 'deep-reasoning',
};
const schema = outputSchema || outputSchemaSnake;
if (schema) {
normalized.outputSchema = schema.type ? schema : { type: 'object', ...schema };
}
if (!normalized.query) {
throw new Error('Legacy research payload requires query, input, or instructions');
}
return normalized;
}
const ENDPOINT_BY_COMMAND = {
search: '/search',
contents: '/contents',
findsimilar: '/findSimilar',
answer: '/answer',
research: '/search',
};
(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.cjs');
process.exit(1);
}
try {
const payload = await readPayload(process.argv.slice(3));
const result = await postJson(endpoint, apiKey, normalizePayload(command, payload));
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
})();
Related skills
How it compares
Pick exa-search for semantic ranking and structured Exa endpoints inside Claude Code; use generic browser skills for interactive page automation.
FAQ
What does exa-search require to run?
exa-search version 1.0.2 is designed for Claude Code, requires Node.js, needs network access to the Exa API, and declares allowed-tools Bash and Read in its skill metadata.
Which Exa capabilities does exa-search expose?
exa-search exposes semantic search with type auto, similar-page discovery, full result content retrieval, research-paper lookup, GitHub discovery, and structured deep-research flows.