Skip to main content

Overview

Base URL: https://api.exa.ai/websets/v0 Auth: Pass your API key via the x-api-key header. Get one at https://dashboard.exa.ai/api-keys Websets is an asynchronous search system. You define a query, criteria for verification, and optional enrichments. The system searches, verifies each result, and returns structured items over time. Results are available via polling or webhooks.

Installation

Minimal Working Example

SDK Sub-Client Reference

The SDKs provide sub-clients for all API resources. Here are the key operations beyond the minimal example above. Python SDK note: All response attributes use snake_case. JSON field hasMorehas_more, nextCursornext_cursor, createdAtcreated_at, externalIdexternal_id, websetIdwebset_id.

How Websets Work

Lifecycle

  1. Create — You POST a search config (query, count, optional criteria/enrichments/entity type). A webset is created with status running.
  2. Search — The system searches and verifies each result against your criteria. Matching items are added to the webset. Each item triggers a webset.item.created event.
  3. Enrichment — If enrichments are configured, each item is processed. webset.item.enriched events fire as enrichment results arrive.
  4. Idle — When all searches and enrichments complete, the webset status becomes idle and a webset.idle event fires.

Key Concepts

  • Search: Defines what to look for (query + count). Multiple searches can be added to one webset.
  • Criteria: Verification rules. Each result is checked against criteria before becoming an item. Max 5 criteria per search.
  • Entity: Optional type hint (e.g. "company", "person", "article", "research_paper", "custom") that shapes how results are found and verified. Auto-detected if not specified.
  • Enrichments: Additional data extraction applied to each item (e.g. “Find the CEO name”). Max 10 per webset.
  • Monitors: Scheduled re-runs that keep websets updated. Supports cron expressions.
  • Webhooks: Real-time HTTP callbacks for events.
  • Imports: Bring your own URLs and run enrichments on them.
  • Exports: Bulk download of webset items as CSV/JSON.

API Endpoints — Full Reference

Websets

POST /websets/ — Create a Webset

Request body:
Field details: Response: A Webset object (see Object Schemas below).

GET /websets/{id} — Get a Webset

  • {id} can be the webset ID or externalId.
  • Query param ?expand=items includes up to 100 items in the response.
Response: A Webset object. When expanded, includes an items array of WebsetItem objects.

GET /websets/ — List All Websets

Query params: Response: { "data": [Webset, ...], "hasMore": boolean, "nextCursor": string | null }

POST /websets/{id} — Update a Webset

Request body:
Only metadata can be updated. Response: Updated Webset object.

DELETE /websets/{id} — Delete a Webset

Deletes the webset and all associated items, searches, and enrichments. Response: The deleted Webset object.

POST /websets/{id}/cancel — Cancel Running Operations

Cancels all running searches and enrichments on the webset. Response: The Webset object with updated status.

POST /websets/preview — Preview Search Results

Runs a search without creating a webset. Same request body as create. Useful for testing queries before committing.

Items

GET /websets/{websetId}/items — List Items

Query params: Response: { "data": [WebsetItem, ...], "hasMore": boolean, "nextCursor": string | null }

GET /websets/{websetId}/items/{itemId} — Get a Single Item

Response: A WebsetItem object.

DELETE /websets/{websetId}/items/{itemId} — Delete an Item

Response: The deleted WebsetItem object.

Searches

POST /websets/{websetId}/searches — Add a Search

Add a new search to an existing webset. Request body is the same shape as search in the create webset request:
Searches run sequentially (not in parallel with other searches), but can run in parallel with enrichments. Response: A WebsetSearch object.

GET /websets/{websetId}/searches/{searchId} — Get Search Status

Response: A WebsetSearch object with progress field showing found count and completion percentage (0–100).

POST /websets/{websetId}/searches/{searchId}/cancel — Cancel a Search

Response: The canceled WebsetSearch object.

Enrichments

POST /websets/{websetId}/enrichments — Add an Enrichment

Enrichments are applied to all existing items and any future items added to the webset. Response: A WebsetEnrichment object.

GET /websets/{websetId}/enrichments/{enrichmentId} — Get Enrichment Status

Response: A WebsetEnrichment object.

PATCH /websets/{websetId}/enrichments/{enrichmentId} — Update an Enrichment

Response: Updated WebsetEnrichment object.

DELETE /websets/{websetId}/enrichments/{enrichmentId} — Delete an Enrichment

Response: The deleted WebsetEnrichment object.

POST /websets/{websetId}/enrichments/{enrichmentId}/cancel — Cancel a Running Enrichment

Response: The canceled WebsetEnrichment object.

Exports

POST /websets/{websetId}/exports — Schedule an Export

Generates a downloadable file of all items. Request body:
Response: An export object with id, status (pendingcompleted), and downloadUrl (available when completed).

GET /websets/{websetId}/exports/{exportId} — Get Export Status

Poll until status is completed, then use the downloadUrl.

Imports

Imports let you bring your own URLs (e.g. from a CSV) and run enrichments on them.

POST /imports — Create an Import

Response: An import object with id and status.

GET /imports/{importId} — Get Import Details

Response: Import object with status and progress.

GET /imports — List All Imports

Query params: cursor, limit (same pagination pattern). Response: { "data": [Import, ...], "hasMore": boolean, "nextCursor": string | null }

PATCH /imports/{importId} — Update an Import

DELETE /imports/{importId} — Delete an Import


Monitors

Monitors run searches on a schedule to keep websets updated.

POST /monitors — Create a Monitor

Field details: Response: A monitor object.

GET /monitors/{monitorId} — Get Monitor Details

PATCH /monitors/{monitorId} — Update a Monitor

Update cadence, behavior, or metadata.

DELETE /monitors/{monitorId} — Delete a Monitor

GET /monitors — List All Monitors

Query params: cursor, limit. Response: { "data": [Monitor, ...], "hasMore": boolean, "nextCursor": string | null }

GET /monitors/{monitorId}/runs — List Monitor Runs

Returns the history of executions for this monitor.

GET /monitors/{monitorId}/runs/{runId} — Get a Monitor Run


Webhooks

POST /webhooks — Create a Webhook

Response: A Webhook object. Important: The secret field is only returned on creation. Store it securely for signature verification.
Redirects are not followed. Webhook deliveries are sent directly to the registered url. If your endpoint responds with a 3xx redirect, the delivery will be treated as a failure. Always register the final destination URL.

GET /webhooks/{webhookId} — Get Webhook Details

PATCH /webhooks/{webhookId} — Update a Webhook

Update url, events, or metadata.

DELETE /webhooks/{webhookId} — Delete a Webhook

GET /webhooks — List All Webhooks

Query params: cursor, limit.

GET /webhooks/{webhookId}/attempts — List Delivery Attempts

Returns the history of delivery attempts for this webhook, including response status codes and bodies. Response: { "data": [WebhookAttempt, ...], "hasMore": boolean, "nextCursor": string | null }

Webhook Signature Verification

Webhooks are signed with HMAC SHA256. The signature is in the Exa-Signature header:
Verification steps:
  1. Parse the header to extract t (timestamp) and v1 (signature).
  2. Construct the signed payload: {timestamp}.{raw_request_body}.
  3. Compute HMAC SHA256 using the secret from webhook creation.
  4. Compare your computed signature with v1.

Events

Events track state changes across the system. Retained for 60 days.

GET /events — List All Events

Query params: cursor, limit. Response: { "data": [Event, ...], "hasMore": boolean, "nextCursor": string | null }

GET /events/{eventId} — Get a Single Event

Response: An event object with id, object ("event"), type, data, and createdAt.

Teams

GET /teams/me — Get Team Info

Returns your team’s concurrency usage and limits.

Object Schemas

Webset

Status values: running, idle, paused

WebsetSearch

Status values: created, running, completed, canceled Canceled reasons: webset_deleted, webset_canceled Progress: found = number of items discovered so far. completion = percentage (0–100).

WebsetItem

Item Properties by Entity Type

Company (properties.type = "company"): Person (properties.type = "person"): Article (properties.type = "article"): Research Paper (properties.type = "research_paper"): Custom (properties.type = "custom"):

WebsetEnrichment

Status values: pending, completed, canceled Format values: text, number, date, url, email, phone, options When format is options, the options array contains objects with a label field (max 20 options).

EnrichmentResult (on each item)

result is always an array of strings (even for numbers/dates — they’re stringified). null if the enrichment couldn’t find the data.

Evaluation (on each item)

satisfied values: yes, no, unclear

Webhook

Status values: active, inactive secret is only returned on creation. Store it immediately for signature verification.

WebhookAttempt


Event Types

Event shape:
Events are retained for 60 days before automatic deletion.

Pagination

All list endpoints use cursor-based pagination:
Pass nextCursor as the cursor query parameter in the next request. Continue until hasMore is false. Python SDK: Use page.has_more and page.next_cursor (snake_case attributes). JavaScript SDK: Use page.hasMore and page.nextCursor.

Patterns and Best Practices

  • Websets are async. After creating, poll with GET or use webhooks. Don’t expect results in the create response.
  • Use wait_until_idle in SDKs to block until processing completes. Default timeout is 3600s (1 hour), poll interval 5s.
  • Multiple searches can run on one webset. Use POST /websets/{id}/searches to add more. Searches run sequentially with each other but in parallel with enrichments.
  • Items are available immediately. You can list items while the webset is still running.
  • Enrichment format controls output type. Use text, number, date, url, email, phone, or options.
  • options format requires an options array with 1–20 items, each having a label string.
  • Monitor cron triggers at most once per day. This is a system constraint.
  • Use expand=items for convenience. GET /websets/{id}?expand=items returns the webset and its latest 100 items in one call.
  • Use externalId for idempotency. Set externalId on creation to prevent duplicate websets. Returns 409 if the ID already exists. You can then use externalId in place of id for all subsequent API calls.
  • Webhook secrets are shown once. The secret field is only returned in the create webhook response. Store it immediately.
  • Enrichment results are arrays. Even for single values, result is always ["value"] or null if not found.
  • Criteria successRate on search responses shows what percentage (0–100) of evaluated items matched that criterion.
  • Entity type auto-detection works well. Only specify entity when you need fine control. For non-standard entities, use {"type": "custom", "description": "Your entity type"}.
  • Item data is nested under properties. Access item.properties.url, item.properties.company.name, etc. — not item.url. Enrichment results are at item.enrichments[].result (always a list[str] or null).
  • Enrichment results have enrichmentId, not description. To get the human-readable description, build a map from webset.enrichments: {e.id: e.description for e in webset.enrichments}, then look up enr.enrichment_id.
  • Initial search is on the webset object. After create(), the search is at webset.searches[0] — no separate list call needed. Poll progress via searches.get(webset_id, search_id).

Full API Reference

For detailed request/response schemas for each endpoint, see the Websets API Reference.
Last modified on May 28, 2026