Billing Report
Query the authenticated API key’s actual spend for a date range, grouped by billed model, including cost components, token counts, and request counts.
This endpoint is part of HaoAI OpenAPI. A key can read only its own usage; other keys in the same account or team are excluded.
Endpoint
GET https://hao.ai/api/v1/usage/billing-reportSend requests to the website host hao.ai.
Authentication
Pass an API key created in the dashboard in the request header:
Authorization: Bearer sk-haoai-...Existing sk-pohu-... keys are also accepted. A positive wallet balance is not required.
The key must be enabled and unexpired. If it has an IP allowlist, the script’s outbound IP must match that list.
API key authentication grants access only to that key’s JSON report. Usage from other keys is excluded. Pass the secret in the request header, never in the URL.
Query Parameters
Send parameters in the query string. For daily billing, use start_date, end_date, and timezone.
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | string | With end_date | First date, in YYYY-MM-DD format, inclusive |
end_date | string | With start_date | Last date, in YYYY-MM-DD format, inclusive |
timezone | string | No | IANA timezone, such as Asia/Shanghai; defaults to UTC |
range | string | No | Preset date range, such as yesterday, 7d, or 30d; defaults to 30d |
from, to | integer | For range=custom | Unix milliseconds mapped to calendar dates in the selected timezone; includes the entire date containing to |
model | string | No | Billed model ID; omit to return all models used by the key |
Supported presets: 12h, 24h, 48h, today, yesterday, 2daysago, 3d, 7d, 14d, 30d, 90d, thismonth, lastmonth, and custom.
This report uses calendar windows: 12h and 24h mean today, and 48h includes yesterday and today. Prefer explicit dates or yesterday.
An explicit date pair takes precedence; avoid mixing range formats.
A query window cannot exceed 90 days and returns only the authenticated API key’s costs. Unknown or duplicate query parameters return 400.
Daily Spend by Model
Set start_date and end_date to the same date to get each model’s actual_cost for that day.
For multiple days, query each date separately. A multi-day request aggregates the entire window; granularity and group_by are not supported.
For example, 2026-10-05 with Asia/Shanghai covers midnight on October 5 through midnight on October 6 in that timezone. The response expresses the window in UTC with an exclusive end time.
Request Examples
cURL
curl --get 'https://hao.ai/api/v1/usage/billing-report' \
-H "Authorization: Bearer $HAOAI_API_KEY" \
--data-urlencode 'start_date=2026-10-05' \
--data-urlencode 'end_date=2026-10-05' \
--data-urlencode 'timezone=Asia/Shanghai'Response Format
Successful response, 200 OK:
{
"code": 0,
"message": "success",
"data": {
"currency": "USD",
"range": {
"from": "2026-10-04T16:00:00Z",
"to": "2026-10-05T16:00:00Z"
},
"rows": [
{
"api_key_id": "63a7f4c1-1542-4a65-a03e-4d0e15f71962",
"api_key_name": "Production",
"api_key_prefix": "sk-haoai-7fA3k9mB",
"api_key_suffix": "hJv2",
"model": "anthropic/claude-sonnet-4-5",
"requests": 18,
"input_tokens": 120000,
"output_tokens": 45000,
"cache_read_tokens": 12000,
"cache_creation_tokens": 3000,
"input_cost": 0.4,
"output_cost": 0.8,
"cache_read_cost": 0.1,
"cache_creation_cost": 0.5,
"search_cost": 0,
"image_cost": 0,
"video_cost": 0,
"other_cost": 0,
"actual_cost": 1.8,
"official_cost": 3,
"effective_discount": 0.6
}
],
"totals": {
"requests": 18,
"input_tokens": 120000,
"output_tokens": 45000,
"cache_read_tokens": 12000,
"cache_creation_tokens": 3000,
"input_cost": 0.4,
"output_cost": 0.8,
"cache_read_cost": 0.1,
"cache_creation_cost": 0.5,
"search_cost": 0,
"image_cost": 0,
"video_cost": 0,
"other_cost": 0,
"actual_cost": 1.8,
"official_cost": 3,
"effective_discount": 0.6
}
}
}Envelope and Report Fields
| Field | Type | Description |
|---|---|---|
code | integer | 0 on success |
message | string | success on success |
data.currency | string | Always USD |
data.range.from | string | Inclusive start, in UTC RFC 3339 format |
data.range.to | string | Exclusive end, in UTC RFC 3339 format |
data.rows | array | Rows grouped by billed model for this key; empty when there is no usage |
data.totals | object | Request, token, and cost totals across rows; zero when there is no usage |
rows[] Fields
| Field | Type | Description |
|---|---|---|
api_key_id | string | The authenticated key’s public UUID |
api_key_name | string | Current key name |
api_key_prefix, api_key_suffix | string | Optional display prefix and trailing characters; never the full secret |
channel_name | string | Optional current channel label, not a separate billing group |
model | string | Billed model ID |
requests | integer | Request count |
input_tokens, output_tokens | integer | Input and output token counts |
cache_read_tokens, cache_creation_tokens | integer | Cache read and creation token counts |
input_cost, output_cost | number | Input and output costs |
cache_read_cost, cache_creation_cost | number | Cache read and creation costs |
search_cost, image_cost, video_cost, other_cost | number | Search, image, video, and other costs |
actual_cost | number | Actual platform charge, equal to the sum of cost components |
official_cost | number | Official list-price cost, not the actual charge |
effective_discount | number | actual_cost / official_cost; omitted when the official cost is zero |
totals contains the same count and cost fields, without key, channel, or model identifiers.
Amounts are in USD, settled at 1 micro-USD precision (0.000001 USD). Use actual_cost for spend tracking; do not recalculate historical bills using today’s model prices. Use decimal arithmetic when summing amounts.
The report uses the same processed usage aggregates as the dashboard. Recently completed requests may appear after a delay; query again later if needed. A failed query returns an error, never a fabricated zero-cost result. Recent calendar-day windows in whole-hour timezones can be represented exactly. Beyond the 100-day hourly retention window, historical ranges must align with whole UTC days. Windows that cannot be represented exactly by retained aggregates return 400.
Rate Limits
A maximum of 3 requests per minute is allowed. Excessive requests return 429.
Send requests sequentially, at least 20 seconds apart. On 429, wait for Retry-After before retrying.
Responses include Cache-Control: no-store and must not be stored in a public cache.
Error Responses
Example error response:
{
"code": 429,
"message": "too many requests",
"reason": "rate_limited"
}| Status | reason | Description |
|---|---|---|
| 400 | validation_error / bad_request | Invalid dates, timezone, model filter, or parameters; range too large or not exactly representable |
| 401 | unauthorized | Missing, invalid, disabled, expired, or deleted key; or inactive owning account |
| 403 | apikey_ip_not_allowed | Outbound IP is not allowed by the key |
| 429 | rate_limited | Too many requests; wait for Retry-After before retrying |
| 503 | service_unavailable | Service temporarily busy; retry after Retry-After |
| 500 | internal_error | Query failure or timeout; retry later |