Astrodyne

Usage API

Every request you have made, what it cost, and what it was charged on — queryable, paginated, and matching your transaction history exactly.

Endpoints

MethodPathSummary
GET/v1/usageRequest-level usage history, cursor-paginated.
GET/v1/usage/summaryTotals for a bounded range.
GET/v1/usage/by-modelSpend and billable usage per model.
GET/v1/usage/by-api-keySpend and billable usage per key you own.

These authenticate with your account session, like the API keys endpoints — an ak- token is refused.

Billable and raw are different numbers
billable_* is what your charge was computed from. raw_* is what the model reported, and it is null when the model reported nothing. They are never the same field and a null is never a zero — "we do not know" and "it was nothing" are different facts.

Token fields

FieldMeaning
billable_input_tokens
billable_output_tokens
billable_total_tokens
What the charge was computed from. Always present.
raw_input_tokens
raw_output_tokens
What the model reported. null when it reported nothing.
charged_unitsThe settled amount, in integer units.
reserved_units, released_units The hold taken before the request, and the unused part returned after it.

Where a model returns more than the cap you asked for, the excess is absorbed and never billed: billable_output_tokens never exceeds the max_tokens you sent.

Ranges and pagination

Reading usage

curl
# Request history. Ranges are [start, end) — end is EXCLUSIVE.
curl "https://api.astrodyne.ai/v1/usage?limit=25" \
  -H "Authorization: Bearer $ASTRODYNE_SESSION_TOKEN"

# Totals, and spend per model.
curl "https://api.astrodyne.ai/v1/usage/summary" \
  -H "Authorization: Bearer $ASTRODYNE_SESSION_TOKEN"
curl "https://api.astrodyne.ai/v1/usage/by-model" \
  -H "Authorization: Bearer $ASTRODYNE_SESSION_TOKEN"
Python
import os
import httpx

session = {"Authorization": f"Bearer {os.environ['ASTRODYNE_SESSION_TOKEN']}"}

with httpx.Client(base_url="https://api.astrodyne.ai") as http:
    summary = http.get("/v1/usage/summary", headers=session).json()
    print(summary["request_count"], "requests,", summary["spend_usd"], "USD")

    # Page through request history. The cursor is opaque; do not construct one.
    cursor = None
    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor
        page = http.get("/v1/usage", headers=session, params=params).json()
        for record in page["data"]:
            # billable_* is what you were charged on.
            # raw_* is what the model reported, and is None when it
            # reported nothing — do not treat that as zero.
            print(record["request_id"], record["model"],
                  record["billable_total_tokens"], record["charged_units"])
        if not page["has_more"]:
            break
        cursor = page["next_cursor"]
JavaScript
const session = { Authorization: `Bearer ${process.env.ASTRODYNE_SESSION_TOKEN}` };

const summary = await (await fetch(
  "https://api.astrodyne.ai/v1/usage/summary", { headers: session },
)).json();
console.log(summary.request_count, "requests,", summary.spend_usd, "USD");

// Page through history. The cursor is opaque; never construct one.
let cursor = null;
do {
  const url = new URL("https://api.astrodyne.ai/v1/usage");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const page = await (await fetch(url, { headers: session })).json();
  for (const record of page.data) {
    // billable_* is what you were charged on. raw_* is what the model
    // reported and is null when it reported nothing — not zero.
    console.log(record.request_id, record.model, record.billable_total_tokens);
  }
  cursor = page.has_more ? page.next_cursor : null;
} while (cursor);

Filters

ParameterNotes
startOptional. ISO-8601 UTC. Defaults to 30 days ago.
endOptional. ISO-8601 UTC. **Exclusive.**
modelOptional. Must be a model you can see.
api_key_idOptional. Must be a key you own.
statusOptional. One of the public status values.
limitOptional. 1–100, default 25.
cursorOptional. From `next_cursor`.

A model you cannot see and a model that does not exist both answer 404 model_not_found. An api_key_id belonging to someone else and one that does not exist both answer 404 api_key_not_found.

Totals

/v1/usage/summary reports counts and spend for a range. Provider coverage is reported as countsraw_usage_available_request_count and raw_usage_missing_request_count — rather than folded into a total, because summing an unknown as zero would understate it silently.

What these numbers are