> ## Documentation Index
> Fetch the complete documentation index at: https://docs.labelbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Track usage and cost

> Read exact model cost for a session, its subagents, or a whole session tree, and token usage for many sessions at once.

Every model call a session makes is recorded in a cost ledger with its exact cost in US dollars. Use these reads to see what one session cost, how cost splits across its subagents, and what a batch of sessions cost, and to tell a final figure from one that is still settling.

The TypeScript samples assume the `rl` client from [Client setup](/managed-agents/api#client-setup).

## Before you begin

* Any organization role that can read sessions can read their cost: user, developer, or admin. The billing role cannot see sessions. See [Organizations and roles](/managed-agents/organizations-and-roles) and [API keys](/managed-agents/api-keys).
* These figures report what model and tool use cost, for tracking and comparison. Credits are bought separately on the **Billing** screen; see [Billing](/managed-agents/billing).

## Choose a read

| Question                                                                                         | Operation                                                                        |
| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| What did this session, its subagents, or its whole tree cost, and which calls made up that cost? | `getSessionModelCosts`                                                           |
| Which subagent or teammate spent the most?                                                       | `listSessionModelCostNodes`                                                      |
| What did each session in a list cost?                                                            | `listSessionModelCosts`                                                          |
| How many events and tokens did each session in a list use?                                       | `listSessionUsage`                                                               |
| What did grading cost?                                                                           | `listSessionOutcomes`. See [Outcomes](/managed-agents/outcomes#cost-of-grading). |

## What the cost covers

Model cost covers:

* **Model calls**: every inference call made by the agent, its subagents and teammates, and grading passes.
* **Provider-side tools**: metered tools that run with the model call, such as a provider's web search.

Sandbox compute is not part of these reads.

## How cost rolls up

Sessions form a tree. A root session can delegate to subagents, run teammates, and start grading passes, and each of those is a child session with its own cost. Every cost read lets you pick a scope.

```mermaid theme={"theme":"css-variables"}
flowchart TD
  root["Root session: self 0.42"] --> research["Research subagent: self 0.31"]
  root --> grader["Grading pass: self 0.07"]
  research --> fetcher["Fetcher subagent: self 0.12"]
```

| Scope            | Includes                                | Root in the diagram | Research subagent in the diagram |
| ---------------- | --------------------------------------- | ------------------- | -------------------------------- |
| `self` (default) | The session's own calls.                | 0.42                | 0.31                             |
| `subtree`        | The session and every session under it. | 0.92                | 0.43                             |
| `tree`           | Every session under the same root.      | 0.92                | 0.92                             |

For a root session, `subtree` and `tree` are the same, and that total is what the whole run cost.

## Read one session's cost

`getSessionModelCosts` returns a summary for the scope you ask for, the `self`, `subtree`, and `tree` summaries side by side, and the individual calls (attempts) in that scope, oldest first.

<Tabs>
  <Tab title="Console">
    1. On the **Sessions** tab, open the session.
    2. Click **Model cost** in the session header.
    3. Pick a scope: **This session**, **Session and descendants**, or **Whole session tree**.
    4. Read the total, then the **Model cost by session** and **Model cost attempts** sections for the breakdown.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const costs = await rl.managedAgents.getSessionModelCosts({
      session_id: 'b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38',
      scope: 'tree',
      limit: 50,
    });
    console.log(costs.summary.model_cost_usd, costs.summary.completeness);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38/costs?scope=tree&limit=50' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

Some fields are omitted from this response.

```json theme={"theme":"css-variables"}
{
  "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
  "scope": "tree",
  "summary": {
    "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
    "scope": "tree",
    "model_cost_usd": "0.92",
    "completeness": "complete",
    "attempt_count": "37",
    "charge_count": "37",
    "adjustment_count": "0"
  },
  "summaries": {
    "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
    "self": { "scope": "self", "model_cost_usd": "0.42", "completeness": "complete" },
    "subtree": { "scope": "subtree", "model_cost_usd": "0.92", "completeness": "complete" },
    "tree": { "scope": "tree", "model_cost_usd": "0.92", "completeness": "complete" }
  },
  "attempts": [
    {
      "attempt_id": "3c8f1a2d-7e4b-4c90-a6d5-1b9e2f7c4a83",
      "root_session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "session_path": "/",
      "kind": "model",
      "state": "charged",
      "completeness": "complete",
      "created_at": "2026-09-17T10:15:04Z",
      "updated_at": "2026-09-17T10:15:09Z",
      "charge": {
        "charge_id": "3c8f1a2d-7e4b-4c90-a6d5-1b9e2f7c4a83",
        "completeness": "complete",
        "original_model_cost_usd": "0.018472",
        "model_cost_usd": "0.018472",
        "adjustments": [],
        "recorded_at": "2026-09-17T10:15:09Z"
      }
    }
  ],
  "next_page_token": "eyJvIjoiNTAifQ",
  "model_cost_snapshot_token": "<opaque snapshot token>",
  "read_timestamp": "2026-09-17T10:32:00Z"
}
```

| Parameter                   | Description                                                                                                                        |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `scope`                     | `self` (default), `subtree`, or `tree`.                                                                                            |
| `limit`                     | Attempts per page, 1 to 100. Defaults to 50.                                                                                       |
| `page_token`                | The `next_page_token` from the previous page. Send it with the same `scope` and `limit`.                                           |
| `model_cost_snapshot_token` | Pins the read to the moment of an earlier response. See [Page through a consistent snapshot](#page-through-a-consistent-snapshot). |

Each attempt is one call. `session_path` places it in the tree (`/` is the root). `state` is `prepared` or `dispatched` while the call is in flight, `charged` once its cost is recorded, `not_charged` when the call produced no charge, and `indeterminate` when it may have incurred a cost but no usable receipt came back. Each attempt shows its cost amounts; token counts and prices are withheld.

### Page through a consistent snapshot

The first page returns a `model_cost_snapshot_token` and a `read_timestamp`. Every later page read with its `next_page_token` sees the ledger exactly as it was at that moment, so pages never skip or repeat attempts while the session keeps running and the totals match the attempts you paged through.

* Page tokens only work with the same session, scope, limit, and snapshot that produced them.
* A snapshot token only works with the same session and scope.
* Snapshots are kept for 24 hours. After that, start again without a token.

## Find which session spent the most

`listSessionModelCostNodes` returns one entry per session in the scope, ordered by `session_path`, each with its `self`, `subtree`, and `tree` totals. Send `scope=tree` to list every session in the tree; the default, `self`, returns only the session you name. It takes the same `limit`, `page_token`, and `model_cost_snapshot_token` parameters. Pass the snapshot token from a `getSessionModelCosts` response to read both at the same moment.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const { session_nodes } = await rl.managedAgents.listSessionModelCostNodes({
      session_id: 'b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38',
      scope: 'tree',
    });
    const top = [...session_nodes].sort(
      (a, b) => Number(b.model_costs.self.model_cost_usd) - Number(a.model_costs.self.model_cost_usd),
    )[0];
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38/costs/sessions?scope=tree' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "session_nodes": [
    {
      "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "root_session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "session_path": "/",
      "model_costs": {
        "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
        "self": { "scope": "self", "model_cost_usd": "0.42", "completeness": "complete" },
        "subtree": { "scope": "subtree", "model_cost_usd": "0.92", "completeness": "complete" },
        "tree": { "scope": "tree", "model_cost_usd": "0.92", "completeness": "complete" }
      }
    }
  ],
  "model_cost_snapshot_token": "<opaque snapshot token>",
  "read_timestamp": "2026-09-17T10:32:00Z"
}
```

## Compare many sessions

Both batch reads take `session_ids`, a comma-separated list of up to 1000 ids.

* `listSessionModelCosts` returns `session_model_costs`: one entry per id, in request order, repeated if an id is repeated, each with `self`, `subtree`, and `tree` totals. If any id is unknown or outside your access, the whole request returns `404`.
* `listSessionUsage` returns `session_usage`: event count, token counts, and `cost_micros` (cost in millionths of a US dollar) for each session's own events. Children are not included. Unknown ids and sessions with no events are left out, so compare the number of rows with the number of ids you sent.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const ids = ['b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38', '0c6f2b9e-4d17-4a38-b5e2-9f1a3c7d8e06'];
    const { session_model_costs } = await rl.managedAgents.listSessionModelCosts({ session_ids: ids.join(',') });
    const { session_usage } = await rl.managedAgents.listSessionUsage({ session_ids: ids.join(',') });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/sessions/usage?session_ids=b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38,0c6f2b9e-4d17-4a38-b5e2-9f1a3c7d8e06' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "session_usage": [
    {
      "session_id": "b7e1c9a4-3d62-4f15-8a07-5c2e9f6d1b38",
      "event_count": 214,
      "input_tokens": 182340,
      "output_tokens": 9215,
      "cache_read_tokens": 140112,
      "cache_write_tokens": 21480,
      "cost_micros": 420000
    }
  ]
}
```

Use `listSessionModelCosts` for amounts you reconcile, because it covers child sessions and reports completeness. Use `listSessionUsage` for token counts.

## Know when a figure is final

Every summary and attempt has a `completeness`. A summary reports the weakest state in its scope, so it never claims more certainty than its least certain call.

| Completeness     | Meaning                                                                     |
| ---------------- | --------------------------------------------------------------------------- |
| `complete`       | Every call in scope is priced.                                              |
| `pending`        | A call is still in flight, or nothing is recorded yet. The total will grow. |
| `unpriced`       | A call has no matching price.                                               |
| `partial`        | Only part of the cost is known, or several states are mixed.                |
| `legacy_partial` | A charge was recorded under earlier accounting rules.                       |
| `indeterminate`  | A call may have incurred a cost, but no usable receipt came back.           |

A total can change in three ways:

1. **The session is still running.** New calls add to the total. Once the session has stopped, no new calls arrive unless you resume it.
2. **Calls are still settling.** `pending` becomes `charged` or `not_charged`.
3. **A charge is corrected.** Charges are never edited. A correction is added as an adjustment, so `model_cost_usd` is the current amount and `original_model_cost_usd` is the amount first recorded. `adjustment_count` tells you whether any correction applies in scope.

Treat a figure as settled when the session has stopped and `completeness` is `complete`, and record the `read_timestamp` next to it.

## Amount format

* All amounts are in US dollars. No other currency is used.
* `model_cost_usd` and every other `_usd` field is an exact decimal string with up to nine decimal places and no trailing zeros, such as `"0.18472"`. A zero amount is `"0"`.
* `attempt_count`, `charge_count`, and `adjustment_count` are integer strings.
* `cost_micros` in usage rows and grading passes is an integer in millionths of a dollar.

Add amounts with a decimal type, such as `Decimal` in Python or a decimal library in TypeScript. Floating-point sums drift.

## Organization analytics

To see cost, tokens, and tool calls across your whole organization over time, broken down by agent, model, and tool, use [Analytics](/managed-agents/analytics). To total a specific set of sessions, use the batch reads in [Compare many sessions](#compare-many-sessions).

## What can go wrong

| Symptom or code                                                                    | Cause                                                                                | Fix                                                                                                           |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `400 invalid_request` on `scope`                                                   | The scope is not `self`, `subtree`, or `tree`.                                       | Send one of the three, or omit it.                                                                            |
| `400 invalid_request` on `page_token`                                              | The token came from a different session, scope, limit, or snapshot.                  | Send the same parameters as the page that returned it.                                                        |
| `400 invalid_request` on `model_cost_snapshot_token`, "outside the retained range" | The snapshot is more than 24 hours old.                                              | Read again without a snapshot token.                                                                          |
| `400 invalid_request` on `session_ids`                                             | The list is missing or has more than 1000 ids.                                       | Split the list into batches of 1000.                                                                          |
| `404 not_found` on `session_ids` from `listSessionModelCosts`                      | One id is unknown or outside your access. The whole request failed.                  | Remove the id and resend.                                                                                     |
| `listSessionUsage` returns fewer rows than ids                                     | Unknown ids and sessions with no events are left out.                                | Match rows by `session_id`.                                                                                   |
| The root session's cost looks low                                                  | `self` is the default scope and excludes subagents and grading.                      | Send `scope=tree`.                                                                                            |
| Usage `cost_micros` and model cost differ                                          | Usage counts only the session's own events, in whole millionths of a dollar.         | Use `listSessionModelCosts` for reconciliation.                                                               |
| The total changed after you recorded it                                            | The session was still running, calls were still settling, or a correction was added. | Check `completeness` and `adjustment_count`. See [Know when a figure is final](#know-when-a-figure-is-final). |
| `404 not_found`                                                                    | The session does not exist or is outside your organization.                          | Check the id and your API key's organization.                                                                 |
| `429 rate_limit_exceeded`                                                          | Too many requests.                                                                   | Wait for the time in `Retry-After`, then retry.                                                               |

The full list of codes is in [Errors](/managed-agents/errors).

## Limits

| Limit                      | Value                |
| -------------------------- | -------------------- |
| Ids in one batch read      | 1000                 |
| Attempts or nodes per page | 1 to 100, default 50 |
| Snapshot lifetime          | 24 hours             |

Every other limit is on [Limits](/managed-agents/limits).

## Next steps

<CardGroup cols={2}>
  <Card title="Teams" href="/managed-agents/teams">
    See why a team costs more than one agent.
  </Card>

  <Card title="Outcomes" href="/managed-agents/outcomes">
    Read what each grading pass cost.
  </Card>

  <Card title="Multi-agent" href="/managed-agents/multi-agent">
    Understand the session trees behind cost rollups.
  </Card>

  <Card title="Limits" href="/managed-agents/limits">
    Check quotas and request limits.
  </Card>
</CardGroup>
