Skip to content

Commit 935b91e

Browse files
Lookovclaude
andcommitted
docs: document /v2/logs, billing transactions, tracing headers and model lookups
- New Usage Logs page for GET /v2/logs: filters, paging, correlation ids - API Key Usage: the group_by/tz breakdown, plus the reconciliation trap that the top-level requests count matches sum(groups[].requests.charged), never .total - Account Balance: GET /v2/billing/transactions, keyset paging, and how reference_id joins a charge back to your own request - New capability page for X-Client-Request-Id, x-inference-id and the cost headers, including the carriers that deliberately omit them (SSE, wav) - Model Catalogue: GET /model/{id}, which is singular and not under /v1 - New Model Performance Metrics page for GET /models/metrics Two behaviours documented against the code rather than by analogy: the /v2/logs filters and ?aliases= on /models/metrics do NOT split on commas the way the catalogue filters do, and percentiles of tps run the opposite way to latency percentiles, so its p99 is the fastest case rather than the slowest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent e62d91d commit 935b91e

11 files changed

Lines changed: 1174 additions & 0 deletions

docs/SUMMARY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
* [🟡 Service Endpoints](api-references/service-endpoints/README.md)
1515
* [Account Balance](api-references/service-endpoints/account-balance.md)
1616
* [Model Catalogue API](api-references/service-endpoints/models-catalogue.md)
17+
* [Model Performance Metrics](api-references/service-endpoints/model-metrics.md)
1718
* [API Key Management](api-references/service-endpoints/api-key-management.md)
1819
* [API Key Usage](api-references/service-endpoints/api-key-usage.md)
20+
* [Usage Logs](api-references/service-endpoints/usage-logs.md)
1921
* [Complete Model List](api-references/service-endpoints/complete-model-list.md)
2022
* [All Model IDs](api-references/model-database.md)
2123
* [Text Models (LLM)](api-references/text-models-llm/README.md)
@@ -760,6 +762,7 @@
760762
* [Vision in Text Models](capabilities/image-to-text-vision.md)
761763
* [Web Search](capabilities/web-search.md)
762764
* [Batch Processing](capabilities/batch-processing.md)
765+
* [Request Tracing and Cost Headers](capabilities/request-tracing-and-cost.md)
763766
* [Model comparison](capabilities/models-comparsion.md)
764767

765768
***

docs/api-references/service-endpoints/account-balance.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,55 @@ Returns detailed billing information, balance and auto top-up settings.
2929
{% openapi-operation spec="billing-detail-v2" path="/v2/billing/detail" method="get" %}
3030
[OpenAPI billing-detail-v2](https://raw.githubusercontent.com/aimlapi/api-docs/refs/heads/main/docs/api-references/service-endpoints/billing-detail-v2.json)
3131
{% endopenapi-operation %}
32+
33+
## Get account transactions
34+
35+
`GET /v2/billing/transactions` answers "where did the balance go". Each entry is one movement of money — either a **successful top-up** or a **charge**, and a charge carries the model that produced it.
36+
37+
Authenticate with a **regular** AIML API key. There is no `key_prefix` here: a wallet belongs to the account, not to a key, so the endpoint always answers for the account behind the key you used.
38+
39+
{% hint style="info" %}
40+
This is the account ledger — the same figures that move your balance — so it is what you reconcile against. [API Key Usage](api-key-usage.md) and [Usage Logs](usage-logs.md) come from the analytics pipeline instead, and are meant for attribution and budgeting.
41+
{% endhint %}
42+
43+
### Reading an entry
44+
45+
`amount` is **always positive**; `direction` is what carries the sign — `TOPUP` for money in, `CHARGE` for money out. `type` says what produced the entry: `PAYMENT`, `BONUS`, `REFUND`, `ADJUSTMENT`, `MODEL_USAGE`, `CREDITS_EXPIRED` or `INIT`.
46+
47+
`reference_id` is what makes an entry joinable to something you already have. For a `MODEL_USAGE` charge it is the **inference id** — the same value returned in the `x-inference-id` response header, reported as `inference_id` in [Usage Logs](usage-logs.md), and, for an async generation, the `generation_id` that submit returned. For a payment it is the payment transaction id. It is `null` when there is nothing on your side to point at, such as a bonus, an expiry or a manual adjustment.
48+
49+
`model` is `null` on top-ups, on charges that are not model usage, and in the rare case where the model could not be resolved.
50+
51+
```bash
52+
# every charge, newest first
53+
curl -H 'Authorization: Bearer <YOUR_AIMLAPI_KEY>' \
54+
'https://api.aimlapi.com/v2/billing/transactions?direction=CHARGE&limit=100'
55+
```
56+
57+
### Paging through the ledger
58+
59+
Paging is **cursor-based**. Take `pagination.next_cursor` from a response and pass it back as `cursor` to get the next page; it is `null` once you reach the end.
60+
61+
{% hint style="warning" %}
62+
Do not page this endpoint by offset, and do not rebuild the cursor. The ledger grows at the head, so a numeric offset silently skips or repeats entries between pages. The cursor is opaque and is accepted **only in the exact form we issued** — re-encoding it is rejected with `400`.
63+
{% endhint %}
64+
65+
{% hint style="info" %}
66+
A charge lands **after** you saw your response — it is applied when the request completes on the billing side, and for async video after the generation reports `completed`. Reconciling the instant a generation finishes will under-count. Failed requests are not charged at all, so they have no entry here.
67+
68+
Timestamps are UTC, and a balance may legitimately go negative — overdraft is allowed.
69+
{% endhint %}
70+
71+
Returns the account's balance movements, newest first.
72+
73+
{% openapi-operation spec="billing-transactions-v2" path="/v2/billing/transactions" method="get" %}
74+
[OpenAPI billing-transactions-v2](https://raw.githubusercontent.com/aimlapi/api-docs/refs/heads/main/docs/api-references/service-endpoints/billing-transactions-v2.json)
75+
{% endopenapi-operation %}
76+
77+
### Errors
78+
79+
| Code | When |
80+
| ----- | ------------------------------------------------------------------------------------------------------------------ |
81+
| `400` | `to` not later than `from`; an unknown query parameter; `limit` outside 1–100; an unknown `direction` or `type`; a malformed or re-encoded cursor |
82+
| `401` | Missing or invalid key |
83+
| `403` | A management key — this route is for regular keys |

docs/api-references/service-endpoints/api-key-usage.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,37 @@ The response always echoes `start` and `end` resolved to UTC, so you can see exa
3131

3232
Any key can read its own spend. Reading the spend of a **different** key requires a management key.
3333

34+
## Grouping the breakdown
35+
36+
`/v2/usage/detail` can additionally split the window into buckets. Pass `group_by`:
37+
38+
| Value | Adds one entry per… |
39+
| ----------- | --------------------------------- |
40+
| `day` | day in the window |
41+
| `model` | model used in the window |
42+
| `day,model` | model per day |
43+
44+
```bash
45+
curl -H 'Authorization: Bearer <YOUR_AIMLAPI_KEY>' \
46+
'https://api.aimlapi.com/v2/usage/detail?period=30d&group_by=day,model&tz=Europe/Berlin'
47+
```
48+
49+
Each entry in the resulting `groups` array carries its `spend`, `tokens` and a `requests` object. Omitting `group_by` leaves the response exactly as it was.
50+
51+
`tz` sets where the day boundary falls; it defaults to UTC and only affects `group_by=day`. The top-level `start` and `end` stay UTC regardless.
52+
53+
{% hint style="warning" %}
54+
**Reconcile against `requests.charged`, not `requests.total`.**
55+
56+
A failed request is billed nothing — the hold is rolled back — so it has no charge behind it. `requests.total` counts every request in the group, `requests.charged` only the billed ones, and the top-level `requests` is the charged count. So `requests` equals the sum of `groups[].requests.charged`, and it matches the sum of `groups[].requests.total` only in a window where nothing failed.
57+
{% endhint %}
58+
59+
{% hint style="info" %}
60+
`tz` must be a **canonical IANA zone name matched exactly**`Europe/Berlin`, `UTC`. Offset forms (`+05:00`) and other-case spellings (`utc`) are rejected with `400`.
61+
62+
`group_by` and `tz` are accepted on `/v2/usage/detail` only. A flat total has nowhere to put a breakdown, so `/v2/usage` answers `400` rather than silently ignoring them.
63+
{% endhint %}
64+
3465
## Get key usage
3566

3667
Returns the total spend for a key over the requested window.
@@ -46,3 +77,7 @@ Returns the same total plus a per-model breakdown, sorted by spend.
4677
{% openapi-operation spec="usage-detail-v2" path="/v2/usage/detail" method="get" %}
4778
[OpenAPI usage-detail-v2](https://raw.githubusercontent.com/aimlapi/api-docs/refs/heads/main/docs/api-references/service-endpoints/usage-detail-v2.json)
4879
{% endopenapi-operation %}
80+
81+
{% hint style="info" %}
82+
Need the individual requests rather than totals — what failed, what a specific call cost, which of your own customers it belonged to? See [Usage Logs](usage-logs.md).
83+
{% endhint %}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"title": "AIML API",
5+
"version": "1.0.0"
6+
},
7+
"servers": [
8+
{
9+
"url": "https://api.aimlapi.com"
10+
}
11+
],
12+
"paths": {
13+
"/v2/billing/transactions": {
14+
"get": {
15+
"operationId": "_v2_billing_transactions",
16+
"parameters": [
17+
{
18+
"name": "from",
19+
"in": "query",
20+
"required": false,
21+
"description": "Only return entries created at or after this moment, ISO-8601 with an offset.",
22+
"schema": {
23+
"type": "string",
24+
"example": "2026-08-01T00:00:00Z"
25+
}
26+
},
27+
{
28+
"name": "to",
29+
"in": "query",
30+
"required": false,
31+
"description": "Only return entries created before this moment, ISO-8601 with an offset. Must be later than `from`.",
32+
"schema": {
33+
"type": "string",
34+
"example": "2026-08-18T00:00:00Z"
35+
}
36+
},
37+
{
38+
"name": "direction",
39+
"in": "query",
40+
"required": false,
41+
"description": "Keep only money coming in (`TOPUP`) or money going out (`CHARGE`).",
42+
"schema": {
43+
"type": "string",
44+
"enum": [
45+
"TOPUP",
46+
"CHARGE"
47+
],
48+
"example": "CHARGE"
49+
}
50+
},
51+
{
52+
"name": "type",
53+
"in": "query",
54+
"required": false,
55+
"description": "Keep only entries of this kind.",
56+
"schema": {
57+
"type": "string",
58+
"enum": [
59+
"PAYMENT",
60+
"BONUS",
61+
"REFUND",
62+
"ADJUSTMENT",
63+
"MODEL_USAGE",
64+
"CREDITS_EXPIRED",
65+
"INIT"
66+
],
67+
"example": "MODEL_USAGE"
68+
}
69+
},
70+
{
71+
"name": "limit",
72+
"in": "query",
73+
"required": false,
74+
"description": "Entries per page, 1-100.",
75+
"schema": {
76+
"type": "integer",
77+
"default": 50,
78+
"minimum": 1,
79+
"maximum": 100,
80+
"example": 50
81+
}
82+
},
83+
{
84+
"name": "cursor",
85+
"in": "query",
86+
"required": false,
87+
"description": "Position to continue from, taken verbatim from the previous response's `pagination.next_cursor`. The value is opaque and is accepted only in the exact form we issued.",
88+
"schema": {
89+
"type": "string",
90+
"example": "eyJ2IjoxLCJjIjoiMjAyNi0wOC0xN1QxMDowMDowMC4wMDBaIn0"
91+
}
92+
}
93+
],
94+
"responses": {
95+
"200": {
96+
"description": "One entry per balance movement, newest first.",
97+
"content": {
98+
"application/json": {
99+
"schema": {
100+
"type": "object",
101+
"properties": {
102+
"data": {
103+
"type": "array",
104+
"description": "The requested page of ledger entries.",
105+
"items": {
106+
"type": "object",
107+
"properties": {
108+
"created_at": {
109+
"type": "string",
110+
"description": "When the entry was recorded, in UTC.",
111+
"example": "2026-08-17T10:00:00.000Z"
112+
},
113+
"direction": {
114+
"type": "string",
115+
"enum": [
116+
"TOPUP",
117+
"CHARGE"
118+
],
119+
"description": "Which way the money moved. `amount` is always positive, so this is what carries the sign.",
120+
"example": "CHARGE"
121+
},
122+
"type": {
123+
"type": "string",
124+
"enum": [
125+
"PAYMENT",
126+
"BONUS",
127+
"REFUND",
128+
"ADJUSTMENT",
129+
"MODEL_USAGE",
130+
"CREDITS_EXPIRED",
131+
"INIT"
132+
],
133+
"description": "What produced the entry.",
134+
"example": "MODEL_USAGE"
135+
},
136+
"amount": {
137+
"type": "object",
138+
"description": "How much moved, always as a positive number.",
139+
"properties": {
140+
"usd": {
141+
"type": "number",
142+
"example": 1.56
143+
},
144+
"credits": {
145+
"type": "integer",
146+
"example": 3120000
147+
}
148+
},
149+
"required": [
150+
"usd",
151+
"credits"
152+
]
153+
},
154+
"model": {
155+
"type": "string",
156+
"nullable": true,
157+
"description": "Model that produced the charge. Null on top-ups, on charges that are not model usage, and when the model could not be resolved.",
158+
"example": "openai/gpt-5"
159+
},
160+
"reference_id": {
161+
"type": "string",
162+
"nullable": true,
163+
"description": "Handle that ties the entry back to something you have. For `MODEL_USAGE` it is the inference id, for a payment it is the payment transaction id. Null when the entry has no caller-side handle, such as a bonus, an expiry or a manual adjustment.",
164+
"example": "V1StGXR8Z5jdHi6BmyT8k"
165+
}
166+
},
167+
"required": [
168+
"created_at",
169+
"direction",
170+
"type",
171+
"amount",
172+
"model",
173+
"reference_id"
174+
]
175+
}
176+
},
177+
"pagination": {
178+
"type": "object",
179+
"properties": {
180+
"next_cursor": {
181+
"type": "string",
182+
"nullable": true,
183+
"description": "Pass this back as `cursor` to fetch the next page. Null on the last page.",
184+
"example": "eyJ2IjoxLCJjIjoiMjAyNi0wOC0xN1QxMDowMDowMC4wMDBaIn0"
185+
},
186+
"has_more": {
187+
"type": "boolean",
188+
"description": "Whether another page follows this one.",
189+
"example": true
190+
}
191+
},
192+
"required": [
193+
"next_cursor",
194+
"has_more"
195+
]
196+
}
197+
},
198+
"required": [
199+
"data",
200+
"pagination"
201+
]
202+
}
203+
}
204+
}
205+
}
206+
},
207+
"x-hideTryItPanel": true,
208+
"x-codeSamples": [
209+
{
210+
"lang": "cURL",
211+
"source": "curl -L \\\n --request GET \\\n --url 'https://api.aimlapi.com/v2/billing/transactions?direction=CHARGE&limit=50' \\\n --header 'Authorization: Bearer <YOUR_AIMLAPI_KEY>'"
212+
},
213+
{
214+
"lang": "JavaScript",
215+
"source": "async function main() {\n const url = new URL(\"https://api.aimlapi.com/v2/billing/transactions\");\n url.searchParams.set(\"direction\", \"CHARGE\");\n url.searchParams.set(\"limit\", \"50\");\n\n const response = await fetch(url, {\n headers: {\n \"Authorization\": \"Bearer <YOUR_AIMLAPI_KEY>\",\n },\n });\n\n const data = await response.json();\n console.log(JSON.stringify(data, null, 2));\n}\n\nmain();"
216+
},
217+
{
218+
"lang": "Python",
219+
"source": "import requests\nimport json\n\ndef main():\n response = requests.get(\n \"https://api.aimlapi.com/v2/billing/transactions\",\n headers={\n # Insert your AIML API key instead of <YOUR_AIMLAPI_KEY>:\n \"Authorization\": \"Bearer <YOUR_AIMLAPI_KEY>\",\n },\n params={\"direction\": \"CHARGE\", \"limit\": 50},\n )\n\n data = response.json()\n print(json.dumps(data, indent=2, ensure_ascii=False))\n\nif __name__ == \"__main__\":\n main()"
220+
}
221+
]
222+
}
223+
}
224+
},
225+
"components": {
226+
"securitySchemes": {
227+
"access-token": {
228+
"scheme": "bearer",
229+
"bearerFormat": "<YOUR_AIMLAPI_KEY>",
230+
"type": "http",
231+
"description": "Bearer key"
232+
}
233+
}
234+
}
235+
}

0 commit comments

Comments
 (0)