Skip to content

Public Billing API ​

The Public Billing API returns spending for the authenticated API key by default. Keys owned by organization administrators can also query spending for their organization. The response includes spending before and after discounts for the range, each day, and each model on each date, plus categorized token usage.

Endpoint ​

text
GET https://console.exchangetoken.ai/api/public/v1/billing

Responses use application/json. Dates are sorted in ascending order; models within each date are sorted by model identifier. Results are aggregated and do not include individual request records.

Authentication ​

Use the same API key used for model requests in the Authorization header:

http
Authorization: Bearer sk-YOUR_API_KEY

Omitting scope or setting scope=key queries only the authenticated key's spending, including when the key belongs to an organization administrator. With scope=org, only keys owned by organization administrators (admin/root) can query spending across all keys in their organization. Keys owned by ordinary members receive 403 forbidden.

Permissions use the key owner's current account role. The server determines the organization from that account. Parameters such as token_id, user_id, or org_id that select a query target are not accepted. A console login session is not required. API keys must not be included in query parameters.

Exhausted quotas do not prevent billing queries. Expired, disabled, or deleted keys, disabled accounts, and requests that violate the key's IP restrictions cannot access this endpoint.

Query parameters ​

ParameterTypeRequiredDescription
fromstringYesInclusive start date in YYYY-MM-DD format.
tostringYesExclusive end date in YYYY-MM-DD format.
timezonestringNoIANA time zone for date boundaries and daily grouping. Defaults to UTC. Examples: Asia/Shanghai, America/New_York.
scopestringNoQuery scope: key for the authenticated key, or org for its organization. Defaults to key when omitted. org requires a key owned by an organization administrator.

The interval is [from, to). The start date must precede the end date, and the interval must contain no more than 31 calendar days. Supported years range from 1970 to 2299. Duplicate parameters, unsupported parameters, invalid time zones, and invalid scope values (including empty values) return 400.

For example, from=2026-09-01&to=2026-09-08 selects September 1 through September 7 in the requested time zone. To query one day, set to to the following date. Daylight saving time does not change the 31-calendar-day limit. Split longer ranges into adjacent, non-overlapping intervals.

Response fields ​

FieldTypeDescription
fromstringInclusive start date.
tostringExclusive end date.
timezonestringTime zone used for the query.
currencystringCurrency, always USD.
total_amountstringTotal spending before discounts for the interval, with 9 decimal places.
total_discounted_amountstringTotal spending after discounts for the interval, with 9 decimal places.
dataarrayDaily aggregates.
data[].datestringDate in YYYY-MM-DD format.
data[].total_amountstringDaily spending before discounts, with 9 decimal places.
data[].total_discounted_amountstringDaily spending after discounts, with 9 decimal places.
data[].modelsarrayModel aggregates for the date.
data[].models[].modelstringModel identifier, consistent with the console billing center.
data[].models[].amountstringSpending before discounts for this model and date, with 9 decimal places.
data[].models[].discounted_amountstringSpending after discounts for this model and date, with 9 decimal places.
data[].models[].usageobjectCategorized token usage for this model and date.

All usage fields are non-negative integers:

FieldDescription
input_tokensTokens billed as regular input, excluding tokens assigned to cache reads or cache writes.
cache_read_tokensTokens billed as cache reads.
cache_write_tokensTotal tokens billed as cache writes, across all cache durations.
output_tokensTokens billed as output.
reasoning_tokensTokens billed separately as reasoning.

amount and total_amount represent the original charges before discounts. discounted_amount and total_discounted_amount sum the downstream charges after discounts recorded in the bill; historical amounts are not recalculated using current discount settings. Without a discount, the before- and after-discount amounts are equal. Fully waived charges have an after-discount amount of 0.000000000.

Token usage follows billing categories. Reasoning tokens already included in output billing are not counted again in reasoning_tokens; a value of 0 does not imply that the model performed no reasoning. Categories with no usage return 0.

usage is aggregated by billing category without filtering by pricing unit. For models priced in units other than tokens, the returned values may follow the model's metering basis. Clients should use decimal arithmetic for monetary values.

For both before- and after-discount amounts, the range total equals the sum of daily totals, and each daily total equals the sum of its model amounts. Dates without spending return zero amounts and an empty models array.

Example response ​

The following values illustrate the response structure:

json
{
  "from": "2026-09-01",
  "to": "2026-09-03",
  "timezone": "UTC",
  "currency": "USD",
  "total_amount": "1.200000000",
  "total_discounted_amount": "0.960000000",
  "data": [
    {
      "date": "2026-09-01",
      "total_amount": "1.200000000",
      "total_discounted_amount": "0.960000000",
      "models": [
        {
          "model": "example-model",
          "amount": "1.200000000",
          "discounted_amount": "0.960000000",
          "usage": {
            "input_tokens": 100000,
            "cache_read_tokens": 20000,
            "cache_write_tokens": 10000,
            "output_tokens": 30000,
            "reasoning_tokens": 0
          }
        }
      ]
    },
    {
      "date": "2026-09-02",
      "total_amount": "0.000000000",
      "total_discounted_amount": "0.000000000",
      "models": []
    }
  ]
}

Examples ​

cURL ​

Supply the API key through an environment variable and encode query parameters with --data-urlencode:

bash
export EXCHANGE_TOKEN_API_KEY='sk-YOUR_API_KEY'

curl --fail-with-body --get \
  'https://console.exchangetoken.ai/api/public/v1/billing' \
  --header "Authorization: Bearer ${EXCHANGE_TOKEN_API_KEY}" \
  --data-urlencode 'from=2026-09-01' \
  --data-urlencode 'to=2026-09-08' \
  --data-urlencode 'timezone=Asia/Shanghai'

This example queries the authenticated key by default. To query organization billing with a key owned by an organization administrator, add --data-urlencode 'scope=org'. Response fields and the monetary basis are the same for both scopes.

Python ​

This example uses the Python standard library and parses monetary values with Decimal:

python
import json
import os
from decimal import Decimal
from urllib.parse import urlencode
from urllib.request import Request, urlopen

params = urlencode({
    "from": "2026-09-01",
    "to": "2026-09-08",
    "timezone": "UTC",
})
request = Request(
    "https://console.exchangetoken.ai/api/public/v1/billing?" + params,
    headers={"Authorization": "Bearer " + os.environ["EXCHANGE_TOKEN_API_KEY"]},
)
with urlopen(request, timeout=90) as response:
    bill = json.load(response)

print(Decimal(bill["total_amount"]), bill["currency"])
print(Decimal(bill["total_discounted_amount"]), bill["currency"])
for day in bill["data"]:
    for item in day["models"]:
        print(day["date"], item["model"], Decimal(item["amount"]),
              Decimal(item["discounted_amount"]), item["usage"])

Errors ​

Errors use the following JSON structure:

json
{
  "error": {
    "type": "invalid_request",
    "message": "date range must contain between 1 and 31 calendar days; to is exclusive"
  }
}
HTTP statuserror.typeDescription
400invalid_requestMissing dates, invalid dates or range, invalid time zone or scope, duplicate parameters, or unsupported parameters.
401unauthorizedA valid Bearer API key was not supplied.
403forbiddenKey or account status does not permit access, IP restrictions are not satisfied, or the key owner lacks permission to query organization billing.
404not_foundThe endpoint does not exist.
405method_not_allowedThe method is unsupported. This endpoint accepts only GET.
429rate_limit_exceededThe request is temporarily limited. Retry later.
500internal_errorAn internal service error occurred.
503service_unavailable / billing_unavailableThe service is busy, or authentication or billing data is temporarily unavailable.
504query_timeoutThe query timed out. Retry with a shorter date range.

Data updates and retries ​

For 429 responses, wait for the number of seconds specified in the Retry-After header before retrying.

Spending data updates have a short delay. Querying data for the current day is not recommended.