costs
Grouped cost breakdowns, paginated events, uncapped totals, raw and aggregated export, and the analytics subcommands — baseline, unit economics, drift, priced models, and simulation
costs is a command group. Run it bare for a grouped breakdown of a project's spend over a window, or use a subcommand for a specific view:
| Command | Purpose |
|---|---|
solwyn costs | Aggregate spend grouped by one dimension. Top 100 groups. |
solwyn costs events | Page through individual cost events. Production and above. |
solwyn costs total | The uncapped total for the window, for reconciling a truncated breakdown. |
solwyn costs export | Stream CSV or NDJSON to stdout. |
solwyn costs baseline | The daily-spend baseline anomaly rules arm against. |
solwyn costs unit-economics | Cost per request, per run, and tokens per dollar, with a comparison window. |
solwyn costs drift | Stored actual cost versus the SDK's pre-flight estimates. |
solwyn costs priced-models | Every model the pricing catalog can bill. |
solwyn costs simulate | Replay the window's usage against one current priced model. |
Every subcommand accepts either credential and requires a project, resolved from --project, SOLWYN_PROJECT_ID, or default_project in your config file. The one exception is costs --account, which rolls up every project and requires a user login instead.
All of these commands move quantities and selectors only: dollars, token counts, model and provider names, your own tag labels. No field in any response can carry a prompt or a completion. See Privacy.
Shared filters
costs, events, total, and export take the same filter set. Filters go after the subcommand — solwyn costs --model gpt-5 total is a usage error with a hint to reorder.
| Option | Type | Purpose |
|---|---|---|
--range | string | Preset window. Default 7d (30d for export). Allowed presets depend on your plan and are validated by the API. |
--from, --to | ISO 8601 with timezone | An explicit window. Both required together; --to is exclusive and must be later. Replaces --range. |
--model | string | Filter to one model. |
--provider | string | Filter to one provider. |
--modality | string | text, image, audio, video, or embedding. |
--status | string | success, error, or budget_denied. |
--service-tier | string | Provider service tier, for example flex or priority. |
--provider-region | string | Serving region. |
--agent-run | string | A run id (run_ plus 32 hex characters) or a run name. |
--failover-reason | string | circuit_open, primary_error, model_fallback, or cost_routed. |
--model-fallback / --no-model-fallback | flag pair | Only calls that were (or were not) served by a fallback model. |
--provider-fallback / --no-provider-fallback | flag pair | Only calls that were (or were not) served by a fallback provider. |
--tag | key=value, repeatable | Filter by an attribution tag. Team and above. Split on the first =, so values may contain =; duplicate keys are rejected. |
Window and tag validation happens locally, before any request. A bad value exits 2 with invalid_cost_query and no network call.
When the API narrows your window to your plan's retention, a note goes to stderr in both output formats:
Retention clamped window: 2026-07-02T00:00:00Z to 2026-07-31T00:00:00Z (requested 2026-07-01T00:00:00Z to 2026-07-31T00:00:00Z).The JSON payloads carry the same information as a window object with requested_from, requested_to, effective_from, effective_to, and retention_clamped.
costs
Aggregate spend for the window, grouped by one dimension.
solwyn --project <project_id> costs [--group-by <dimension>] [--range <preset>] [filters...]
solwyn costs --account [--group-by project|<dimension>] [--range <preset>]| Option | Type | Default | Purpose |
|---|---|---|---|
--group-by | string | model (project with --account) | A dimension from your plan's billing capabilities, or tag:<key> on Team and above. |
--account | boolean | false | Roll up every project in the account. Requires JWT. |
| Contract | |
|---|---|
| Auth | any; --account requires JWT |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.v1; solwyn.account_costs.v1 with --account |
Dimensions in use today include day, model, provider, status, service_tier, provider_region, failover_reason, agent_run, and tag:<key>. The CLI does not enumerate them — your plan does. When the API refuses a grouping or range, the CLI appends your plan's allowed values to the error's hint and exits 2.
$ solwyn --project proj_0123456789abcdef01234567 costs --range 30d --group-by model
Group Provider Cost Input Output Calls Unpriced Local
gpt-5-mini openai 2.500000 2400 680 8 0 2| Column | Meaning |
|---|---|
| Group | The grouping key. — when the API returns none. |
| Provider | Provider for the group. - when the group spans providers, for example when grouping by day. |
| Cost | Priced total in USD. |
| Input / Output | Total input and output tokens. |
| Calls | Number of calls in the group. |
| Unpriced | Calls recorded with no observable billable quantity — never a fabricated $0. |
| Local | Calls to zero-cost local providers (ollama, vllm, lmstudio), recorded for visibility. |
| Run name | Appears only when grouping by agent_run and a run carries a name. |
Two more notes can appear on stderr: showing top 100 groups when the breakdown is truncated, and Savings: $…; upper …; blocked calls N. when hard-deny blocked spend in the window. Use costs total for the untruncated figure.
The Unpriced and Local lanes exist because Solwyn records only quantities and selectors — a call whose billable quantity is unobservable is shown, not invented. See Surface coverage.
JSON output
{
"schema": "solwyn.costs.v1",
"project_id": "proj_0123456789abcdef01234567",
"range": "7d",
"group_by": "model",
"groups": [
{
"group_key": "gpt-5-mini", "provider": "openai", "modality": null, "agent_run_name": null,
"total_cost": 2.5, "total_input_tokens": 2400, "total_output_tokens": 680,
"call_count": 8, "unpriced_call_count": 0, "local_call_count": 2
}
],
"truncated": false,
"window": {
"requested_from": "2026-07-09T00:00:00Z", "requested_to": "2026-07-16T00:00:00Z",
"effective_from": "2026-07-09T00:00:00Z", "effective_to": "2026-07-16T00:00:00Z",
"retention_clamped": false
}
}Pretty-printed here; the CLI emits one line. truncated and window are always present; savings appears only when non-null.
Account rollup
--account groups by project by default and draws an eight-level sparkline of daily spend. Only --model, --provider, --modality, --group-by, and window filters are accepted; the others exit 2.
$ solwyn costs --account
Project Window cost Current period Calls Daily
Alpha 2.500000 3.000000 8 ▃█On a plan without analytics, the table collapses to Project and Current period, and the JSON sets analytics_withheld: true rather than emitting zeros.
Budget and quota periods use server receive time (received_at). Analytics use the event's stored timestamp: a backdated batch counts against the applicable budget/quota period when received, while appearing on its original analytics day within Core's clock and retention bounds. Future skew can be clamped; a configured past horizon can clamp older timestamps. Backdating cannot bypass current-period limits.
costs events
Page through individual cost events. Production and above.
solwyn --project <project_id> costs events [--limit 1-100] [--cursor <c>] [--all] [filters...]| Option | Type | Default | Purpose |
|---|---|---|---|
--limit | integer 1–100 | 25 | Events per page. |
--cursor | string | none | Continue from a previous page. |
--all | boolean | false | Follow all remaining pages from the optional starting cursor; --limit remains per-page. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.events.v1 |
The table shows Time, Model, Provider, Cost, Denied cost, Deny source, Deny reason, SDK instance, and Tags. An unpriced event shows — for cost. When more pages exist, the table view prints the next cursor to stderr; in JSON it is the next_cursor field alongside has_more.
Each JSON item carries ids, provider and model, modality, region and service tier, ten token counters, cost and denied cost, price flags, a media breakdown for image, audio, and video calls, your tags, status, latency, the SDK instance id, and a timestamp. Event rows deliberately omit call_id, the provider-fallback flag, and the failover reason — use a raw export for those.
With --all, filters persist and rows remain in Core order; exhausted JSON has next_cursor=null, has_more=false. Repeated cursors, inconsistent continuation, malformed responses and later-page failures produce a nonzero exit with no partial success stdout.
Events require Production except the first-page onboarding probe (--limit 1 without a cursor). On a pre-Production plan, costs events --limit 1 --all can pass that probe and receive 402 on a cursor page. The command exits 1 and emits no partial stdout; first-page success is not permission to fetch all events.
costs total
The server-authoritative total for the window, not capped to the top 100 groups.
$ solwyn --project proj_0123456789abcdef01234567 costs total --range 30d
Cost Input Output Calls Unpriced Local
2.500000 2400 680 8 0 2| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.total.v1 |
Takes the shared filters and nothing else. The JSON payload adds project_id, range, window, and savings when present.
costs export
Stream the window as CSV or newline-delimited JSON to stdout.
solwyn --project <project_id> costs export [--format csv|json] [--group-by <dimension>] [--range <preset>] [filters...] > costs.csv| Option | Type | Default | Purpose |
|---|---|---|---|
--format | csv | json | csv | json is NDJSON, one object per line. |
--group-by | string | unset | Omit for a raw per-event export (Team and above). Supply a dimension for an aggregated export (Production and above). |
--range | string | 30d | Note the different default. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | stream (CSV or NDJSON), not an envelope |
The response bytes are copied to stdout unchanged. There is no output-file flag — redirect. --json does not wrap the stream. Requested/effective window and retention headers, plus X-Solwyn-Export-Snapshot, are echoed on stderr when supplied. Keep streams separate and retain that metadata with the export.
Raw exports are capped at 25,000 events or 32 MiB; aggregate exports at 10,000 pricing subgroups or 32 MiB. Oversized exports return 422/exit 2 with no partial result, rather than a silently truncated file. Narrow the window. A connection failure after streaming begins can still leave incomplete bytes and is never replayed automatically. Write to a temporary file and rename only on success, preserving CSV formula-safety prefixes.
costs baseline
The trailing daily-spend baseline that daily-anomaly alert rules arm against.
$ solwyn --project proj_0123456789abcdef01234567 costs baseline --days 7
Lookback Days with data Mean / day Stdev / day Armed Freshness
7 6 1.250000 0.375000 yes committed closed days| Option | Type | Default | Purpose |
|---|---|---|---|
--days | integer 3–30 | 7 | Lookback window. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.baseline.v1 |
A baseline is armed once at least three days in the lookback had spend; until then, anomaly rules skip evaluation. Figures use transactional daily spend excluding today; closed days reflect committed metadata immediately.
costs unit-economics
Per-unit cost metrics for the window, compared against the preceding window of the same length.
$ solwyn --project proj_0123456789abcdef01234567 costs unit-economics --range 30d --cause
Metric Value Previous Delta Context
Cost / request 0.012500 0.010000 +25.00% 200 requests
Tokens / dollar 50000.000000 — — 2.0M tokens served
Avg prompt tokens 120.000000 100.000000 +20.00% 200 requests| Option | Type | Default | Purpose |
|---|---|---|---|
--range | 7d | 30d | 90d | 30d | The window. This subcommand has no --from/--to. |
--vs-prev / --no-vs-prev | boolean | --vs-prev | Compare with the preceding same-length window. |
--cause | boolean | false | Ask the API for its inferred cost drivers. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.unit-economics.v1 |
A Cost / agent run row appears when the window contained runs. A delta is — rather than a fake zero whenever the previous value is missing or not positive. With --cause, a suspected cause and its confidence print to stderr, for example Suspected cause (high): Model mix shifted toward a higher-cost target.
costs drift
How far stored actual cost diverged from the pre-flight estimate the SDK made before each call, per provider and model.
$ solwyn --project proj_0123456789abcdef01234567 costs drift --range 7d
Provider Model Actual Estimated Difference Events
openai gpt-5 12.500000 10.000000 +2.500000 80
Total — 12.500000 10.000000 +2.500000 80| Option | Type | Default | Purpose |
|---|---|---|---|
--range | string | 7d | Preset window. |
--from, --to | ISO 8601 with timezone | none | Explicit window, both required together. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.drift.v1 |
Difference is actual minus estimated. Two caveat lines always print to stderr: the estimate heuristic (server-side, input tokens assumed equal to output), that events without an estimate are excluded and counted separately, that unit-priced and unpriced estimates are null, and that budget-denied events are included. The JSON payload carries the same caveats as a caveats object plus missing_estimate_count.
costs priced-models
The exact provider, model, and modality targets the pricing catalog can bill, stamped with the catalog version. Use it to find a valid --model for simulate.
$ solwyn --project proj_0123456789abcdef01234567 costs priced-models
Provider Model Modality Pricing version
openai gpt-5 text 2026-08-06
bedrock amazon.nova-pro-v1:0 text 2026-08-06| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.priced-models.v1 |
No options. The JSON payload records the catalog's provenance as a first-party vendored dataset.
costs simulate
Replay the window's historical usage against one current priced target and compare.
solwyn --project <project_id> costs simulate --provider <provider> --model <model> \
[--region <region> --service-tier <tier>] [--range <preset> | --from <iso> --to <iso>]| Option | Type | Default | Purpose |
|---|---|---|---|
--provider | provider enum | required | Target provider. |
--model | string | required | Exact model identifier from priced-models. |
--region | string | none | Required together with --service-tier for Bedrock; rejected for other providers. |
--service-tier | service-tier enum | none | Required together with --region for Bedrock; rejected for other providers. |
--range, --from, --to | window | 7d | As for drift. |
| Contract | |
|---|---|
| Auth | any |
| Exit codes | 0 success · 1 generic/API · 2 usage · 3 auth · 5 not found |
| JSON schema | solwyn.costs.simulation.v1 |
$ solwyn --project proj_0123456789abcdef01234567 costs simulate \
--provider bedrock --model amazon.nova-pro-v1:0 --region us-east-1 --service-tier standard
Metric Value
Provider bedrock
Model amazon.nova-pro-v1:0
Modality text
Region us-east-1
Service tier standard
Baseline 12.500000
Simulated 9.750000
Delta (repriced - baseline) -2.750000
Scanned events 100
Baseline events 95
Repriced events 90
Excluded events 10Three event populations matter. Scanned is every event in the window. Baseline is the priced, served events whose stored actual cost forms the baseline figure. Repriced is the subset compatible with the target, repriced at current rates. Unpriced and local events are excluded from both figures; events with a modality mismatch or a missing token breakdown stay in the baseline but are not repriced. When the two populations differ, stderr says so explicitly, because the delta then compares different sets. The pricing snapshot date and the approximate-tokenizer caveat also print to stderr.
Simulation is a read-only POST and does not reserve budget, rewrite spend or change billing. Its 25,000-event cap returns 422/exit 2 with no partial result when exceeded. Narrow the window. Definite 401 rejection may refresh JWT once; definite 429 uses the single bounded rate-limit allowance. A 503 or transport failure never automatically replays simulation.
MCP provides list_cost_events, get_cost_baseline, get_unit_economics, get_cost_drift, list_priced_models and simulate_costs in both profiles. get_costs(account=true) requires JWT, rejects explicit project_id, defaults to project grouping and accepts only account-supported filters. MCP pages and deadlines remain bounded.
Related
forecast— the month-to-date projectionproviders— failover groups and unmetered surfacestags— the tag keys and values you can filter and group bystatus— budget usage and provider health at a glance- Costs — the dashboard's view of the same data
- Privacy — the complete list of quantities Solwyn transmits, and the content it never does