Skip to content

Observability · For developers · MIT

Report spend from the usage API

Answer what an organisation spent, on which models and keys, and what failed, from the Lobstack usage API rather than by estimating.

Installing a skill

When an agent loads it

Use when producing a cost report, attributing spend to a model, key or agent, or investigating an hour of high latency or errors.

Inside the file

  • The summary, and the two fields that decide whether you may quote it
  • The four questions, and the group_by that answers each
  • Reading a bad hour
  • From a terminal
  • Two responses that are not errors
  • Do not

The file

v1.0.0
# In Lobstack: Skills › Add a skill, then paste the file or drop the folder
# In Claude Code:
mkdir -p .claude/skills/lobstack-usage-reporting
# paste SKILL.md into .claude/skills/lobstack-usage-reporting/SKILL.md
lobstack-usage-reporting/SKILL.md134 lines
---name: lobstack-usage-reportingdescription: "Answer what an organisation spent, on which models and keys, and what failed, from the Lobstack usage API rather than by estimating. Use when producing a cost report, attributing spend to a model, key or agent, or investigating an hour of high latency or errors."license: MITmetadata:  title: "Report spend from the usage API"  version: 1.0.0  author: lobstack  category: observability  tags: [observability, usage, reporting, cost]--- # Report spend from the usage API One endpoint answers every version of "what did we spend". Use it rather thansumming your own logs: the Console and the desktop client both read this, so areport built from it agrees with what the customer sees instead of quietlydisagreeing by a few percent. ```bashcurl -s "https://www.lobstack.ai/api/v1/usage?range=30d&group_by=model" \  -H "Authorization: Bearer $LOBSTACK_API_KEY" | jq``` The key needs the usage:read scope, or the response is a 403 saying exactlythat. A browser session also works, which is how the Console reads it. | param | values | default || --- | --- | --- || `range` | `7d` `14d` `30d` `90d` | `7d` || `group_by` | `day` `model` `key` `agent` | `day` || `agent_id` | restrict to one agent | — || `key_id` | restrict to one API key | — | ## The summary, and the two fields that decide whether you may quote it ```json{  "enabled": true,  "org_id": "…",  "authenticated_via": "api_key",  "range": "30d",  "group_by": "model",  "summary": {    "requests": 12480, "errors": 41, "error_rate": 0.0033,    "errors_by_class": { "provider": 33, "timeout": 6, "validation": 2 },    "prompt_tokens": 8104221, "completion_tokens": 1201884, "total_tokens": 9306105,    "cost_usd": 41.204118,    "unpriced_requests": 0,    "p50_latency_ms": 780, "p95_latency_ms": 2410, "p99_latency_ms": 5120,    "streamed": 9902  },  "groups": [ … ],  "truncated": false}``` `unpriced_requests` — rows the meter could not price. cost_usd sums anull as zero, which is the only arithmetic available and not the only truth: atotal built partly from unpriced rows is a floor, and a reader who is not toldhow many were unpriced will read it as exact. Non-zero on a recent window means anunknown model key is passing through uncosted right now — go find it withaudit-unmetered-model-calls. `truncated` — true when the row cap bound. Sums are then a floor as well.Narrow the range or the filters rather than publishing the number. Report both, always, in one sentence: *"$41.20 across 12,480 requests over 30days; 0 unpriced, not truncated."* That sentence is defensible. $41.20 on itsown is not. The latency figures are true percentiles over the raw values rather than bucketedapproximations, so p95 is the 95th percentile and can be compared acrossranges. ## The four questions, and the group_by that answers each ```bash# What is it costing us over time, and is that trending?curl -s ".../api/v1/usage?range=30d&group_by=day" -H "$AUTH" \  | jq -r '.groups[] | [.key, .requests, .cost_usd] | @tsv' # Which model is the bill? Almost always one, and almost always a surprise.curl -s ".../api/v1/usage?range=30d&group_by=model" -H "$AUTH" \  | jq -r '.groups | sort_by(-.cost_usd)[] | [.key, .requests, .cost_usd] | @tsv' # Which team or environment? One key per surface makes this answerable.curl -s ".../api/v1/usage?range=30d&group_by=key" -H "$AUTH" | jq '.groups' # Which agent? For a fleet, this is the per-unit economics.curl -s ".../api/v1/usage?range=7d&group_by=agent" -H "$AUTH" | jq '.groups'``` Mint one API key per surface — web, batch job, staging, each internal tool — andgroup_by=key becomes cost attribution for free. Retrofitting that after thefact is impossible: the rows are already written against one key. ## Reading a bad hour errors_by_class settles the only question that matters first: whose fault wasit. provider and timeout dominating means upstream. validationdominating means a deploy went out with a bad body — check what changed. authdominating means a key was rotated or revoked and something did not get the memo. Then pull the specific requests by id from the trace table, or reproduce with therequest id in hand. The class names and what each implies are inlobstack-gateway-errors. ## From a terminal ```bashnpx lobstack spend --days 7``` Same data, same arithmetic, no jq. Also needs usage:read.  ## Two responses that are not errors enabled: false with reason: "request_tracing_not_migrated" means the tracetable does not exist on that deployment yet. It is a 200 with a null summary,deliberately, so a dashboard renders "not available" rather than "$0.00". An empty groups array with a real summary means the range genuinely had notraffic. Do not conflate the two. ## Do not - Do not publish cost_usd without unpriced_requests and truncated.- Do not build a parallel cost table from your own logs. It will disagree with the  invoice, and the invoice will be right.- Do not compare this month's cost_usd with last month's without checking  whether the model mix moved. A cheaper month can be a worse month.

More for developers

5
  • Observability

    Audit a codebase for unmetered model calls

    Find model calls that bypass the gateway and therefore appear on no bill anyone reads, and detect the opposite fault of counting one call twice.

  • Cost

    Read a Lobstack receipt

    Read the receipt Lobstack returns on every model call — cost_usd, savings_usd, baseline_reason — and report cost from it instead of estimating.

  • Migration

    Migrate an SDK call to the Gateway

    Move existing OpenAI or Anthropic SDK code onto the Lobstack Gateway by changing the base URL and the key, and verify it actually routed.

  • Cost

    Pick a model tier, not a flagship

    Choose the cheapest model tier that can actually do the task instead of defaulting to a flagship, and verify the choice before spending on it.

  • Cost

    Keep an agent inside a budget

    Hold an agent, job or run inside a hard dollar budget using the per-call cost the Gateway returns, and handle the 402 when an allowance runs out.

All developer skills →