> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# List Evaluations

GET https://api.captain.dev/v3/evals

List your evaluations, newest first, scoped to the environment of the
API key you call with.

Each row carries the status, progress and per-configuration scorecards, so
a dashboard or agent can show where every evaluation stands without
fetching each one. Per-case results and stored answers stay on
GET /v3/evals/\{eval\_id}.

Page with `cursor` until `next_cursor` is null; there is no total count.
Evaluations still running are settled against their job before the page
returns, up to a few per page, so a finished run stops reading as running.

Reference: https://docs.captain.dev/reference/evals/list

## Request

### Query parameters

- `collection` (string, optional, nullable) — Only evaluations against this collection.
- `status` (string, optional, nullable) — Filter by status: `pending`, `running`, `completed`, `completed_with_errors` or `failed`.
- `limit` (integer, optional, default: 25) — Rows per page (1-100).
- `cursor` (string, optional, nullable) — Continue from a previous page's `next_cursor`.

## Response

### 200

Successful Response

- `count` (integer, required) — Rows on this page. There is no total; page until next_cursor is null.
- `evals` (list of EvalListRowV3, optional)
- `next_cursor` (string, optional, nullable) — Pass as `cursor` to continue. Null on the last page.

## Errors

### 400 Bad Request Error

Invalid status or cursor.

- `error` (string, required) — Error code, for example `INVALID_ID_PREFIX`. `HTTP_ERROR` when the route raised a plain message.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code. Present only on `HTTP_ERROR`.

### 401 Unauthorized Error

Missing or invalid API key.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

### 403 Forbidden Error

The API key does not have query permission.

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

### 422 Unprocessable Entity Error

Validation Error

- `error` (string, required) — `VALIDATION_ERROR`.
- `message` (string, required)
- `details` (list of V3EvalsGetResponsesContentApplicationJsonSchemaDetailsItems, required) — One entry per invalid field, with `type`, `loc` and `msg`.
- `path` (string, required)

## Types

### EvalListRowV3

One row of GET /v3/evals. The top-line numbers a list needs. Per-case results and stored answers stay on GET /v3/evals/\{eval\_id}, so a page of rows costs one query regardless of how many cases each eval ran.

- `cases` (integer, required)
- `cases_error` (integer, required)
- `collection_name` (string, required)
- `created_at` (string, required)
- `eval_id` (string, required)
- `progress` (EvalProgressV3, required)
- `status` (enum, required)
  - Allowed values: `pending`, `running`, `completed`, `completed_with_errors`, `failed`
- `units` (integer, required)
- `updated_at` (string, required)
- `billing` (EvalBillingV3, optional, nullable)
- `completed_at` (string, optional, nullable)
- `config_names` (list of string, optional)
- `environment` (string, optional, nullable)
- `error_code` (string, optional, nullable)
- `scorecards` (map from string to EvalScorecardV3, optional)
- `started_at` (string, optional, nullable)

### V3EvalsGetResponsesContentApplicationJsonSchemaDetailsItems

### EvalProgressV3

- `cases_error` (integer, required)
- `cases_total` (integer, required)
- `configs_failed` (integer, required)
- `configs_total` (integer, required)
- `elapsed_seconds` (integer, required)
- `percent` (integer, required)
- `units_completed` (integer, required)
- `units_error` (integer, required)
- `units_failed` (integer, required)
- `units_total` (integer, required)
- `eta_seconds` (integer, optional, nullable)
- `qps` (double, optional, nullable)

### EvalBillingV3

- `billable_units` (integer, required)
- `skipped_units` (integer, required)
- `failed_configs` (list of string, optional)

### EvalScorecardV3

- `chunk_count` (integer, required)
- `collection_name` (string, required)
- `document_count` (integer, required)
- `error` (integer, required)
- `explicit_fields` (list of string, required)
- `failed` (integer, required)
- `name` (string, required)
- `query` (ResolvedQueryConfigV3, required) — Every Query v3 field, defaults filled in, rerank always in object form.
- `question_set_size` (integer, required)
- `scored` (integer, required)
- `status` (enum, required)
  - Allowed values: `scored`, `error`
- `environment` (string, optional, nullable)
- `error_code` (string, optional, nullable)
- `error_message` (string, optional, nullable)
- `latency_p50_ms` (integer, optional, nullable)
- `latency_p95_ms` (integer, optional, nullable)
- `mrr` (double, optional, nullable)
- `ndcg_at_10` (double, optional, nullable)
- `recall_at_1` (double, optional, nullable)
- `recall_at_10` (double, optional, nullable)
- `recall_at_3` (double, optional, nullable)

### ResolvedQueryConfigV3

Every Query v3 field, defaults filled in, rerank always in object form.

- `limit` (integer, required)
- `rerank` (ResolvedRerankV3, required)
- `semantic_ratio` (double, required)
- `boost` (list of ResolvedQueryConfigV3BoostItems, optional)
- `exclude_chunk_types` (list of string, optional)
- `filter` (ResolvedQueryConfigV3Filter, optional, nullable)
- `include` (map from string to boolean, optional)
- `max_chunks_per_document` (integer, optional, nullable)
- `relation_direction` (string, optional, default: outgoing)
- `relation_types` (list of string, optional, nullable)

### ResolvedRerankV3

- `enabled` (boolean, required)
- `candidate_limit` (integer, optional, nullable)
- `model` (string, optional, nullable)

### ResolvedQueryConfigV3BoostItems

### ResolvedQueryConfigV3Filter

## Examples

**Response**

```json
{
  "count": 2,
  "evals": [
    {
      "cases": 3,
      "cases_error": 1,
      "collection_name": "customer_forms",
      "created_at": "2026-09-12T14:02:11.418Z",
      "eval_id": "eval_019b4c2f11d04c8e9a7b3d5c2e1840ff",
      "progress": {
        "cases_error": 1,
        "cases_total": 3,
        "configs_failed": 0,
        "configs_total": 3,
        "elapsed_seconds": 9,
        "percent": 100,
        "units_completed": 6,
        "units_error": 3,
        "units_failed": 0,
        "units_total": 9,
        "eta_seconds": 0,
        "qps": 2
      },
      "status": "completed_with_errors",
      "units": 9,
      "updated_at": "2026-09-12T14:02:22.640Z",
      "billing": {
        "billable_units": 6,
        "skipped_units": 3,
        "failed_configs": []
      },
      "completed_at": "2026-09-12T14:02:22.640Z",
      "config_names": [
        "baseline",
        "rerank",
        "rerank-deep"
      ],
      "environment": "development",
      "error_code": null,
      "scorecards": {
        "baseline": {
          "error": 1,
          "failed": 0,
          "question_set_size": 3,
          "scored": 2,
          "status": "scored",
          "latency_p50_ms": 412,
          "latency_p95_ms": 690,
          "mrr": 0.75,
          "ndcg_at_10": 0.88,
          "recall_at_1": 0.5,
          "recall_at_10": 1,
          "recall_at_3": 1
        }
      },
      "started_at": "2026-09-12T14:02:13.902Z"
    },
    {
      "cases": 500,
      "cases_error": 0,
      "collection_name": "customer_forms",
      "created_at": "2026-09-12T13:55:40.117Z",
      "eval_id": "eval_019b3a81c4e2748fb0d95e17c3aa62b1",
      "progress": {
        "cases_error": 0,
        "cases_total": 500,
        "configs_failed": 0,
        "configs_total": 2,
        "elapsed_seconds": 185,
        "percent": 41,
        "units_completed": 410,
        "units_error": 0,
        "units_failed": 0,
        "units_total": 1000,
        "eta_seconds": 260,
        "qps": 2.1
      },
      "status": "running",
      "units": 1000,
      "updated_at": "2026-09-12T13:58:46.220Z",
      "billing": null,
      "completed_at": null,
      "config_names": [
        "baseline",
        "rerank"
      ],
      "environment": "development",
      "error_code": null,
      "scorecards": {},
      "started_at": "2026-09-12T13:55:41.002Z"
    }
  ],
  "next_cursor": null
}
```

**SDK Code**

```python Python
import requests

BASE_URL = "https://api.captain.dev"
API_KEY = "your_api_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

response = requests.get(
    f"{BASE_URL}/v3/evals",
    headers=headers,
    params={
        "limit": 25,
    },
    timeout=30.0
)

if response.status_code == 200:
    data = response.json()
    for ev in data["evals"]:
        best = max(
            (c for c in ev["scorecards"].values() if c.get("recall_at_10") is not None),
            key=lambda c: c["recall_at_10"],
            default=None,
        )
        found = f"{best['recall_at_10']:.0%} found" if best else f"{ev['progress']['percent']}% complete"
        print(f"{ev['eval_id']}  {ev['status']:22s} {ev['collection_name']:20s} {found}")
    # Page until next_cursor is null
    print(f"next_cursor={data['next_cursor']}")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript
const BASE_URL = "https://api.captain.dev";
const API_KEY = "your_api_key";

type Scorecard = { recall_at_10: number | null };
type Eval = {
    eval_id: string;
    status: string;
    collection_name: string;
    scorecards: Record<string, Scorecard>;
    progress: { percent: number };
};

const query = new URLSearchParams({ limit: "25" });

const response = await fetch(`${BASE_URL}/v3/evals?${query}`, {
    headers: { Authorization: `Bearer ${API_KEY}` }
});

if (response.ok) {
    const data: { evals: Eval[]; next_cursor: string | null } = await response.json();
    for (const ev of data.evals) {
        const scored = Object.values(ev.scorecards)
            .filter((c): c is Scorecard & { recall_at_10: number } => c.recall_at_10 != null)
            .sort((a, b) => b.recall_at_10 - a.recall_at_10);
        const found = scored.length
            ? `${Math.round(scored[0].recall_at_10 * 100)}% found`
            : `${ev.progress.percent}% complete`;
        console.log(`${ev.eval_id}  ${ev.status}  ${ev.collection_name}  ${found}`);
    }
    // Page until next_cursor is null
    console.log(`next_cursor=${data.next_cursor}`);
} else {
    console.error(`Error: ${response.status}`);
}
```