# Search

Search the web, news or images and get ranked results. Optionally read the top results and get the passages that answer your query, scrape them in any format, or have a short cited answer written from them. Use Search when you want results to choose from; use [Research](https://xdive.dev/docs/research.md) when you want a finished answer with verified citations.

`POST https://xdive.dev/api/orgs/{id}/search`

Requires an API key with the `search` scope. New keys get `crawl` and `scrape` by default, so add `search` when you create the key. Search is synchronous: the search and any page reads share a 25-second budget, and `include_answer` gets 30 more seconds to write.

**cURL**

```bash
curl -X POST "https://xdive.dev/api/orgs/$ORG_ID/search" \
  -H "X-API-Key: $XDIVE_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "query": "rotate API keys without downtime",
    "limit": 5,
    "freshness": "py",
    "search_depth": "advanced"
  }'
```

**Python**

```python
from xdive import XDive

xdive = XDive()  # reads XDIVE_API_KEY
response = xdive.search(
    "rotate API keys without downtime",
    limit=5,
    freshness="py",
    search_depth="advanced",
)
for result in response["results"]:
    best = result.get("passages", [{}])[0].get("text", "")
    print(result["rank"], result["url"], best[:120])
```

**JavaScript**

```ts
import { XDive } from "xdive";

const xdive = new XDive(); // reads XDIVE_API_KEY
const response = await xdive.search("rotate API keys without downtime", {
  limit: 5,
  freshness: "py",
  search_depth: "advanced",
});
for (const result of response.results) {
  console.log(result.rank, result.url, result.passages?.[0]?.text.slice(0, 120));
}
```

## Parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `query` | string | required | The search query. At most 400 characters and 50 words. |
| `sources` | array of strings | `["web"]` | Any of `web`, `news`, `images`. Results from several sources are interleaved. |
| `limit` | integer | `10` | Results to return, 1 to 50. |
| `country` | string | none | Two-letter country code to localize results, such as `US` or `DE`. |
| `search_language` | string | none | Language code such as `en` or `en-us`. |
| `freshness` | string | none | Limit results by publication date. See [Freshness](#freshness). |
| `safe_search` | string | `moderate` | `off`, `moderate` or `strict`. |
| `include_domains` | array of strings | none | Only return results from these hosts. At most 20. See [Domain filters](#domain-filters). |
| `exclude_domains` | array of strings | none | Never return results from these hosts. At most 20. |
| `search_depth` | string | `basic` | `basic` returns the engine's results. `advanced` also reads the top results and attaches query-relevant `passages` and a `relevance_score`. See [Search depth](#search-depth). |
| `auto_parameters` | boolean | `false` | Infer `sources` and `freshness` from the wording of the query when you did not set them. |
| `scrape` | object | none | Scrape the top results. See [Scraping results](#scraping-results). |
| `include_answer` | boolean | `false` | Also write a short answer with citations from the top results. Needs an LLM. See [Answer](#answer). |

### Freshness

| Value | Results published |
|---|---|
| `pd` | In the past day. |
| `pw` | In the past week. |
| `pm` | In the past month. |
| `py` | In the past year. |
| `2026-01-01to2026-06-30` | Between two dates, inclusive, written `YYYY-MM-DDtoYYYY-MM-DD`. |

### Domain filters

Entries are host names such as `example.com`, without a scheme, path or port. A filter matches the host and its subdomains: `example.com` matches `docs.example.com` but not `notexample.com`. Filters are applied after the engine ranks results, so a narrow `include_domains` can return fewer than `limit` results.

### Search depth

With `search_depth: "advanced"`, the top 8 web and news results are read in parallel, through the page cache, within 6 seconds. Each result that could be read gains:

- `page`: the page's `url`, `title` and main-content `markdown`, so you need no second fetch;
- `passages`: up to 3 excerpts most relevant to the query, best first;
- `relevance_score`: the score of the best passage.

Results keep the search engine's order. Advanced search does not re-rank them by `relevance_score`; sort by it yourself if that suits your use. Results that could not be read in time have no `passages`. Each page read counts as one page against your quota.

Passage scores are BM25 relevance scores of the passage against the query: higher is more relevant, and `0` means no query term in common, so such passages are left out.

### Auto parameters

`auto_parameters: true` fills in parameters you left unset from the query's wording. Words such as "news", "latest", "breaking", "today" or "announced" add `news` to `sources`. "today", "yesterday", "breaking", "right now", "this week" or "latest" set `freshness` to `pw`; "this month", "recent" or "recently" set `pm`; the current year written in the query sets `py`. The parameters used are returned in `parameters`.

### Scraping results

`scrape` fetches the top web and news results through the scrape pipeline and attaches each one as `page`, with `passages` and `relevance_score` computed from its content.

| Field | Type | Default | Description |
|---|---|---|---|
| `limit` | integer | `3` | Results to scrape, 0 to 5. `0` means the default. |
| `formats` | array of strings | `["markdown"]` | Format names, as on [Scrape](https://xdive.dev/docs/scrape.md#formats). Format options are not accepted here. |
| `respect_robots` | boolean | `true` | Skip pages that robots.txt disallows. |
| `main_content_only` | boolean | `true` | Main-content extraction. |

A result that could not be scraped has `scrape_error` instead of `page`. With `advanced` and Markdown-only scraping, pages already read are reused; with other formats they are fetched again, but a page is never counted twice.

### Answer

`include_answer: true` writes an answer of at most about 150 words from the top results, citing them as `[1]`, `[2]`. Citation numbers are checked against the sources in code, so the answer cannot cite a page that was not read. If you do not pass `scrape`, the top 5 results are scraped for it.

The answer needs an LLM configured for your organization. Problems with the answer never fail the search: `answer` then holds an `error` instead.

## Response

```json
{
  "query": "rotate API keys without downtime",
  "provider": "brave",
  "search_depth": "advanced",
  "results": [
    {
      "id": "4f9c2a7e1b3d5f80",
      "source": "web",
      "rank": 1,
      "title": "Rotating API keys safely",
      "url": "https://docs.example.com/security/key-rotation",
      "description": "Issue a second key, deploy it, then revoke the first.",
      "published_at": "2026-03-02T00:00:00Z",
      "page": {
        "url": "https://docs.example.com/security/key-rotation",
        "title": "Rotating API keys safely",
        "depth": 0,
        "markdown": "# Rotating API keys safely\n\nKeep two keys active during a rotation..."
      },
      "passages": [
        {
          "text": "Keep two keys active during a rotation. Issue the new key, roll it out to every client, confirm traffic has moved, then revoke the old key.",
          "score": 6.218,
          "start": 412
        }
      ],
      "relevance_score": 6.218
    },
    {
      "id": "b07e19d2c4a6f835",
      "source": "web",
      "rank": 2,
      "title": "Zero-downtime credential rotation",
      "url": "https://blog.example.org/zero-downtime-rotation",
      "description": "A checklist for rotating secrets in production."
    }
  ]
}
```

| Field | Type | Description |
|---|---|---|
| `query` | string | The query as searched. |
| `provider` | string | The search engine used, such as `brave` or `serper`; several engines are joined with `+` (`brave+serper`) when their results are fused. |
| `search_depth` | string | The `search_depth` you sent, empty when omitted. |
| `parameters` | object | With `auto_parameters`: the `sources` and `freshness` that were used. |
| `results` | array | The results, best first. |
| `answer` | object | With `include_answer`: `text`, `citations` and `confidence`, or `error`. |

### Result fields

| Field | Type | Description |
|---|---|---|
| `id` | string | Stable identifier of the result. |
| `source` | string | `web`, `news` or `images`. |
| `rank` | integer | Position in this response, from 1. |
| `title`, `url`, `description` | string | As returned by the engine. |
| `published_at` | string | Publication time, when the engine knows it. |
| `image` | object | For image results: `url`, `thumbnail`, `width`, `height`. |
| `page` | object | The page's content, from `advanced` or `scrape`. With `scrape`, a full [scrape page object](https://xdive.dev/docs/scrape.md#the-page-object). |
| `passages` | array | Up to 3 query-relevant excerpts, best first. Each has `text`, `score` and `start`, the byte offset of the excerpt in the page's text. |
| `relevance_score` | number | The best passage's score. |
| `scrape_error` | string | Why `scrape` could not fetch this result. |

### Answer fields

| Field | Type | Description |
|---|---|---|
| `text` | string | The answer, with citation markers such as `[1]`. |
| `citations` | array | The sources the answer cites: `number`, `url`, `title` and a supporting `quote`. |
| `confidence` | string | `high`, `medium`, `low` or `none`, from how many sources the answer cites. See [Research](https://xdive.dev/docs/research.md#confidence). |
| `error` | string | Present instead of the fields above when no answer could be written, for example when no LLM is configured. |

## Usage

Each request counts as one search. Pages read by `advanced` and pages fetched by `scrape` count as pages. Identical engine queries are cached for an hour (10 minutes for news or `freshness` searches) and are answered faster, but still count as a search. A request is refused with `402` when your search quota, or for `advanced` and `scrape` your page quota, is used up.

## Errors

| Status | Code | When |
|---|---|---|
| `400` | | Missing or too long `query`, an unknown source, `limit` outside 1 to 50, an invalid `country`, `search_language`, `freshness` or `safe_search`, more than 20 domain filters or a filter that is not a host name, an invalid `search_depth`, `scrape.limit` outside 0 to 5, or an unknown format in `scrape.formats`. |
| `402` | | The monthly search quota, or the page quota for `advanced` and `scrape`, is used up. |
| `403` | | The key lacks the `search` scope. |
| `429` | | Your plan's request rate limit was exceeded. Retry after `Retry-After`. |
| `429` | `CONCURRENCY_LIMIT` | Too many concurrent requests for your plan. Retry after `Retry-After`. |
| `502` | | The search engine failed. |
| `503` | | No search engine is configured on the server. |
| `503` | `SERVER_BUSY` | The server is at capacity. Retry after `Retry-After`. |

See [Errors and limits](https://xdive.dev/docs/errors.md) for authentication errors and retries.

## Related

- [Research](https://xdive.dev/docs/research.md) for an answer with verified citations from a full search-and-read run.
- [Scrape](https://xdive.dev/docs/scrape.md) for formats and their options.
- [Usage and billing](https://xdive.dev/docs/usage.md) for what is metered.

---

Source: https://xdive.dev/docs/search · All docs for agents: https://xdive.dev/llms.txt
