exa-webhooks-events
Build event-driven integrations for Exa search API, including content monitoring and similarity alerts using queue-based async patterns.
Install
mkdir -p .claude/skills/exa-webhooks-events && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7748" && unzip -o skill.zip -d .claude/skills/exa-webhooks-events && rm skill.zipInstalls to .claude/skills/exa-webhooks-events
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.
Build event-driven integrations with Exa using scheduled monitors andKey capabilities
- →Schedule content monitoring with searchAndContents
- →Detect new content using date filters
- →Implement similarity alerts with findSimilarAndContents
- →Deliver notifications via webhooks with retry logic
- →Generate daily research digests
How it works
It builds asynchronous notification patterns by scheduling periodic searches and comparing results against previously tracked URLs. It uses a queue system to manage these tasks and a webhook delivery function with retry logic.
Inputs & outputs
When to use exa-webhooks-events
- →Create content alerts for specific keywords
- →Build competitive intelligence pipelines
- →Implement scheduled research digests
- →Set up automated similarity notifications
About this skill
Exa Webhooks & Events
Overview
Build event-driven integrations around Exa neural search. Exa is a synchronous search API (no native webhooks), so this skill covers building async patterns: scheduled content monitoring with searchAndContents, similarity alerts with findSimilarAndContents, new content detection using date filters, and webhook-style notification delivery.
Prerequisites
exa-jsinstalled andEXA_API_KEYconfigured- Queue system (BullMQ/Redis) or cron scheduler
- Webhook endpoint for notifications
Event Patterns
| Pattern | Mechanism | Use Case |
|---|---|---|
| Content monitor | Scheduled searchAndContents with startPublishedDate | New article alerts |
| Similarity alert | Periodic findSimilarAndContents + diff | Competitive monitoring |
| Content change | Re-search + compare result sets | Update tracking |
| Research digest | Scheduled answer + email/Slack | Daily briefings |
Instructions
Step 1: Content Monitor Service
import Exa from "exa-js";
import { Queue, Worker } from "bullmq";
const exa = new Exa(process.env.EXA_API_KEY!);
interface SearchMonitor {
id: string;
query: string;
webhookUrl: string;
lastResultUrls: Set<string>;
intervalMinutes: number;
searchType: "auto" | "neural" | "keyword";
}
const monitorQueue = new Queue("exa-monitors", {
connection: { host: "localhost", port: 6379 },
});
async function createMonitor(config: Omit<SearchMonitor, "lastResultUrls">) {
await monitorQueue.add("check-search", config, {
repeat: { every: config.intervalMinutes * 60 * 1000 },
jobId: config.id,
});
console.log(`Monitor created: ${config.id} (every ${config.intervalMinutes} min)`);
}
Step 2: Execute Monitored Searches
const worker = new Worker("exa-monitors", async (job) => {
const monitor = job.data;
// Search for new content published since last check
const results = await exa.searchAndContents(monitor.query, {
type: monitor.searchType || "auto",
numResults: 10,
text: { maxCharacters: 500 },
highlights: { maxCharacters: 300, query: monitor.query },
// Only find content published in the monitoring window
startPublishedDate: getLastCheckDate(monitor.id),
});
// Filter to genuinely new results
const newResults = results.results.filter(
r => !monitor.lastResultUrls?.has(r.url)
);
if (newResults.length > 0) {
await sendWebhook(monitor.webhookUrl, {
event: "exa.new_results",
monitorId: monitor.id,
query: monitor.query,
timestamp: new Date().toISOString(),
results: newResults.map(r => ({
title: r.title,
url: r.url,
snippet: r.text?.substring(0, 200),
highlights: r.highlights,
publishedDate: r.publishedDate,
score: r.score,
})),
});
// Update tracked URLs
await updateLastResultUrls(monitor.id, newResults.map(r => r.url));
}
}, { connection: { host: "localhost", port: 6379 } });
Step 3: Similarity Alert System
async function monitorSimilarContent(
seedUrl: string,
webhookUrl: string,
checkIntervalHours = 24
) {
const results = await exa.findSimilarAndContents(seedUrl, {
numResults: 5,
text: { maxCharacters: 300 },
excludeSourceDomain: true,
// Only find content from the last check period
startPublishedDate: new Date(
Date.now() - checkIntervalHours * 60 * 60 * 1000
).toISOString(),
});
if (results.results.length > 0) {
await sendWebhook(webhookUrl, {
event: "exa.similar_content_found",
seedUrl,
matchCount: results.results.length,
matches: results.results.map(r => ({
title: r.title,
url: r.url,
snippet: r.text?.substring(0, 200),
score: r.score,
})),
});
}
return results.results.length;
}
Step 4: Webhook Delivery with Retry
async function sendWebhook(url: string, payload: any, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Exa-Event": payload.event,
},
body: JSON.stringify(payload),
});
if (response.ok) return;
console.warn(`Webhook ${response.status}: ${url}`);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
Step 5: Daily Research Digest
async function generateDailyDigest(
topics: string[],
webhookUrl: string
) {
const digest = [];
for (const topic of topics) {
const results = await exa.searchAndContents(topic, {
type: "neural",
numResults: 3,
summary: { query: `Latest developments in: ${topic}` },
startPublishedDate: new Date(
Date.now() - 24 * 60 * 60 * 1000
).toISOString(),
});
digest.push({
topic,
articles: results.results.map(r => ({
title: r.title,
url: r.url,
summary: r.summary,
})),
});
}
await sendWebhook(webhookUrl, {
event: "exa.daily_digest",
date: new Date().toISOString().split("T")[0],
topics: digest,
});
}
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Rate limited monitors | Too many concurrent checks | Stagger monitor intervals |
| Empty results | Date filter too narrow | Widen to 48-hour windows |
| Duplicate alerts | Missing URL dedup | Track result URLs between runs |
| Webhook delivery fails | Endpoint down | Retry with exponential backoff |
Examples
Create a Competitive Intelligence Monitor
await createMonitor({
id: "competitor-watch",
query: "AI code review tools launch announcement",
webhookUrl: "https://api.myapp.com/webhooks/exa-alerts",
intervalMinutes: 60,
searchType: "neural",
});
Resources
- Exa Search Reference
- Exa Find Similar
- BullMQ Documentation
Next Steps
For deployment setup, see exa-deploy-integration.
When not to use it
- →When real-time native webhooks are required (Exa is synchronous)
Prerequisites
Limitations
- →Exa is a synchronous search API with no native webhooks
How it compares
This workflow enables event-driven behavior on a synchronous search API by wrapping it in a scheduled monitoring and state-tracking layer.
Compared to similar skills
exa-webhooks-events side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| exa-webhooks-events (this skill) | 1 | 26d | Caution | Intermediate |
| maintainx-incident-runbook | 1 | 26d | Caution | Intermediate |
| telegram-bot-builder | 106 | 6mo | Review | Intermediate |
| playwright-browser-automation | 29 | 7mo | 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
maintainx-incident-runbook
jeremylongshore
Incident response procedures for MaintainX integration failures. Use when experiencing outages, investigating issues, or responding to MaintainX integration incidents. Trigger with phrases like "maintainx incident", "maintainx outage", "maintainx down", "maintainx emergency", "maintainx runbook".
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
playwright-browser-automation
lackeyjb
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.
workflow-orchestration-patterns
wshobson
Design durable workflows with Temporal for distributed systems. Covers workflow vs activity separation, saga patterns, state management, and determinism constraints. Use when building long-running processes, distributed transactions, or microservice orchestration.
codex-skill
feiskyer
Use when user asks to leverage codex, gpt-5, or gpt-5.1 to implement something (usually implement a plan or feature designed by Claude). Provides non-interactive automation mode for hands-off task execution without approval prompts.
bullmq-specialist
davila7
BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.