> SpecStream is the live web data engine for teams and agents. This is the Markdown copy of https://specstream.ai/docs/research. Every page, with what SpecStream does and costs: https://specstream.ai/llms.txt

# Research reports

POST /v1/research answers a question with a written report from the live web. Each cited sentence links to the page it came from, with the time the page was read.

## Start a report

Send your question as `input` and choose a `depth`. The call answers `202` at once with the report’s `id` and its price. Create a key under [Agents & API](https://specstream.ai/workspace/agents?tab=keys) in your workspace. [Authentication](https://specstream.ai/docs/api.md#authentication)

Terminal

```bash
curl https://api.specstream.ai/v1/research \
  -H "Authorization: Bearer $SPECSTREAM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: report-2026-11-13-001" \
  -d '{"input": "Which countries have a retail central bank digital currency, and when did each launch?", "depth": "quick"}'
```

JSON

```json
{
  "data": {
    "id": "rr_836a49eb6e934a1101427e77",
    "status": "queued",
    "depth": "quick",
    "price_usd": 0.1,
    "links": {
      "self": "/v1/research/rr_836a49eb6e934a1101427e77",
      "events": "/v1/research/rr_836a49eb6e934a1101427e77/events"
    }
  }
}
```

## Read the report

Read `GET /v1/research/{id}` until `status` is `completed`. A quick report takes about a minute, a standard report about four minutes, and a deep report about five. To follow the work as it happens, open `GET /v1/research/{id}/events`: it is a server-sent event stream that names each stage and each page as it is read, and `Last-Event-ID` resumes it.

Terminal

```bash
curl https://api.specstream.ai/v1/research/rr_836a49eb6e934a1101427e77 \
  -H "Authorization: Bearer $SPECSTREAM_API_KEY"
```

A completed report, cut short:

JSON

```json
{
  "data": {
    "id": "rr_836a49eb6e934a1101427e77",
    "status": "completed",
    "stage": "done",
    "depth": "quick",
    "progress": {"searches": 5, "pages_read": 8, "sources": 7},
    "report": {
      "title": "...",
      "format": "markdown",
      "content": "... markdown with [1] markers ...",
      "citations": [
        {"n": 1, "url": "https://...", "title": "...", "read_at": "2026-11-12T23:16:31Z",
         "excerpt": "the passage behind the sentence", "start": 812, "end": 977, "supported": true}
      ],
      "sources": [{"n": 1, "url": "https://...", "title": "...", "read_at": "2026-11-12T23:16:31Z", "cited": true}]
    },
    "usage": {"searches": 5, "pages_read": 8, "charged_usd": 0.1}
  }
}
```

| Field | What it holds |
| --- | --- |
| `status` | queued, running, completed, failed, cancelled, or payment\_required. |
| `stage` | queued, planning, searching, reading, writing, checking, or done. |
| `report.content` | The text, with a \[1\] marker after each cited sentence. For JSON output, report.structured holds the object. |
| `report.citations` | One entry per cited sentence: the page, when it was read, the passage, and where the sentence sits in content (start and end). |
| `report.sources` | Every page read for the report, with its read time. |
| `usage` | The searches run, the pages read, and charged\_usd, the price taken for this report. |

The same two calls in Python:

Python

```python
import os, time, requests

api = "https://api.specstream.ai"
auth = {"Authorization": "Bearer " + os.environ["SPECSTREAM_API_KEY"]}

run = requests.post(api + "/v1/research", headers=auth,
                    json={"input": "<your question>", "depth": "standard"}).json()["data"]
while run["status"] in ("queued", "running"):
    time.sleep(5)
    run = requests.get(api + "/v1/research/" + run["id"], headers=auth).json()["data"]

print(run["report"]["content"])
for citation in run["report"]["citations"]:
    print(citation["n"], citation["url"], citation["read_at"])
```

`POST /v1/research/{id}/cancel` stops a report. `GET /v1/research` lists your account’s reports, newest first.

## Price and retries

A report is $0.10 at quick depth, $0.30 at standard, and $1 at deep. The price comes out of your balance once, when the report is delivered. A canceled report is not charged. Send an `Idempotency-Key` header (at most 128 characters) to retry safely: the same key and body return the same report.

## What you can send

| Field | What it does |
| --- | --- |
| `input` | The question, in plain words. Up to 15,000 characters. |
| `depth` | quick, standard (the default), or deep. Each depth has one price and reads more of the web than the one before it. |
| `output` | {"type": "text"} for markdown (the default), or {"type": "json", "schema": {...}} for JSON that matches your JSON Schema. |
| `length` | short, standard, or long. It sets how long the text is. |
| `language` | The report’s language. By default it is the question’s language. |
| `include_domains, exclude_domains` | The sites to read, or the sites to leave out. |
| `webhook` | {"url": "https://..."}. SpecStream calls it when the report ends, signed with your account’s webhook secret. |
| `metadata` | Up to 16 keys of your own. They come back on the report. |

## On MCP

`research_price` shows the price of each depth. `start_research` starts a report once the user approves that price as `confirm_max_cents`. `get_research` reads it until it is completed, `cancel_research` stops it, and `list_research` lists the account’s reports. [Set up MCP](https://specstream.ai/docs/mcp.md)

## When a call stops

| Status | What happened |
| --- | --- |
| `401` | No key or token. Send Authorization: Bearer with your API key. |
| `402` | insufficient\_balance: the balance doesn’t hold the report’s price. Add balance under Settings, Usage and billing, then send it again. |
| `404` | No report with this ID on your account. |
| `409` | The Idempotency-Key was used with another body. Send a new key. |
| `422` | A field is wrong. The reply names the field and the problem. |
| `429` | The account used its calls for the minute. Wait the seconds in Retry-After and send it again. |
