Spend, delivery and accuracy reports
Read what a project spent, how its executions ended, how close the estimates of its model calls were and what its customers cost.
The operations of this page are reports over what a project has already done. Each of them reads the project of the key and changes nothing. The console draws them on Overview, and the customer margin on Customers.
Which report answers what
| Report | What it answers |
|---|---|
GET /v1/analytics/spend | What the project spent today and in the current calendar month, day by day, and how much of its budgets is left. |
GET /v1/analytics/costs | Where the money of a window went: by day, by model and by provider connection. |
GET /v1/analytics/delivery | How the executions of the project's flows ended in a window, how many answers came from the cache and what the cache saved. |
GET /v1/analytics/accuracy | How far the estimates of the model calls were from what the calls cost, in total and by operation. |
GET /v1/analytics/customer-margin | The credits your customers were charged, against what their calls cost. |
The spend report and the cost breakdown count differently, so their totals may differ. The spend report sums what the ledger settled. The cost breakdown sums the cost of each attempt at a provider.
The delivery report counts executions of flows.
The window of a report
The spend report takes no window. Each of the other reports takes from and to, and both are optional.
Without
tothe window ends now. Withoutfromit starts seven days before its end.A window that is too long is not refused: its start is moved forward. The cost breakdown and the customer margin reach 31 days back from the end of the window, and the delivery and the accuracy reports 366 days.
When
fromis not earlier thanto, the window is the seven days beforeto.
The rule of the lists — both ends together, seven days at most — does not apply to a report, and a report does not answer invalid_date_range. The answer names the window that was used.
The dashboard of a flow (Flows) takes its window the same way and counts only whole hours that have closed: its from and to are moved to the start of their hour, and the hour in progress is left out.
Operations
The ledger's standing figure — today, this month, headroom
GET /v1/analytics/spend
- Scope:
read
No window: today, this month, and the headroom right now.
Responses
Spend summary
{
"data": {
"currency": "USD",
"spend_today": "0.02700000",
"spend_month": "0.02700000",
"active_reservations": 1,
"request_count": 1,
"average_cost": "0.02700000",
"denied_operations": 1,
"available_budget": "0.02700000",
"daily": [
{
"date": "string",
"spend": "0.02700000",
"count": 1
}
]
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( |
Headers
X-Request-Id
The body of every error is the error envelope.
Success and cache rates over the hourly rollup
GET /v1/analytics/delivery
- Scope:
read
Parameters
| Parameter | Description |
|---|---|
from optionalquery | Start of the window an analytics answer covers — a date the server can parse, ISO-8601 recommended (a value without an offset is read as UTC). Unlike the lists, each end is optional on its own and the window is never refused, only fitted (
|
to optionalquery | End of the window an analytics answer covers; now when absent. See
|
Responses
Delivery summary
{
"data": {
"from": "2026-10-02T09:30:00Z",
"to": "2026-10-02T09:30:00Z",
"requests": 1,
"completed": 1,
"failed": 1,
"rejected": 1,
"cache_hits": 1,
"success_rate": "string",
"cache_hit_rate": "string",
"p95_duration_ms": 1,
"active_flows": 1,
"saved_cost": "0.02700000",
"prevented_spend": "0.02700000"
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
How wrong the estimates were
GET /v1/analytics/accuracy
- Scope:
read
Summed over the daily accuracy snapshots, so the window is honoured. A window the snapshots do not cover yet answers the lifetime counters with is_all_time: true.
Parameters
| Parameter | Description |
|---|---|
from optionalquery | Start of the window an analytics answer covers — a date the server can parse, ISO-8601 recommended (a value without an offset is read as UTC). Unlike the lists, each end is optional on its own and the window is never refused, only fitted (
|
to optionalquery | End of the window an analytics answer covers; now when absent. See
|
Responses
Estimation accuracy
{
"data": {
"window": {
"from": "string",
"to": "string"
},
"is_all_time": true,
"history_since": "string",
"trend": [
{
"day": "string",
"sample_count": 1,
"mae_cost": "0.02700000",
"bias_cost": "0.02700000",
"cost_relative_error": "string"
}
],
"sample_count": 1,
"underestimated_input_count": 1,
"underestimated_input_rate": "string",
"avg_input_token_error": "string",
"avg_output_token_error": "string",
"avg_cost_error": "0.02700000",
"cost_error_sum": "0.02700000",
"mae_cost": "0.02700000",
"mae_input_tokens": "string",
"mae_output_tokens": "string",
"cost_relative_error": "string",
"input_token_relative_error": "string",
"output_token_relative_error": "string",
"bias_direction": "under",
"estimated_cost_sum": "0.02700000",
"actual_cost_sum": "0.02700000",
"by_operation": [
{
"operation_code": "generate_email",
"provider": "string",
"model": "gpt-4o-mini",
"sample_count": 1,
"underestimated_input_count": 1,
"avg_cost_error": "0.02700000",
"mae_cost": "0.02700000",
"cost_relative_error": "string",
"bias_direction": "under",
"needs_attention": true,
"estimated_cost_sum": "0.02700000",
"actual_cost_sum": "0.02700000"
}
]
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( |
Headers
X-Request-Id
The body of every error is the error envelope.
Where the money went, cut by day / model / connection
GET /v1/analytics/costs
- Scope:
read
Attributed per attempt over a caller-chosen window — deliberately not the same number as /v1/analytics/spend, which is the ledger's.
Parameters
| Parameter | Description |
|---|---|
from optionalquery | Start of the window an analytics answer covers — a date the server can parse, ISO-8601 recommended (a value without an offset is read as UTC). Unlike the lists, each end is optional on its own and the window is never refused, only fitted (
|
to optionalquery | End of the window an analytics answer covers; now when absent. See
|
Responses
Cost breakdown
{
"data": {
"window": {
"from": "2026-10-02T09:30:00Z",
"to": "2026-10-02T09:30:00Z"
},
"currency": "USD",
"total_cost": "0.02700000",
"requests": 1,
"attempts": 1,
"by_day": [
{
"key": "string",
"cost": "0.02700000",
"requests": 1,
"attempts": 1,
"share": "string"
}
],
"by_model": [
{
"key": "string",
"cost": "0.02700000",
"requests": 1,
"attempts": 1,
"share": "string"
}
],
"by_connection": [
{
"key": "string",
"cost": "0.02700000",
"requests": 1,
"attempts": 1,
"share": "string"
}
],
"by_model_connection": [
{
"model_code": "gpt-4o-mini",
"connection_id": "74438200973594624",
"cost": "0.02700000",
"requests": 1,
"attempts": 1,
"share": "string"
}
]
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
What we charged in credits against what the calls cost us
GET /v1/analytics/customer-margin
- Scope:
read
Two sides in two units, never a ratio: credits and dollars are different things, and only the tenant knows what a credit sells for. Reads raw journal and attempt rows, so the window is capped.
Parameters
| Parameter | Description |
|---|---|
from optionalquery | Start of the window an analytics answer covers — a date the server can parse, ISO-8601 recommended (a value without an offset is read as UTC). Unlike the lists, each end is optional on its own and the window is never refused, only fitted (
|
to optionalquery | End of the window an analytics answer covers; now when absent. See
|
external_user_id optionalquery | Narrows the report to one customer; absent means all of them.
|
Responses
Margin for the window, in total and per customer
{
"data": {
"window": {
"from": "2026-10-02T09:30:00Z",
"to": "2026-10-02T09:30:00Z"
},
"currency": "USD",
"total": {
"external_user_id": "user_8421",
"credits_spent": "12.5000",
"usd_cost": "0.02700000",
"requests": 1
},
"by_customer": [
{
"external_user_id": "user_8421",
"credits_spent": "12.5000",
"usd_cost": "0.02700000",
"requests": 1
}
]
}
}Errors
| Status | Codes | When |
|---|---|---|
| 401 | No credential, or one that is not valid | |
| 403 | Authenticated, but not allowed: scope ( | |
| 422 | Validation failure or a semantically impossible request. Field-level validation from the framework answers |
Headers
X-Request-Id
The body of every error is the error envelope.
Examples
Read the spend of the month
average_cost is spend_month divided by request_count. available_budget is the smallest amount left among the budgets of the project, and null when the project has none.
curl https://api.slen.ai/api/v1/analytics/spend \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": {
"currency": "USD",
"spend_today": "3.18240000",
"spend_month": "41.90775000",
"active_reservations": 0,
"request_count": 1301,
"average_cost": "0.03221195",
"denied_operations": 5,
"available_budget": "458.09225000",
"daily": [
{
"date": "2026-10-01",
"spend": "38.72535000",
"count": 1204
},
{
"date": "2026-10-02",
"spend": "3.18240000",
"count": 97
}
]
}
}Read how executions ended
The window is the month of September.
curl "https://api.slen.ai/api/v1/analytics/delivery?from=2026-09-01T00%3A00%3A00Z&to=2026-10-01T00%3A00%3A00Z" \
-H "Authorization: Bearer $SLEN_API_KEY"Read the accuracy of estimates
When the window has no samples, the answer carries the figures of the whole history of the project, and says so with is_all_time set to true.
curl "https://api.slen.ai/api/v1/analytics/accuracy?from=2026-09-01T00%3A00%3A00Z&to=2026-10-01T00%3A00%3A00Z" \
-H "Authorization: Bearer $SLEN_API_KEY"Break the cost down
The window is seven days. share of a slice is its part of total_cost.
curl "https://api.slen.ai/api/v1/analytics/costs?from=2026-09-25T00%3A00%3A00Z&to=2026-10-02T00%3A00%3A00Z" \
-H "Authorization: Bearer $SLEN_API_KEY"Read the margin by customer
The two sides are in two units: credits_spent is credits, and usd_cost is money. Only captured calls count. external_user_id in the query narrows the report to one customer.
curl "https://api.slen.ai/api/v1/analytics/customer-margin?from=2026-09-25T00%3A00%3A00Z&to=2026-10-02T00%3A00%3A00Z" \
-H "Authorization: Bearer $SLEN_API_KEY"{
"data": {
"window": {
"from": "2026-09-25T00:00:00+00:00",
"to": "2026-10-02T00:00:00+00:00"
},
"currency": "USD",
"total": {
"credits_spent": "36.0000",
"usd_cost": "0.41280000",
"requests": 18
},
"by_customer": [
{
"external_user_id": "user_8421",
"credits_spent": "24.0000",
"usd_cost": "0.27520000",
"requests": 12
},
{
"external_user_id": "user_3107",
"credits_spent": "12.0000",
"usd_cost": "0.13760000",
"requests": 6
}
]
}
}Where to go next
Overview — how money, credits and time are written in an answer.
Authentication — the scope a key needs to read a report.
Built from openapi b070e1f, sdk contract 5cc7055, php ae4a53b, node 84bc62d