Skip to main content

Overview

The Token Usage API returns raw LLM token counts aggregated by time bucket, organization, member, and model. Use it to reconcile usage against your own model provider (AWS Bedrock, Anthropic, etc.), build cost dashboards, or audit inference by user and model. This API reports tokens, not ACUs. For ACU-based billing data, use the Usage API (GET /v1/billing/usage). Availability: BYOK deployments only. Deployments that use the TextQL-managed model provider cannot access this API. Base URL: https://app.textql.com/v1/billing Endpoint: GET /v1/billing/token-usage

Authentication

Same as the Usage API. All requests require an API key with billing:read permission. Create keys in Settings → Developers → API Keys. The token is Base64-encoded {member_id}:{api_token}. Pass it in one of two ways:
# Authorization header
-H 'Authorization: Bearer YOUR_TOKEN'

# Or as a custom header
-H 'tql_api_key: YOUR_TOKEN'
The API key’s organization determines tenant scope: results include all organizations in that tenant (unless filtered by organization).

Your First Request

Fetch the last 90 days of token usage for your tenant:
curl 'https://app.textql.com/v1/billing/token-usage' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Example response:
{
  "data": [
    {
      "start_datetime": "2026-01-31T00:00:00Z",
      "end_datetime": "2026-02-01T00:00:00Z",
      "organization": "acme-engineering",
      "email": "m.chen@acme.com",
      "model": "claude-sonnet-4-6",
      "input_tokens": 125000,
      "cache_read_input_tokens": 45000,
      "cache_write_input_tokens": 12000,
      "output_tokens": 38000,
      "total_tokens": 220000,
      "request_count": 142
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 100,
    "total_count": 1
  }
}
Each record is one (time bucket × organization × member × model) slice. Token fields are summed across all LLM requests in that slice.

Query Parameters

All parameters are optional query parameters on GET /token-usage.

Date range

ParameterFormatDefaultDescription
start_dateRFC 3339 (UTC)90 days before end_dateInclusive lower bound
end_dateRFC 3339 (UTC)Current UTC timeExclusive upper bound
Constraints:
  • start_date must be before end_date.
  • The window end_date − start_date cannot exceed 90 days.
curl 'https://app.textql.com/v1/billing/token-usage?start_date=2026-01-01T00:00:00Z&end_date=2026-02-01T00:00:00Z' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Granularity

Controls time bucket size. Default: day.
ValueBucket sizeend_datetime offset
hour1 hourstart_datetime + 1 hour
day1 calendar day (UTC)start_datetime + 1 day
month1 calendar month (UTC)start_datetime + 1 month
curl 'https://app.textql.com/v1/billing/token-usage?granularity=hour&start_date=2026-06-10T00:00:00Z&end_date=2026-06-11T00:00:00Z' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Filtering

By organization — comma-separated organization names (not UUIDs). Omit to include all organizations in the tenant.
curl 'https://app.textql.com/v1/billing/token-usage?organization=acme-engineering,acme-research' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Unknown organization names return 400 invalid_parameter. By user email — comma-separated, case-insensitive. Only members whose email matches (in the selected orgs) are included. If no members match, the response is an empty data array with 200 OK.
curl 'https://app.textql.com/v1/billing/token-usage?email=j.ramirez@acme.com,s.patel@acme.com' \
  -H 'Authorization: Bearer YOUR_TOKEN'
By model — comma-separated model names as stored in usage records (e.g. claude-sonnet-4-6, gpt-4o). Exact match.
curl 'https://app.textql.com/v1/billing/token-usage?model=claude-sonnet-4-6,gpt-4o' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Filters can be combined:
curl 'https://app.textql.com/v1/billing/token-usage?organization=acme-engineering&email=m.chen@acme.com&model=claude-sonnet-4-6&granularity=day&start_date=2026-01-27T00:00:00Z&end_date=2026-02-03T00:00:00Z' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Sorting

Use sort to order results before pagination. Prefix with - for descending. Default: -start_datetime (newest buckets first).
ValueDescription
start_datetimeOldest bucket first
-start_datetimeNewest bucket first (default)
emailAlphabetical by user email
-emailReverse alphabetical by email
modelAlphabetical by model name
-modelReverse alphabetical by model
total_tokensLowest total tokens first
-total_tokensHighest total tokens first
Tie-break order (when primary sort values match): email → model → start_datetime → organization → total_tokens.
curl 'https://app.textql.com/v1/billing/token-usage?sort=-total_tokens' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Pagination

ParameterDefaultMax
page_size1001000
page1
curl 'https://app.textql.com/v1/billing/token-usage?page=2&page_size=50' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Use pagination.total_count to compute total pages. Requesting a page beyond the last page returns an empty data array with the requested page and correct total_count.

Response Fields

Top level

FieldTypeDescription
dataarrayToken usage records (see below)
pagination.pageintegerCurrent page (1-based)
pagination.page_sizeintegerPage size used for this response
pagination.total_countintegerTotal matching records across all pages

Record object (data[])

FieldTypeDescription
start_datetimestring (UTC, RFC 3339)Start of the time bucket, inclusive
end_datetimestring (UTC, RFC 3339)End of the time bucket, exclusive
organizationstringOrganization name
emailstringMember email, lowercase. Empty string if usage is non-attributed (see below)
modelstringModel name
input_tokensintegerSum of input/prompt tokens
cache_read_input_tokensintegerSum of cache-read input tokens
cache_write_input_tokensintegerSum of cache-write input tokens
output_tokensintegerSum of output/completion tokens
total_tokensintegerSum of all four token fields above
request_countintegerNumber of LLM usage events in this bucket
total_tokens calculation:
total_tokens = input_tokens + cache_read_input_tokens + cache_write_input_tokens + output_tokens

Member Attribution

Usage is attributed to a member when member_id is present on the underlying usage event and the member’s email is not a TextQL staff domain. Non-attributed usage (empty email):
  • Usage with no member attribution
  • Usage from @textql.com or *.textql.com email addresses (TextQL staff)
Non-attributed rows are still returned with "email": "". Clients may display these as “(non-attributed)” in UI.

BYOK Requirement

This API is only available when your deployment uses your own model provider (BYOK — bring your own keys).
Deployment typeToken Usage API
BYOK (customer-managed provider)Available
TextQL-managed providerNot available403 forbidden
Error when not BYOK:
{
  "code": "forbidden",
  "message": "token usage API is only available for BYOK deployments"
}

Comparison with Usage API

Usage APIToken Usage API
PathGET /v1/billing/usageGET /v1/billing/token-usage
MetricACURaw token counts
Groupingcategory (llm_tokens, compute_hours, …)model name
BYOK requiredNoYes
Extra fieldscategory, acu, cost_centermodel, input_tokens, cache_*, output_tokens, total_tokens, request_count
Sort optionsincludes acu / -acuincludes total_tokens, model
Shared behavior: auth, tenant scoping, organization / email filters, date range defaults, 90-day max window, granularity, pagination.

Code Examples

Python — paginate all token usage for January

import requests

TOKEN = "your-token"
BASE = "https://app.textql.com/v1/billing"
headers = {"Authorization": f"Bearer {TOKEN}"}

def get_all_token_usage(**params):
    records = []
    page = 1
    while True:
        resp = requests.get(
            f"{BASE}/token-usage",
            headers=headers,
            params={**params, "page": page, "page_size": 1000},
        )
        resp.raise_for_status()
        data = resp.json()
        records.extend(data["data"])
        if len(records) >= data["pagination"]["total_count"]:
            break
        page += 1
    return records

usage = get_all_token_usage(
    start_date="2026-01-01T00:00:00Z",
    end_date="2026-02-01T00:00:00Z",
    granularity="day",
)

JavaScript — top models by token volume (last 90 days)

const TOKEN = "your-token";
const BASE = "https://app.textql.com/v1/billing";

async function getTokenUsage(params = {}) {
  const query = new URLSearchParams(params).toString();
  const res = await fetch(`${BASE}/token-usage?${query}`, {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

const data = await getTokenUsage({
  granularity: "month",
  sort: "-total_tokens",
  page_size: 20,
});

Error Reference

Errors use the same envelope as the Usage API:
{
  "code": "error_code",
  "message": "Human-readable description"
}
HTTP statuscodeTypical cause
400invalid_parameterInvalid date format; start_dateend_date; date range > 90 days; invalid granularity or sort; invalid email format; unknown organization name; invalid page / page_size
403forbiddenMissing/invalid/expired API key; insufficient permissions; or deployment is not BYOK
500internal_errorServer-side failure retrieving usage
Note: Authentication failures return 403 (not 401) with code: "forbidden".

Troubleshooting & Support

ProblemWhat to check
403 token usage API is only available for BYOK deploymentsDeployment must use customer-managed model keys, not TextQL-managed provider
403 on every requestToken format ({member_id}:{api_token} Base64), expiry, and billing:read permission
Empty dataWiden date range; remove email / model filters; confirm LLM usage exists for the tenant
Unknown organizationorganization must be the exact org name as shown in TextQL, not a UUID
email is empty on some rowsExpected for non-attributed usage (unattributed events or TextQL staff)
Counts differ from provider billsThis API reflects TextQL-recorded usage; provider billing may use different bucketing or attribution
If you’re still running into issues, contact support at support@textql.com and include the full request URL and response body.