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

# Analytics

> Monitor usage and cost using the Simplismart Python SDK

Track GPU compute consumption and cost across plan types using the `client.get_usage_stats()` method.

## Cost & Usage

### `get_usage_stats`

Fetches time-series usage and cost data for a given plan type and time range.

```python theme={null}
from datetime import datetime, timedelta, timezone
from simplismart import Simplismart, UsageStatsParams
import json

client = Simplismart()

now = datetime.now(tz=timezone.utc)

stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="private",
        start_time=(now - timedelta(days=7)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
    )
)

print(json.dumps(stats, indent=2, default=str))
```

**Expected Output** — `get_usage_stats()` returns a single dict, not a bare list of items:

```json theme={null}
{
  "total_cost": "8.00000000000004",
  "currency": "usd",
  "items": [
    {
      "event_name": "2-X-YOUR-EVENT-NAME-HERE",
      "source": "pg-llama3p1-8b_369ff438-YOUR-DEPLOYMENT-ID-HERE",
      "total_cost": "8.00000000000004",
      "total_usage": "120",
      "currency": "usd",
      "unit_price": {
        "amount": "0.066666666666667",
        "currency": "usd"
      },
      "points": [
        {
          "timestamp": "2026-05-05T16:00:00Z",
          "usage": "8",
          "cost": "0.533333333333336",
          "event_count": 4
        }
      ]
    }
  ]
}
```

Use `total_cost` (top-level) for the overall total, and each item's `event_name` + `total_cost` for the breakdown by deployment (or by accelerator type with `group_by="accelerator"` — see below, including a `plan_type="reserved"`-specific breakdown that only shows up in that grouping).

#### `UsageStatsParams`

| Parameter              | Type                                    | Description                                                                                                                                                                       | Required |
| ---------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `plan_type`            | `PlanType`                              | Compute plan to query. See [Plan Types](#plan-types)                                                                                                                              | Yes      |
| `start_time`           | `str`                                   | Range start in ISO 8601 format (e.g. `2026-04-01T00:00:00+00:00`)                                                                                                                 | Yes      |
| `end_time`             | `str`                                   | Range end in ISO 8601 format                                                                                                                                                      | Yes      |
| `window_size`          | `WindowSize`                            | Aggregation bucket size. Options are: `MINUTE`, `15MIN`, `30MIN`, `HOUR`, `3HOUR`, `6HOUR`, `12HOUR`, `DAY`, `WEEK`                                                               | Yes      |
| `workspace_id`         | `str \| None`                           | Restrict to a specific workspace UUID. Uses the org default if omitted                                                                                                            | No       |
| `deployment_ids`       | `list[str] \| None`                     | Filter by deployment UUID(s). Only valid for `private`, `byoc`                                                                                                                    | No       |
| `deployment_slugs`     | `list[str] \| None`                     | Filter by deployment slug(s). Only valid for `private`, `byoc`                                                                                                                    | No       |
| `model_names`          | `list[str] \| None`                     | Filter by model name(s) (e.g. `DeepSeek-R1`). Only valid for `shared`                                                                                                             | No       |
| `training_job_ids`     | `list[str] \| None`                     | Filter by training job UUID(s). Only valid for `training`                                                                                                                         | No       |
| `training_job_names`   | `list[str] \| None`                     | Filter by training job name(s). Only valid for `training`                                                                                                                         | No       |
| `model_repo_ids`       | `list[str] \| None`                     | Filter by model repo UUID(s). Only valid for `compilation`                                                                                                                        | No       |
| `model_repo_names`     | `list[str] \| None`                     | Filter by model repo name(s). Only valid for `compilation`                                                                                                                        | No       |
| `include_all_statuses` | `bool`                                  | Include all deployment statuses (SUCCESS, STOPPED, DELETED, FAILED, etc.). Default: only SUCCESS and STOPPED. Not supported for `training`                                        | No       |
| `group_by`             | `"deployment" \| "accelerator" \| None` | `"accelerator"` pools items per GPU/CPU type instead of per deployment — the same toggle as the dashboard's Group By dropdown. Only valid for `plan_type` in `private`/`reserved` | No       |

<Info>
  List parameters accept one or more values (for example `["a", "b"]`). Passing a filter that is not valid for the selected `plan_type` raises a `ValidationError`.
</Info>

<Note>
  1. You can find `workspace-id` under Settings > Workspaces. Select your workspace and copy the workspace ID.

  <img src="https://mintcdn.com/simplismart-3f10d72e/FykmCSjILEeK8UbY/images/sdk/python/1.workspace-id.png?fit=max&auto=format&n=FykmCSjILEeK8UbY&q=85&s=50956cba497df570d353a2087597e7bb" alt="" width="3020" height="1719" data-path="images/sdk/python/1.workspace-id.png" />

  2. Go to Deployments and select a deployment to find the `deployment-id` and `deployment-slug`.

  <img src="https://mintcdn.com/simplismart-3f10d72e/FykmCSjILEeK8UbY/images/sdk/python/2-depl-id-slug.png?fit=max&auto=format&n=FykmCSjILEeK8UbY&q=85&s=382da7e4e09a9c1b01ffb3fe29043670" alt="" width="3017" height="1705" data-path="images/sdk/python/2-depl-id-slug.png" />
</Note>

#### Plan Types

| Value         | Description                                                                  |
| ------------- | ---------------------------------------------------------------------------- |
| `shared`      | [Shared endpoint](/inference/shared-endpoint) usage                          |
| `private`     | [Private/dedicated deployment](/inference/dedicated-endpoint) usage          |
| `byoc`        | [Bring Your Own Compute](/inference/bring-your-own-compute) deployment usage |
| `reserved`    | Reserved capacity usage                                                      |
| `training`    | [Training and fine-tuning](/training-suite/introduction) job usage           |
| `compilation` | [Model compilation](/model-suite/optimise-a-model) job usage                 |

### Examples

**Dedicated Deployment usage (`plan_type="private"`)**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="private",
        start_time=(now - timedelta(days=14)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
    )
)
```

**Shared endpoint (`plan_type="shared"`): hourly buckets for the last 48 hours, pin results to one `workspace_id`**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="shared",
        start_time=(now - timedelta(days=2)).isoformat(),
        end_time=now.isoformat(),
        window_size="HOUR",
        workspace_id="your-workspace-uuid",
    )
)
```

**Dedicated/BYOC: daily cost only for chosen deployments, pass `deployment_ids` (use `deployment_slugs` instead when you have slugs, not UUIDs)**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="private",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        deployment_ids=["uuid-1", "uuid-2"],
    )
)
```

**Shared endpoint: daily cost only for listed `model_names`**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="shared",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        model_names=["DeepSeek-R1", "Llama-3"],
    )
)
```

**Training (`plan_type="training"`): weekly buckets for the last 90 days, narrow to `training_job_names`**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="training",
        start_time=(now - timedelta(days=90)).isoformat(),
        end_time=now.isoformat(),
        window_size="WEEK",
        training_job_names=["finetune-llama-v1", "finetune-llama-v2"],
    )
)
```

**Compilation (`plan_type="compilation"`): daily cost for specific `model_repo_names`**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="compilation",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        model_repo_names=["my-llama-repo", "my-mistral-repo"],
    )
)
```

**BYOC: daily rollup for one deployment slug, including non-success deployment statuses (`include_all_statuses=True`)**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="byoc",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        deployment_slugs=["my-deploy"],
        include_all_statuses=True,
    )
)
```

**Reserved (`plan_type="reserved"`): daily cost plus the pooled reservation commitment breakdown**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="reserved",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        group_by="deployment",
    )
)

print(stats["total_cost"])  # real, commitment-adjusted account total
for entry in stats.get("reservation_commitment", []):
    print(entry["feature_id"], entry["name"])
    for day in entry["daily"]:
        print(" ", day["timestamp"], "overage:", day["overage_amount"], "true_up:", day["true_up_amount"])
```

For `plan_type="reserved"`, you always get this breakdown automatically — there's no flag to set. `total_cost` reflects what your org is actually billed under the reservation, and `items` includes every deployment covered by it, even ones that predate the reservation. `overage_amount` is usage billed above what you've committed to for that window; `true_up_amount` is committed capacity you paid for but didn't use that window.

With `group_by="accelerator"` instead, use `items[].points[].computed_overage_amount` / `computed_true_up_amount` per time bucket rather than `reservation_commitment`:

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="reserved",
        start_time=(now - timedelta(days=30)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        group_by="accelerator",
    )
)

print(stats["total_cost"])
for item in stats["items"]:
    print(item["event_name"])
    for point in item["points"]:
        print(" ", point["timestamp"], "overage:", point["computed_overage_amount"], "true_up:", point["computed_true_up_amount"])
```

**Grouping — deployment vs accelerator**

```python theme={null}
stats = client.get_usage_stats(
    UsageStatsParams(
        plan_type="private",
        start_time=(now - timedelta(days=7)).isoformat(),
        end_time=now.isoformat(),
        window_size="DAY",
        group_by="accelerator",
    )
)
```

`group_by="accelerator"` pools `items` per GPU/CPU type instead of per deployment. Only valid for `plan_type` in `private`/`reserved`. Each pooled item gains a `sources` key (a list of every deployment source string folded into it) in place of the single `source` value the per-deployment grouping has; `source` itself is `None` on these pooled items.

Each point in a pooled item also carries `computed_commitment_utilized_amount`, `computed_overage_amount`, and `computed_true_up_amount`:

```json theme={null}
{
  "timestamp": "2026-07-14T00:00:00Z",
  "usage": "21220.0",
  "cost": "1448.7226666666668",
  "event_count": 9401,
  "computed_commitment_utilized_amount": "115.104",
  "computed_overage_amount": "2.16",
  "computed_true_up_amount": "204.29866666666666"
}
```

* **`computed_commitment_utilized_amount`** — cost covered by capacity you've already committed to.
* **`computed_overage_amount`** — cost billed at on-demand rates because usage went above your committed quantity that window.
* **`computed_true_up_amount`** — committed capacity you paid for but didn't use that window.

**Which field to use, by grouping:**

| `group_by`      | Use                                                                                     |
| --------------- | --------------------------------------------------------------------------------------- |
| `"accelerator"` | `items[].points[].computed_overage_amount` / `computed_true_up_amount`, per time bucket |
| `"deployment"`  | `reservation_commitment[].daily[].overage_amount` / `true_up_amount`, account-wide      |

<Info>
  `group_by` just changes how `items` is organized — it's available for both `plan_type="private"` and `plan_type="reserved"`, with the same two values (`"deployment"` or `"accelerator"`) either way. The reservation breakdown described above only ever comes with `plan_type="reserved"` — a `plan_type="private"` request, even with `group_by="accelerator"`, never includes `reservation_commitment`. For `plan_type="reserved"`, that breakdown is always included and always covers your whole account, regardless of which `group_by` you pick.
</Info>

***

## Error Handling

The SDK raises `SimplismartError` for API errors. Pydantic validates `plan_type` and `window_size` before the request is sent, so invalid values are caught locally.

```python theme={null}
from simplismart import Simplismart, UsageStatsParams
from simplismart.exceptions import SimplismartError

client = Simplismart()

try:
    stats = client.get_usage_stats(
        UsageStatsParams(
            plan_type="private",
            start_time="2026-04-01T00:00:00+00:00",
            end_time="2026-04-30T23:59:59+00:00",
            window_size="DAY",
        )
    )
except SimplismartError as e:
    print("Status:", e.status_code)
    print("Message:", e)
    print("Payload:", e.payload)
```

#### SimplismartError Attributes

| Attribute     | Type   | Description                    |
| ------------- | ------ | ------------------------------ |
| `status_code` | `int`  | HTTP status code               |
| `payload`     | `dict` | Full error response payload    |
| `message`     | `str`  | Error message from the backend |
