# Research

Ask a question and get an answer written from the web, in which every claim cites a source that was actually read. A research run searches, reads the best pages, keeps the passages relevant to the question, and has a language model write the answer; the citations are then checked in code. Use it for questions you would otherwise answer by searching and reading several pages yourself. For raw results to choose from, use [Search](https://xdive.dev/docs/search.md).

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

Requires an API key with the `search` scope, an LLM configured for your organization and a search engine on the server. Research is synchronous: a quick run takes seconds and is stopped after 45 seconds, a deep run after 150 seconds.

**cURL**

```bash
curl -X POST "https://xdive.dev/api/orgs/$ORG_ID/research" \
  -H "X-API-Key: $XDIVE_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "query": "How long does Example Cloud keep audit logs by default?",
    "depth": "quick",
    "freshness": "py"
  }'
```

**Python**

```python
from xdive import XDive

xdive = XDive()  # reads XDIVE_API_KEY
result = xdive.research(
    "How long does Example Cloud keep audit logs by default?",
    depth="quick",
    freshness="py",
)
print(result["answer"])
for citation in result["citations"]:
    print(f"[{citation['number']}] {citation['url']}")
```

**JavaScript**

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

const xdive = new XDive(); // reads XDIVE_API_KEY
const result = await xdive.research("How long does Example Cloud keep audit logs by default?", {
  depth: "quick",
  freshness: "py",
});
console.log(result.answer);
for (const citation of result.citations) console.log(`[${citation.number}] ${citation.url}`);
```

> **Tip:**
>
> The SDKs time out requests after 120 seconds by default. For `depth: "deep"`, raise it: `XDive(timeout=180)` in Python, `new XDive({ timeoutMs: 180_000 })` in JavaScript.

## Parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `query` | string | required | The question, at most 500 characters. Answers are written in the language of the question. |
| `depth` | string | `quick` | `quick`: one search round, seconds. `deep`: planned sub-queries and up to three rounds. See [How a run works](#how-a-run-works). |
| `output` | string | `answer` for quick, `report` for deep | `answer`: a direct answer of at most 150 words. `report`: a structured Markdown report of about 400 to 800 words. |
| `schema` | object | none | A JSON Schema. The result is returned as `data` instead of prose. See [Structured output](#structured-output). |
| `max_sources` | integer | `6` quick, `15` deep | Sources to keep for the answer. At most 10 above the default (16 quick, 25 deep); larger values are capped. |
| `include_domains` | array of strings | none | Only use results from these hosts and their subdomains, as on [Search](https://xdive.dev/docs/search.md#domain-filters). |
| `exclude_domains` | array of strings | none | Never use results from these hosts. |
| `freshness` | string | none | `pd`, `pw`, `pm`, `py` or a `YYYY-MM-DDtoYYYY-MM-DD` range, as on [Search](https://xdive.dev/docs/search.md#freshness). |
| `country` | string | none | Two-letter country code to localize the searches. |
| `no_cache` | boolean | `false` | Run fresh instead of serving a cached result, and do not cache this one. See [Caching](#caching). |

## How a run works

### Quick

1. The question is searched (10 results). Pages found by several queries rank higher, and no site gets more than two places.
2. Up to 10 candidate pages are read in parallel, through the page cache, within 6 seconds. Once 3 pages have arrived, the run waits at most 0.7 seconds for more instead of holding the answer for the slowest site. Pages that were not read in time contribute their search snippet instead; such sources are marked `snippet: true` and rank below pages that were read.
3. Each page is split into passages and the passages most relevant to the question are kept, up to 4 per source, across up to 6 sources.
4. The model writes the answer from those numbered passages, citing them as `[1]`, `[2]`.

### Two-step questions

Some questions describe a thing indirectly and then ask about it, such as "Name a co-founder of the company that develops the V8 JavaScript engine." A single search for that question finds pages about V8, not about the company's founders.

In quick mode, while the first search runs, the model triages the question. When it finds a two-step question, the intermediate thing (here, the company) is identified from the pages already read, never from the model's own knowledge; if those pages do not name it, one search for it is made. The remaining question is then searched with the name filled in, adding up to 4 sources. `usage.plan` reports the triage (`single`, `multi_hop`, or `unavailable` when it failed or was not ready 2 seconds after the first round, and was ignored) and `usage.hops` is `1` when a bridge was made. The triage is skipped when you pass a `schema`.

### Follow-up search

When the writer reports that the sources lack a fact the question needs, it names a search for exactly that fact. Quick mode makes that one follow-up search, reads up to 4 more sources within 4 seconds, and rewrites the answer. If the writer asked for more without naming a search, a short gap check decides what to search. The follow-up is skipped when a two-step bridge was already made or a `schema` was passed, and if the rewrite fails the first answer is returned. A quick run makes at most three search rounds in total.

If the writer still returns no answer, it is asked once more to answer from the sources it has, or to say in one sentence that they do not contain the answer and what they do say, with citations. A run that found sources does not come back with an empty answer.

### Deep

The model first plans 2 to 4 sub-queries that cover the question's aspects; they are searched together with the question. After each round a gap check lists up to 3 queries for what is still missing, and the next round searches them, for up to 3 rounds. Each round reads up to 24 candidates within 20 seconds, and up to 15 sources are kept. The default output is a report.

## Citations

Sources are numbered from 1 and passed to the model as untrusted data, with instructions to ignore any instructions inside them; the model has no tools, so a page can change the words of the answer but not what is fetched or cited. After the model writes, every `[n]` marker is checked in code against the numbered sources:

- a number that matches no source is removed from the text;
- groups such as `[1, 3]` and ranges such as `[1-3]` are expanded, and repeats collapse;
- `citations` lists exactly the sources the answer cites, in number order, each with a `quote`: up to 300 characters of that source's most relevant passage.

A fabricated citation cannot survive this check. `sources` lists every source that was given to the model, cited or not.

### Confidence

`confidence` summarizes how well the answer is grounded:

| Value | When |
|---|---|
| `high` | The answer cites at least 2 sources, and at least half of all sources. |
| `medium` | The answer cites at least 1 source. |
| `low` | The answer cites no source. |
| `none` | There is no answer. |

## Structured output

Pass `schema` to get JSON instead of prose. The model fills the schema from the sources and uses `null` for anything they do not support. The response has `data` instead of `answer`, and because structured output carries no citation markers, every source is listed in `citations`. The schema must be a JSON object.

```json
{
  "query": "Audit log retention and export options in Example Cloud",
  "schema": {
    "type": "object",
    "properties": {
      "default_retention_days": { "type": "integer" },
      "max_retention_days": { "type": "integer" },
      "export_formats": { "type": "array", "items": { "type": "string" } }
    }
  }
}
```

## Caching

An identical request from your organization within 10 minutes is answered from a cache, with `cached: true`, and is not metered. Requests count as identical after trimming the query and filling in the default `depth` and `output`. Identical requests that arrive while a run is in progress share that run. Failed runs are not cached. Set `no_cache: true` to force a fresh run.

## Response

```json
{
  "query": "How long does Example Cloud keep audit logs by default?",
  "answer": "Example Cloud keeps audit log entries for 90 days by default [1]. Enterprise plans can extend retention to 365 days [1][2].",
  "citations": [
    {
      "number": 1,
      "url": "https://docs.example.com/security/audit-logs",
      "title": "Audit logs",
      "quote": "Audit log entries are retained for 90 days. On the Enterprise plan, retention can be extended to 365 days from the security settings."
    },
    {
      "number": 2,
      "url": "https://example.com/pricing",
      "title": "Pricing",
      "quote": "Enterprise: SSO, SCIM, audit log retention up to 365 days, and a 99.95% uptime SLA."
    }
  ],
  "sources": [
    {
      "number": 1,
      "url": "https://docs.example.com/security/audit-logs",
      "title": "Audit logs",
      "passages": [
        {
          "text": "Audit log entries are retained for 90 days. On the Enterprise plan, retention can be extended to 365 days from the security settings.",
          "score": 7.412,
          "start": 318
        }
      ],
      "score": 7.412
    },
    {
      "number": 2,
      "url": "https://example.com/pricing",
      "title": "Pricing",
      "passages": [
        {
          "text": "Enterprise: SSO, SCIM, audit log retention up to 365 days, and a 99.95% uptime SLA.",
          "score": 4.106,
          "start": 2210
        }
      ],
      "score": 4.106
    },
    {
      "number": 3,
      "url": "https://blog.example.org/audit-logging-compared",
      "title": "Audit logging compared",
      "passages": [
        {
          "text": "Example Cloud stores audit events for three months unless you upgrade.",
          "score": 3.877,
          "start": 0
        }
      ],
      "score": 2.326,
      "snippet": true
    }
  ],
  "queries": ["How long does Example Cloud keep audit logs by default?"],
  "confidence": "high",
  "usage": {
    "searches": 1,
    "pages_read": 5,
    "rounds": 1,
    "plan": "single",
    "duration_ms": 4210,
    "search_ms": 812,
    "read_ms": 1630,
    "model_ms": 1544
  }
}
```

| Field | Type | Description |
|---|---|---|
| `query` | string | The question, trimmed. |
| `answer` | string | The answer, with citation markers. Absent when `schema` was passed. |
| `data` | object | The filled schema, when `schema` was passed. |
| `citations` | array | The sources the answer cites: `number`, `url`, `title`, `quote`. |
| `sources` | array | Every source given to the model. See below. |
| `queries` | array of strings | Every search query the run made, in order, including sub-queries, bridge and follow-up searches. |
| `confidence` | string | `high`, `medium`, `low` or `none`. See [Confidence](#confidence). |
| `usage` | object | The work the run did. See below. |
| `cached` | boolean | `true` when the result came from the cache. Absent otherwise. |

### Sources

| Field | Type | Description |
|---|---|---|
| `number` | integer | The number the answer cites. |
| `url`, `title` | string | The page. |
| `passages` | array | Up to 4 relevant excerpts, best first: `text`, `score` (BM25 relevance) and `start` (byte offset in the page's text). |
| `score` | number | The best passage's score, lowered for snippet sources. Sources are ordered by it. |
| `snippet` | boolean | `true` when the page could not be read in time and the passage is the search engine's snippet. Absent otherwise. |

### Usage

| Field | Type | Description |
|---|---|---|
| `searches` | integer | Search engine queries that completed. |
| `pages_read` | integer | Pages fetched and read. |
| `rounds` | integer | Search-and-read rounds. |
| `hops` | integer | Two-step bridges made (quick mode). Absent when 0. |
| `plan` | string | The two-step triage: `single`, `multi_hop` or `unavailable`. Quick mode without `schema` only. |
| `duration_ms` | integer | Total run time. |
| `search_ms`, `read_ms`, `model_ms` | integer | Time spent searching, reading pages and waiting for the model, summed across rounds. |

A fresh run counts `searches` against your search quota and `pages_read` against your page quota. Cached results cost nothing. A request is refused with `402` when either quota is already used up.

## Errors

Errors with a `code` also have `"success": false`.

| Status | Code | When |
|---|---|---|
| `400` | | Missing `query`, a query over 500 characters, an invalid `depth` or `output`, or a body that is not valid JSON. |
| `402` | | The monthly search or page quota is used up. |
| `403` | | The key lacks the `search` scope. |
| `404` | `NO_SOURCES` | No readable source was found for the question. Broaden the question or loosen domain and freshness filters. |
| `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`. |
| `501` | | No LLM is configured for the organization, or no search engine on the server. |
| `502` | | The model or another upstream service failed. |
| `503` | `SERVER_BUSY` | The server is at capacity. Retry after `Retry-After`. |
| `504` | `RESEARCH_TIMEOUT` | The run did not finish within 45 seconds (quick) or 150 seconds (deep). Try `depth: "quick"` or a narrower question. |

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

## Related

- [Search](https://xdive.dev/docs/search.md) for ranked results with passages, and `include_answer` for a short answer from the top results.
- [Extract](https://xdive.dev/docs/extract.md) to fill a schema from pages you already know.
- [Usage and billing](https://xdive.dev/docs/usage.md) for what is metered.

---

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