# Count runs by state

**GET** `/api/runs/counts`

Counts runs by state under the filters of List runs: in the range, open now, today, and by day.

Needs [an API token](https://docs.gorunner.io/api/authentication#scopes) with the **Read** permission for runs (`run:read`).

## Parameters

### Query

- `workflowId` · Optional · UUID: One workflow’s runs.
- `environment` · Optional · string: `production` (the default), `test` or `all`.
- `window` · Optional · string: `24h`, `7d`, `30d`, `90d` or `all` (the default). Not with `from` or `to`.
- `tz` · Optional · string: A time zone name, such as `Europe/Berlin`, for “today” and the days. Unknown or left out: UTC.
- `days` · Optional · boolean: `true` adds `days`: each local day’s counts, newest first, 92 at most.

**What are the rarely used parameters?**

- `triggerType` · Optional · string: How it began: `manual`, `webhook`, `cron`, `poll`, `error`, `run_finished`, `event`, `api`, `mcp` or `subflow`.
- `tag` · Optional · string: Runs with exactly this tag, added by the Tags app.
- `from` · Optional · time: Runs started at or after this time (RFC 3339).
- `to` · Optional · time: Runs started before this time. After `from`.
- `q` · Optional · string: Up to 200 characters. Finds runs whose ID starts with it, or whose workflow name or error has it, ignoring case.
- `reason` · Optional · string: Up to 200 characters. Runs whose error has it, ignoring case.
- `group` · Optional · string: A failure group’s `key`, from Group failed runs.

## Read it

`worked`, `failed` and `cancelled` count the range. `held`, `running` and `queued` count what is open now, however old.

`failedWorkedOnRetry` is the failed runs a retry made good; they still count in `failed`. `failedTests` appears only for production, counting failed test runs.

`retentionDays` is how long a finished run is kept, apart from each workflow’s newest finished run in each environment; 0 means forever. It takes no `state`, `status` or paging.

## Responses

- 200 OK

  The counts.

- 400 `VALIDATION_ERROR`

  A filter has a value it doesn’t take. The message names it.

- 403 `FORBIDDEN`

  The token lacks the **Read** permission for runs.

- 500 `INTERNAL_ERROR`

  The counts couldn’t be read. Try again.

Any call can also answer: [`401 UNAUTHORIZED`](https://docs.gorunner.io/errors#UNAUTHORIZED) · [`401 TOKEN_ENDED`](https://docs.gorunner.io/errors#TOKEN_ENDED) · [`403 TOKEN_SURFACE_OFF`](https://docs.gorunner.io/errors#TOKEN_SURFACE_OFF) · [`403 FEATURE_OFF`](https://docs.gorunner.io/errors#FEATURE_OFF) · [`403 EMBED_NOT_ALLOWED`](https://docs.gorunner.io/errors#EMBED_NOT_ALLOWED) · [`403 EMBED_NOT_AVAILABLE`](https://docs.gorunner.io/errors#EMBED_NOT_AVAILABLE) · [`403 ACCOUNT_SUSPENDED`](https://docs.gorunner.io/errors#ACCOUNT_SUSPENDED) · [`429 RATE_LIMITED`](https://docs.gorunner.io/errors#RATE_LIMITED) · [`503 BUSY`](https://docs.gorunner.io/errors#BUSY) · [`503 PAUSED`](https://docs.gorunner.io/errors#PAUSED) · [`503 SERVICE_UNAVAILABLE`](https://docs.gorunner.io/errors#SERVICE_UNAVAILABLE). See [Errors](https://docs.gorunner.io/api/errors#by-status).

Request

**curl**

```bash
GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…"  # needs Read for runs

URL="$GORUNNER/api/runs/counts?window=7d&tz=Europe/"
URL="${URL}Berlin"
curl "$URL" \
  -H "Authorization: Bearer $GORUNNER_TOKEN"
```

**JavaScript**

```js
const GORUNNER = "https://api.gorunner.io";
const TOKEN = process.env.GORUNNER_TOKEN;

const res = await fetch(
  `${GORUNNER}/api/runs/` +
    `counts?window=7d&tz=Europe/Berlin`,
  {
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests

GORUNNER = "https://api.gorunner.io"
TOKEN = os.environ["GORUNNER_TOKEN"]

res = requests.get(
    f"{GORUNNER}/api/runs/counts",
    params={
      "window": "7d",
      "tz": "Europe/Berlin",
    },
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]
```

Response · 200 OK

```json
{
  "data": {
    "all": 120,
    "worked": 108,
    "failed": 10,
    "cancelled": 2,
    "held": 1,
    "running": 2,
    "queued": 0,
    "failedWorkedOnRetry": 4,
    "failedTests": 3,
    "today": {
      "all": 15,
      "worked": 14,
      "failed": 1,
      "cancelled": 0,
      "from": "2026-10-25T00:00:00+02:00"
    },
    "latest": {
      "id": "8f0c41d2-6b7e-4c1a-9d3f-2a5b7c9e1f04",
      "workflowId": "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
      "workflowName": "Order intake",
      "startedAt": "2026-10-25T08:59:58Z",
      "state": "worked"
    },
    "range": {
      "from": "2026-10-18T11:00:00Z",
      "to": null
    },
    "retentionDays": 30,
    "tz": "Europe/Berlin"
  }
}
```

**Every field**

It takes no body.

### Response · 200 OK

- `data` · object: The counts read: every figure the Runs page shows, under the list’s filters.
  - `all` · integer: Runs that started in the range, every state.
  - `worked` · integer: In the range.
  - `failed` · integer: In the range.
  - `cancelled` · integer: In the range.
  - `held` · integer: Held on a person now, whatever their age.
  - `running` · integer: Running now (a Delay included), whatever their age.
  - `queued` · integer: Waiting in the run line now, whatever their age.
  - `failedWorkedOnRetry` · integer: Of `failed`, those a retry made good (they still count in `failed`).
  - `failedTests` · integer: Only when `environment` is production: failed test runs in the range under the same filters.
  - `today` · object: Runs that started since local midnight in `tz`, whatever the range.
    - `all` · integer
    - `worked` · integer
    - `failed` · integer
    - `cancelled` · integer
    - `from` · time
  - `latest` · object or null: The newest run under the filters, in the range or open.
    - `id` · UUID
    - `workflowId` · UUID
    - `workflowName` · string
    - `startedAt` · time
    - `state` · string: One of `failed`, `held`, `running`, `queued`, `worked`, `cancelled`.
  - `range` · object
    - `from` · time or null
    - `to` · time or null
  - `retentionDays` · integer: RUN_RETENTION_DAYS: 0 = runs are kept for ever. Past it a finished run is deleted, except each workflow’s newest finished run per environment.
  - `tz` · string: The zone the days were counted in (UTC when absent or unknown).
  - `days` · array of objects: Only with `days=true`: each local day with runs in the range (days without runs are left out), newest first, at most 92, counting that day’s runs in the range: the range’s first and last days are often a part of a day (a 7-day window starts at this time 7 days ago), and count that part only; `from` is the day’s own start.
    - `all` · integer
    - `worked` · integer
    - `failed` · integer
    - `cancelled` · integer
    - `day` · string
    - `from` · time
    - `held` · integer
    - `running` · integer
    - `queued` · integer

Related: [List runs](https://docs.gorunner.io/api/runs/list-runs) · [Summarize a range of runs](https://docs.gorunner.io/api/stats/summarize-run-range)

Source: https://docs.gorunner.io/api/stats/count-runs
