> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Querying logs programmatically

> Read logs and log counts through the API, SDK, and CLI, and stream live logs with runtype tail

The **Logs** page reads the same data that the Runtype API exposes. Use the API, the TypeScript SDK, or the `runtype` CLI to search log entries from a script, count them for a report, or follow new entries in a terminal.

The following table lists what each interface does:

| Task               | API                  | SDK                      | CLI                  |
| ------------------ | -------------------- | ------------------------ | -------------------- |
| List log entries   | `GET /v1/logs`       | `client.logs.list()`     | `runtype logs list`  |
| Count log entries  | `GET /v1/logs/stats` | `client.logs.getStats()` | `runtype logs stats` |
| Stream new entries | Not available        | Not available            | `runtype tail`       |

## Before you begin

Set up the following before you query logs:

* An API key with at least one of the `FLOWS:READ`, `AGENTS:READ`, or `ANALYTICS:READ` scopes. `runtype tail` needs `ANALYTICS:READ`. For more information, see [Managing API keys](/user-guide/settings/managing-api-keys).
* For the SDK, install it with `npm install @runtypelabs/sdk`.
* For the CLI, install it with `npm install -g @runtypelabs/cli`, then run `runtype login`.

## List log entries

Send a `GET` request to `/v1/logs`. Without a time range, the request returns entries from the last 24 hours, newest first, up to 500 entries.

To list the errors from one agent over a fixed day, run one of the following:

#### cURL

**`cURL`**

```bash title="cURL"
curl "https://api.runtype.com/v1/logs?agentId=YOUR_AGENT_ID&level=error&from=2027-01-19T00:00:00Z&to=2027-01-20T00:00:00Z" \
  -H "Authorization: Bearer $RUNTYPE_API_KEY"
```

#### TypeScript SDK

**`TypeScript`**

```typescript title="TypeScript"
import { RuntypeClient } from '@runtypelabs/sdk'

const client = new RuntypeClient({ apiKey: process.env.RUNTYPE_API_KEY })

const response = await client.logs.list({
  agentId: 'YOUR_AGENT_ID',
  level: 'error',
  from: '2027-01-19T00:00:00Z',
  to: '2027-01-20T00:00:00Z',
})
console.log(response.data.entries)
```

#### CLI

**`CLI`**

```bash title="CLI"
runtype logs list --agent YOUR_AGENT_ID --level error \
  --from 2027-01-19T00:00:00Z --to 2027-01-20T00:00:00Z --json
```

Replace `YOUR_AGENT_ID` with the ID of the agent.

The response wraps its data in a `{ success, data, window }` envelope. The `data` object contains the following fields:

* `entries`: The log entries. Each entry has an `id`, `timestamp`, `level`, `type`, `status`, and `title`, plus the fields that apply to it, such as `flowName`, `agentName`, `toolName`, `durationMs`, `totalTokens`, `estimatedCost`, and `error`.
* `pagination`: The `cursor` for the next page and `hasMore`, which is `true` when more entries match.
* `degraded`: Present and `true` when part of the window couldn't be read, so some entries are missing.

The `window` object holds the `from` and `to` timestamps that the query used after defaults were applied. Check it when a query returns no entries.

Pass `--json` to `runtype logs list`. Without it, the CLI prints each entry's timestamp and level but not its title or type.

### Filter log entries

`GET /v1/logs` and `GET /v1/logs/stats` accept the same filters. The following table lists each filter:

| Query parameter  | CLI flag         | Value                                                                                                              |
| ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| `from`           | `--from`         | ISO 8601 start time. Defaults to 24 hours ago.                                                                     |
| `to`             | `--to`           | ISO 8601 end time. Defaults to now.                                                                                |
| `level`          | `--level`        | Comma-separated list of `debug`, `info`, `warn`, and `error`                                                       |
| `type`           | `--type`         | Comma-separated list of `flow`, `step`, `tool`, `batch`, `system`, `agent`, `model`, `schedule`, and `audit`       |
| `status`         | `--status`       | Comma-separated list of `completed`, `failed`, `unknown`, `running`, `queued`, and `cancelled`                     |
| `category`       | `--category`     | Comma-separated list of `execution`, `agent`, `tool`, `model`, `system`, `error`, `schedule`, `batch`, and `audit` |
| `flowId`         | `--flow`         | A flow ID                                                                                                          |
| `agentId`        | `--agent`        | An agent ID                                                                                                        |
| `executionId`    | `--execution`    | An execution ID, as it appears in an entry's `executionSessionId` field                                            |
| `surfaceId`      | `--surface`      | A surface ID                                                                                                       |
| `conversationId` | `--conversation` | A conversation ID                                                                                                  |
| `search`         | `--search`       | Text to match, ignoring case                                                                                       |

An entry matches a comma-separated list when it matches any value in the list, and it must match every filter that you pass. The API rejects an unknown value with a `400` response. The `type` and `status` values classify entries the same way as the **Type** and **Status** filters on the **Logs** page. An entry whose activity never finished is `unknown`, a finished entry at the `error` level is `failed`, and every other finished entry is `completed`. The values `running`, `queued`, and `cancelled` are accepted but match no entries.

`search` matches an entry's message, name, operation, tool name, and error class. For entries from the last few minutes, it matches only the message.

The `audit` type and category require an Enterprise plan. On other plans, a request that names `audit` with other values returns entries for the other values only. For more information, see [Audit trail](/user-guide/logs/audit-trail).

### Page through results

`GET /v1/logs` also accepts the following parameters, which `runtype logs list` exposes as flags of the same name:

* `limit`: The number of entries per page. The default is 500, and the maximum is 1000.
* `order`: `desc` for newest first, which is the default, or `asc` for oldest first.
* `cursor`: The `pagination.cursor` value from the previous response.

To read every page, repeat the request with the same filters and the returned `cursor` until `hasMore` is `false`. If a response contains `pagination.cursorReset: true`, the API couldn't use your cursor and returned the first page again. Stop paging and start over. Don't follow the cursor in that response.

## Count log entries

Send a `GET` request to `/v1/logs/stats` with the same filters to get counts instead of entries. The following command counts the errors from the last 24 hours:

**`cURL`**

```bash title="cURL"
curl "https://api.runtype.com/v1/logs/stats?level=error" \
  -H "Authorization: Bearer $RUNTYPE_API_KEY"
```

The CLI equivalent is `runtype logs stats --level error`. The `data` object in the response contains the following fields:

* `total`: The number of matching entries.
* `byLevel`, `byType`, `byStatus`, and `byCategory`: Counts keyed by each value.
* `histogram`: Time buckets, each with a `bucket` timestamp, a `count`, and an `errors` count.
* `coveredFrom`: The earliest time for which counts exist. Counts before this time aren't available.
* `degraded`: Present and `true` when the counts are partial.

Repeated identical requests can return a cached result for up to 30 seconds. A cached response sets `cachedAt` to the time it was cached. A fresh response sets `cachedAt` to `null`.

## Stream live logs

Run `runtype tail` to print new log events in your terminal as they happen. The command connects, prints `Connected to tail session` followed by a session ID, and then prints one line per event until you press `Ctrl+C`.

The following command streams warnings from one flow:

**`CLI`**

```bash title="CLI"
runtype tail --flow YOUR_FLOW_ID --level warn
```

Replace `YOUR_FLOW_ID` with the ID of the flow.

`runtype tail` accepts the following options:

* `--flow`, `--agent`, `--surface`, and `--execution`: Limit events to one flow, agent, surface, or execution ID.
* `--level`: Limit events to one level: `debug`, `info`, `warn`, or `error`.
* `--category`: Limit events to one category, such as `execution`, `agent`, `tool`, `model`, `system`, or `error`.
* `--json`: Print each event as one JSON object per line.
* `--no-color`: Print without color.

Each filter option takes one value. To follow several levels, run `runtype tail` without `--level` and filter the output.

If the stream drops events, `runtype tail` prints a warning with the number of dropped events and their time span. To read entries from that span, run `runtype logs list` with `--from` and `--to`. If the connection drops, the command reconnects. It stops after 10 failed attempts in a row.

## Troubleshoot queries

The following responses are the most common:

* **`400` with `Unknown query parameter`**: The request included a parameter that the API doesn't accept. The error lists the accepted parameters. For example, use `agentId`, not `agent_id`.
* **`400` with `Invalid level`, `Invalid type`, `Invalid status`, or `Invalid category`**: A filter value isn't in the allowed list. The error lists the valid values.
* **`402`**: The request asked only for `audit` entries, and the account isn't on an Enterprise plan.
* **`403`**: The API key has none of the required scopes.
* **`503` with `Log store temporarily unavailable`**: The API couldn't read any entries or counts. Retry the request after a short wait.

## Next steps

* [Working with logs](/user-guide/logs/working-with-logs): inspect, filter, and export logs on the **Logs** page
* [Improving your agents from production traces](/developer-guides/guides/improving-agents-from-production): find failing agent runs and turn them into eval cases
* [Audit trail](/user-guide/logs/audit-trail): review account activity in Logs
* [Authentication](/developer-guides/getting-started/authentication): set up API keys for the API, SDK, and CLI