# Runner minutes and cost by group (/api/usage/get-usage)

`GET /api/usage`

Runner-minute and cost attribution over a time window, grouped by label/workflow/job/repo/day. Scoped to the caller's orgs.

Base URL: `https://runners.starsling.dev`

## Authentication

Requires a bearer token (`Authorization: Bearer`).

## Parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `org` | query | string | yes | GitHub organization login. Required — usage is always org-scoped. e.g. `acme` |
| `repo` | query | string | no | Narrow to one repository, as owner/name. e.g. `acme/api` |
| `group_by` | query | string | no | Attribution axis — what each row is a total for. Defaults to repo. `label`, `workflow`, `job`, `repo`, `day`; e.g. `repo` |
| `order_by` | query | string | no | Column the rows are sorted by. Defaults to cost. `cost`, `minutes`, `jobs`, `key`; e.g. `cost` |
| `order` | query | string | no | Sort direction. Defaults to desc — the biggest spender first. `asc`, `desc`; e.g. `desc` |
| `window` | query | string | no | Relative lookback, e.g. 30d. Mutually exclusive with month and from/to. e.g. `30d` |
| `month` | query | string | no | A whole calendar month, YYYY-MM. e.g. `2026-06` |
| `from` | query | string | no | Start of the range, inclusive. Required with `to`. e.g. `2026-06-01` |
| `to` | query | string | no | End of the range, exclusive. Required with `from`. e.g. `2026-07-01` |

## Responses

| Status | Description |
| --- | --- |
| `200` | Attributed runner minutes and cost for the resolved window, plus the plan the window came from. |
| `400` | The window flags conflict or don't parse. Omit them all for the default window, or give exactly one of `window`, `month`, or `from`+`to`. |
| `401` | No credential, or a GitHub grant too old to read org membership — re-run `sling login`. |
| `403` | You are not a member of the org named in `?org=`, or the API key lacks `usage:read`. |
| `422` | A query parameter failed schema validation — most often an unrecognised `group_by`, `order_by`, or `order` value. `details` names the offending field. |
| `500` | The request was valid; the control plane or a dependency it calls failed. |

### `200` body

- `group_by` — string, required. Attribution axis — what each row is a total for. Defaults to repo. `label`, `workflow`, `job`, `repo`, `day`; e.g. `repo`
- `window` — object, required
  - `from` — string, required. e.g. `2026-06-01T00:00:00.000Z`
  - `to` — string, required. e.g. `2026-07-01T00:00:00.000Z`
- `plan` — object, required
  - `status` — string, required. `paid`, `free`, `blocked`
  - `free_minutes_limit` — number. e.g. `2000`
  - `free_minutes_used` — number. e.g. `1840`
  - `blocked_reason` — string. e.g. `…free allowance is used up…`
  - `period_source` — string, required. `stripe`, `install_cycle`, `calendar_month`, `explicit`
- `rows` — array<object>, required
  - array of object
    - `key` — string, required. e.g. `acme/api`
    - `runner_minutes` — number, required. e.g. `1240.5`
    - `jobs` — number, required. e.g. `318`
    - `cost_usd` — number, required. e.g. `9.92`
    - `pct_of_total` — number, required. e.g. `42.7`

### Example

```json
{
  "group_by": "repo",
  "window": {
    "from": "2026-07-12T00:00:00.000Z",
    "to": "2026-08-12T00:00:00.000Z"
  },
  "plan": {
    "status": "free",
    "free_minutes_limit": 2000,
    "free_minutes_used": 1840,
    "period_source": "install_cycle"
  },
  "rows": [
    {
      "key": "acme/api",
      "runner_minutes": 1240.5,
      "jobs": 318,
      "cost_usd": 9.92,
      "pct_of_total": 71.3
    },
    {
      "key": "acme/web",
      "runner_minutes": 499.5,
      "jobs": 96,
      "cost_usd": 3.99,
      "pct_of_total": 28.7
    }
  ]
}
```