## 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](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

```
pip install exa-py    # Python
npm install exa-js    # JavaScript
```

## Minimal Working Example

```
from exa_py import Exa
from exa_py.websets.types import CreateWebsetParameters, CreateEnrichmentParameters
import os

exa = Exa(os.getenv("EXA_API_KEY"))

webset = exa.websets.create(
    params=CreateWebsetParameters(
        search={
            "query": "Top AI research labs focusing on large language models",
            "count": 5
        },
        enrichments=[
            CreateEnrichmentParameters(
                description="LinkedIn profile of VP of Engineering",
                format="text",
            ),
        ],
    )
)
print(f"Webset ID: {webset.id}")
print(f"Dashboard: {webset.dashboard_url}")

# Wait for completion
webset = exa.websets.wait_until_idle(webset.id)

# Get results
items = exa.websets.items.list(webset_id=webset.id)
for item in items.data:
    print(item.model_dump_json(indent=2))
```

```
import Exa from "exa-js";
const exa = new Exa(process.env.EXA_API_KEY);

const webset = await exa.websets.create({
  search: {
    query: "Top AI research labs focusing on large language models",
    count: 10
  },
  enrichments: [
    { description: "Estimate founding year", format: "number" }
  ],
});
console.log(`Webset ID: ${webset.id}`);

const idleWebset = await exa.websets.waitUntilIdle(webset.id, {
  timeout: 60000,
  pollInterval: 2000
});

const items = await exa.websets.items.list(webset.id, { limit: 10 });
for (const item of items.data) {
  console.log(JSON.stringify(item, null, 2));
}
```

```
# Create a webset
curl -s -X POST "https://api.exa.ai/websets/v0/websets/" \
  -H "accept: application/json" \
  -H "content-type: application/json" \
  -H "x-api-key: ${EXA_API_KEY}" \
  -d '{
    "search": {
      "query": "Top AI research labs focusing on large language models",
      "count": 5
    },
    "enrichments": [
      {"description": "Find founding year", "format": "number"}
    ]
  }'

# Check status
curl "https://api.exa.ai/websets/v0/websets/{WEBSET_ID}" \
  -H "accept: application/json" \
  -H "x-api-key: ${EXA_API_KEY}"

# List items
curl "https://api.exa.ai/websets/v0/websets/{WEBSET_ID}/items" \
  -H "accept: application/json" \
  -H "x-api-key: ${EXA_API_KEY}"

# Get webset with items expanded
curl "https://api.exa.ai/websets/v0/websets/{WEBSET_ID}?expand=items" \
  -H "accept: application/json" \
  -H "x-api-key: ${EXA_API_KEY}"
```

## 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:**
```
{
  "search": {
    "query": "AI companies in Europe that raised Series A funding",
    "count": 50,
    "criteria": [
      {"description": "Company is an AI startup"},
      {"description": "Company has raised Series A funding"}
    ],
    "entity": {"type": "company"},
    "behaviour": "override"
  },
  "enrichments": [
    {"description": "Find the CEO name", "format": "text"},
    {"description": "Estimate founding year", "format": "number"},
    {"description": "Industry vertical", "format": "options", "options": [{"label": "Healthcare"}, {"label": "Finance"}, {"label": "Education"}, {"label": "Other"}]} 
  ],
  "externalId": "my-unique-id",
  "metadata": {"project": "market-research"}
}
```
**Field details:**
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `search.query` | string (min 1 char) | Yes | Natural language query. Any URL in the query will be crawled and used as context. |
| `search.count` | number (>= 1) | No (default 10) | Target number of items to find. Actual results may be fewer depending on query complexity. |
| `search.criteria` | array (1–5 items) | No | Verification rules. Auto-detected from query if omitted. Each has a `description` string. |
| `search.entity` | object | No | One of: `{ "type": "company" }`, `{ "type": "person" }`, `{ "type": "article" }`, `{ "type": "research_paper" }`, `{ "type": "custom", "description": "Job Postings" }`. Auto-detected if omitted. |
| `search.behaviour` | string | No (default `"override"`) | `"override"`: reuses existing items, re-evaluates against new criteria, discards non-matching. |
| `enrichments` | array (max 10) | No | Each enrichment has `description` (required), `format`, `options`, `metadata`. |
| `externalId` | string | No | Your own identifier. Can be used in place of webset ID in all GET/PATCH/DELETE calls. Returns 409 if duplicate. |
| `metadata` | object | No | Arbitrary key-value pairs for the webset. |

**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:**
| Param | Type | Description |
| --- | --- | --- |
| `cursor` | string | Pagination cursor from previous response’s `nextCursor`. |
| `limit` | number | Results per page (max 200). |

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

### Items
#### GET `/websets/{websetId}/items` — List Items
**Query params:**
| Param | Type | Description |
| --- | --- | --- |
| `cursor` | string | Pagination cursor. |
| `limit` | number | Results per page. |

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

#### GET `/websets/{websetId}/items/{itemId}` — Get a Single Item
**Response:** A `WebsetItem` object.

### Enrichments
#### POST `/websets/{websetId}/enrichments` — Add an Enrichment
```
{
  "description": "Find the company's LinkedIn page URL",
  "format": "url",
  "metadata": {"source": "linkedin"}
}
```

#### GET `/websets/{websetId}/enrichments/{enrichmentId}` — Get Enrichment Status
**Response:** A `WebsetEnrichment` object.
