# Runs

> Start a run, wait for it or let it run in the background, and fetch the results as JSON or CSV.

A run is one execution of one tool with one input. You start it with `POST /api/v1/tools/{toolId}/runs`; the request body is the tool's input as JSON. The input fields of every tool are in the [tool reference](/docs/tools) and in the live catalog at `GET /api/v1/tools`.

## Sync runs (default) [#sync-runs-default]

Without extra parameters the request waits for the run and answers `200` with the run and its results:

```bash
curl -X POST "https://pullwire.dev/api/v1/tools/youtube-transcripts/runs" \
  -H "x-api-key: $PULLWIRE_API_KEY" -H "content-type: application/json" \
  -d '{"videos": ["jNQXAC9IVRw"]}'
```

A sync run gets **25 seconds**. If the tool is not done by then, it stops: you get the results it collected so far, they are charged and saved, and `run.error` says that the run stopped at the deadline. For bigger jobs use an async run.

## Async runs [#async-runs]

Add `?async=1` to start the run in the background. The request answers right away with `202` and the run:

```bash
curl -X POST "https://pullwire.dev/api/v1/tools/youtube-transcripts/runs?async=1" \
  -H "x-api-key: $PULLWIRE_API_KEY" -H "content-type: application/json" \
  -d '{"videos": ["jNQXAC9IVRw", "dQw4w9WgXcQ"]}'
```

An async run gets up to **50 seconds**. Check its status with the run ID until it is `succeeded` or `failed`:

```bash
curl "https://pullwire.dev/api/v1/runs/$RUN_ID" -H "x-api-key: $PULLWIRE_API_KEY"
```

## Run status [#run-status]

| Status | Meaning |
| --- | --- |
| `queued` | The run was created and is waiting to start (async runs). |
| `running` | Credits are reserved and the tool is working. |
| `succeeded` | The run finished. `resultsCount` can be 0 if nothing matched your input. If `error` is set, the results are partial: the run stopped early (for example at the deadline) or some items could not be fetched (for example `BLOCKED`). Everything in the results was charged and saved. |
| `failed` | The run could not start (for example not enough credits), crashed, or ended without a single result because the target site could not be reached (for example every request was `BLOCKED`). `error` says why. A run that simply found nothing is `succeeded` with 0 results, not `failed`. Results produced before a crash were charged and saved. |

Other useful fields on a run: `resultsCount`, `creditsUsed`, `durationMs`, `createdAt` and `updatedAt`.

## How many results [#how-many-results]

Most tools accept a `maxResults` field. If you leave it out, a run returns **at most 20 results**. You can ask for up to **200 results per run**; for more, start several runs. Credits are reserved for `maxResults` results before the run starts, so a high value needs enough balance even if fewer results come back (see [Credits](/docs/credits)).

## Fetch results [#fetch-results]

Results of a finished run stay available. Fetch them as JSON:

```bash
curl "https://pullwire.dev/api/v1/runs/$RUN_ID/results" -H "x-api-key: $PULLWIRE_API_KEY"
```

or as CSV, for a spreadsheet:

```bash
curl "https://pullwire.dev/api/v1/runs/$RUN_ID/results?format=csv" \
  -H "x-api-key: $PULLWIRE_API_KEY" -o results.csv
```

For an Excel file, use `?format=xlsx` instead (same headers, `-o results.xlsx`).

Large runs are also saved as ready-made files you can download for 30 days, see [Datasets](/docs/datasets).

You can only read runs of your own organization; any other run ID answers `404 NOT_FOUND`.

## When a sync run crashes [#when-a-sync-run-crashes]

If a sync run fails after it already produced results, the API answers `500 INTERNAL_ERROR` and includes the `runId` in the error body. The results produced before the crash were charged, and you can still fetch them with `GET /api/v1/runs/{runId}/results`.
