---
title: "Get the signed-in user's own per-turn µ spend (D5 per-user visibility)"
method: GET
path: "/billing/usage/per-turn"
tags: ["Billing"]
---

# Get the signed-in user's own per-turn µ spend (D5 per-user visibility)

`GET /billing/usage/per-turn`

## Request

- Base URL: `https://api.mnemom.ai/v1`
- URL: `https://api.mnemom.ai/v1/billing/usage/per-turn`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `mnemom_session`

Returns the CURRENT user's own per-turn spend, self-scoped by session `sub` only — there is no person_id parameter and a user can never read another person's turns. Each turn's µ value is a RATE-CARD ESTIMATE derived from token counts (usage_events stores no cost), NOT the authoritative account-scoped ledger burn — the response carries `basis: "rate_card_estimate"` and every µ field is named `estimated_*`. µ is the display unit (1 µ = 1¢ = 1000 mmu). Feature-gated by `USAGE_SELF_SPEND_API_ENABLED`; returns 404 to every caller (before auth) while the flag is off.

## Query parameters

- `days` 7 | 30 | 90
- `limit` integer
- `cursor` string
- `product` 'research' | 'design' | 'develop' | 'operate' | 'canon' | 'unattributed' | 'all'

## Response `200`

The user's own per-turn spend (estimated) for the window.

- object
  - `from` string, date-time, required
  - `to` string, date-time, required
  - `basis` 'rate_card_estimate', required: Always `rate_card_estimate` — the µ figures are derived from token counts, not the settled ledger burn.
  - `data` object[], required
    - `id` string, required: Turn id.
    - `occurred_at` string, date-time, required
    - `provider` string, required
    - `model` string, required
    - `tokens_in` string, required: 64-bit-safe string.
    - `tokens_out` string, required: 64-bit-safe string.
    - `estimated_cost_usd` number, required
    - `estimated_mmu` string, required: Estimated spend in integer mmu (1 µ = 1000 mmu).
    - `estimated_mu` string, required: Estimated spend as a µ decimal string.
  - `totals` object, required
    - `tokens_in` string, required
    - `tokens_out` string, required
    - `turn_count` string, required
    - `estimated_cost_usd` number, required
    - `estimated_mmu` string, required
    - `estimated_mu` string, required
    - `totals_unavailable` boolean, required: True if the window-totals aggregation failed (page still valid).
  - `next_cursor` string, nullable, required

## Other responses

- `400`: Bad request
- `401`: Authentication required
- `404`: Resource not found
- `422`: Request was well-formed but semantically invalid (e.g., business-rule validation failure, malformed YAML inside a JSON payload).
- `429`: Rate-limit exceeded. The global per-IP limiter (100 requests/minute, applied to every `/v1/*` route) rejected this request. Back off until the window resets — `Retry-After` carries the cooldown in seconds and `X-RateLimit-Reset` the absolute reset time.
- `500`: Server error — request was well-formed but the server failed to fulfill it. Typically a downstream dependency (DB / RPC / external API) returned an unexpected error.

---

[API](https://skmtc.dev/mnemom/apis/mnemom-api.md) · [All operations](https://skmtc.dev/mnemom/apis/mnemom-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mnemom/apis/mnemom-api/revisions/274b7a6ea182?raw)
