---
title: "Runs, background mode and retries"
description: "Every agent turn is a run: start it in the background, poll or stream it, resume a dropped stream, retry safely with Idempotency-Key, and cancel it."
canonical_url: "https://scholarxiv.com/developers/docs/agent-api/runs"
markdown_url: "https://scholarxiv.com/developers/docs/agent-api/runs.md"
---

> For the complete documentation index, see [llms.txt](/llms.txt).

# Runs, background mode and retries
URL: /developers/docs/agent-api/runs
LLM index: /llms.txt
Description: Every agent turn is a run: start it in the background, poll or stream it, resume a dropped stream, retry safely with Idempotency-Key, and cancel it.
Related: agent-api, agent-api/quickstart, agent-api/streaming, agent-api/usage, agent-api/errors

# Runs, background mode and retries

Every message you send starts a **run**: one turn of the agent on one conversation. The run keeps going on the server whether or not your client stays connected, and it is stored with an id you can read, stream, and cancel. The same runs back the website, the [Health Chat API](/developers/docs/health-api), and the MCP `research_chat` and `health_chat` tools.

Each response that starts a run carries its id in the `X-Run-Id` header and, for JSON responses, as `run_id`.

## Run in the background

A deep-research turn can take minutes. Instead of holding a connection open, set `background: true`:

```bash
curl https://scholarxiv.com/api/v1/chat \
  -H "Authorization: Bearer sxv_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f0c2a52-7d1e-4a43-9d0e-0b6a3f1c2e11" \
  -d '{"message":"Survey the evidence on GLP-1 agonists and cognition","deep_research":true,"background":true}'
```

The response is `202 Accepted` with the run object and a `Location` header pointing at it:

```json
{
	"object": "agent.run",
	"run_id": "6f1d3c9e-5b2a-4c1e-9a77-2f0d8e4b1c55",
	"chat_id": "65a000000000000000000001",
	"surface": "research",
	"status": "running",
	"background": true,
	"links": {
		"self": "/api/v1/runs/6f1d3c9e-5b2a-4c1e-9a77-2f0d8e4b1c55",
		"stream": "/api/v1/runs/6f1d3c9e-5b2a-4c1e-9a77-2f0d8e4b1c55/stream",
		"cancel": "/api/v1/runs/6f1d3c9e-5b2a-4c1e-9a77-2f0d8e4b1c55/cancel",
		"chat": "/api/v1/chat/65a000000000000000000001"
	}
}
```

Then either poll the run or stream it. `background` and `stream` cannot both be `true`; start in the background and open the run's stream if you want live events.

## Read a run

`GET /api/v1/runs/{run_id}` returns the run. Poll every few seconds until `status` is no longer `queued` or `running`.

| Field                                                    | Meaning                                                                                                                                                                                                   |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                                                 | `queued`, `running`, `completed`, `failed`, or `cancelled`.                                                                                                                                               |
| `result`                                                 | Once `completed`: `text`, `model`, `finish_reason`, `message_id`, the saved assistant `message`, and `output` when you asked for [structured output](/developers/docs/agent-api/tools#structured-output). |
| `error`                                                  | For `failed` (and sometimes `cancelled`) runs: `{ "code", "message" }`. `run_interrupted` means the turn stopped before it finished; read the conversation for what was saved.                            |
| `metrics`                                                | Tokens, tool calls, duration and estimated cost once the run ended. See [Usage](/developers/docs/agent-api/usage).                                                                                        |
| `events`                                                 | How many stream events the run has logged so far.                                                                                                                                                         |
| `metadata`                                               | The `metadata` you sent with the message.                                                                                                                                                                 |
| `created_at`, `started_at`, `updated_at`, `completed_at` | Timestamps. A running run's `updated_at` moves every second while it is alive.                                                                                                                            |

`GET /api/v1/chats/{chatid}/runs` lists the recent runs of one conversation, newest first (`?limit=` up to 50).

Reading, streaming, listing, and cancelling runs do **not** count against your hourly request limit.

## Stream a run, and resume a dropped stream

`GET /api/v1/runs/{run_id}/stream` returns the run's [event stream](/developers/docs/agent-api/streaming) from the beginning, then follows it live until the run ends. A finished run replays in full.

Every event carries its index as the SSE `id`:

```text
id: 0
data: {"type":"start","messageId":"...","messageMetadata":{"chatID":"..."}}

id: 1
data: {"type":"text-start","id":"text-1"}
```

If the connection drops, reconnect with the last id you received, either as the standard `Last-Event-ID` header or as `?after=`:

```bash
curl -N https://scholarxiv.com/api/v1/runs/6f1d3c9e-5b2a-4c1e-9a77-2f0d8e4b1c55/stream \
  -H "Authorization: Bearer sxv_your_key_here" \
  -H "Last-Event-ID: 41"
```

The stream continues at event 42, with nothing repeated and nothing skipped. The ids on a `POST /api/v1/chat` stream with `stream: true` are the same indexes, so a client that loses a POST stream resumes it the same way with the `X-Run-Id` it already received.

## Retry safely with Idempotency-Key

Send an `Idempotency-Key` header (1–255 visible ASCII characters, for example a UUID) on `POST /api/v1/chat` or `POST /api/v1/health/chat`. If the request is retried with the same key:

- You get the **same run** instead of a second turn. Nothing is charged or stored twice. The response has `Idempotent-Replayed: true`.
- A JSON request waits for that run and returns its result. A streaming request replays its events from the start. A background request returns the run.
- A retry may change `stream` and `background`. Changing anything else (for example the message) returns `422 idempotency_key_reused`.
- If the first request is still starting, the retry waits briefly and may return `409 idempotency_in_progress`; retry again after a moment.
- If the first request failed before the agent started (for example a `403` for a model your plan cannot use), the key is released and the retry runs normally.

Keys are remembered for 24 hours. Without a key, a POST that you did not see answered may already be running: check `active_run` on the conversation before you send it again.

## One turn per conversation

A conversation runs one turn at a time. Sending a message while another turn on the same `chat_id` is still running returns `409 run_in_progress` with that turn's `run_id`. Wait for it, stream it, or cancel it first.

`GET /api/v1/chat/{chatid}` (and `GET /api/v1/chats/{chatid}`) include `active_run`: the running turn, or `null`. A turn whose server process died is closed out automatically after about 15 seconds, so the conversation never stays locked.

## Cancel a run

`POST /api/v1/runs/{run_id}/cancel` stops the agent. It returns `{ "cancelled": true, "run": {...} }`, or `cancelled: false` with the final run if it had already ended. What the agent produced before it stopped stays in the conversation.

`POST /api/v1/chats/{chatid}/cancel` still cancels whatever is running on a conversation.

## Retention

Run records, their event logs, and Idempotency-Keys are kept for 24 hours after the run's last update. The conversation itself, including every finished answer and its tool evidence, is kept for as long as the chat exists. Usage rows are kept for 400 days.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
