API reference
GET /usage
Read the quota status and daily usage meters for an app.
The usage endpoint reports the authenticated app’s free-quota status for the current UTC day plus a dense day-by-day series you can chart. Use it to show your own dashboard, decide whether to top up credits, or back off before you cross the free allowance. For the conceptual model behind these numbers — the two-band metering, the weighted AI formula, refunds — see Billing.
/api/v1/usageScope: usage:read Unmetered
This endpoint is deliberately unmetered: reading it never consumes free quota and never debits your wallet. Quota visibility must stay reachable even when you are over quota or out of credits, so it behaves like the models and identity reads — always available to a valid key with the scope.
Scope
The key must carry the usage:read scope (or the * wildcard). A key without it is
rejected with 403 insufficient_scope, and the error carries the required scope in
its body. See Scopes for the full grant list and
Authentication for how a key is resolved to its owner across
linked Discord × Google identities.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
days |
integer | No |
Length of the returned daily series, ending today (UTC). Clamped to the range
[1, 90]; the default is 7. A missing, blank, or non-numeric value
falls back to 7; an out-of-range value is clamped (below 1 clamps up to 1, above
90 clamps down to 90). The resolved value is echoed back as window_days.
|
Response fields
On success the endpoint returns 200 with the usual { "data": …, "meta": { "request_id" } }
envelope. The data object contains:
| Field | Type | Description |
|---|---|---|
app_id | string | The authenticated app the key belongs to. |
date | string | Today’s UTC calendar day (YYYY-MM-DD) — the current quota window. Resets at 00:00 UTC. |
free_quota_units | integer | The app’s free daily allowance in credit units. 0 means unlimited free (the app is never billed). |
used_units_today | integer | Credit units consumed by this app so far today (the credit_units meter for date). |
remaining_free_units | integer | null | Free units left today: max(0, free_quota_units − used_units_today). null when free_quota_units is 0 (unlimited). |
billing_enabled | boolean | Whether over-quota calls currently debit the owner’s prepaid wallet. When false, over-quota calls stay free but are still metered. |
units_per_call | integer | The flat credit cost of one non-AI billable call. 0 means non-AI calls never consume quota or bill. |
window_days | integer | The resolved days value (1–90) — the length of the daily series. |
daily | array | Dense day-by-day series, oldest → today, one entry per UTC day (see below). Always window_days entries long. |
The daily series
Each element of daily is one UTC calendar day. The series is zero-filled:
days with no traffic are still present with zero counters, so a chart never has gaps and you always get
exactly window_days ordered points from oldest to newest.
| Field | Type | Description |
|---|---|---|
date | string | The UTC calendar day (YYYY-MM-DD) for this entry. |
credit_units | integer | Total credit units metered on this day (the quota meter). AI calls contribute weighted token units; flat calls contribute units_per_call. |
requests | integer | Number of billable requests metered on this day, regardless of unit cost. |
used_units_today is the live quota-gate counter for the current day and equals the
credit_units of the last (today’s) entry in daily. Both come from the same
durable daily counter, so they are always consistent within one response.
Request
cURLcurl "https://exe.krallo.com/api/v1/usage?days=7" \
-H "Authorization: Bearer krallo_live_..."
Node.js
const res = await fetch("https://exe.krallo.com/api/v1/usage?days=7", {
headers: { Authorization: `Bearer ${process.env.KRALLO_API_KEY}` },
});
const { data } = await res.json();
console.log(
`Used ${data.used_units_today} of ` +
(data.free_quota_units === 0
? "unlimited"
: `${data.free_quota_units}`) +
` free units today.`,
);
Response
A metered day and several quiet ones, with the free quota still in effect and billing off:
{
"data": {
"app_id": "01J8ZBQ2C4D6E8F0G2H4J6K8M0",
"date": "2026-07-15",
"free_quota_units": 10000,
"used_units_today": 148,
"remaining_free_units": 9852,
"billing_enabled": false,
"units_per_call": 1,
"window_days": 7,
"daily": [
{ "date": "2026-07-09", "credit_units": 0, "requests": 0 },
{ "date": "2026-07-10", "credit_units": 512, "requests": 34 },
{ "date": "2026-07-11", "credit_units": 96, "requests": 12 },
{ "date": "2026-07-12", "credit_units": 0, "requests": 0 },
{ "date": "2026-07-13", "credit_units": 1240, "requests": 71 },
{ "date": "2026-07-14", "credit_units": 380, "requests": 22 },
{ "date": "2026-07-15", "credit_units": 148, "requests": 9 }
]
},
"meta": { "request_id": "c2a4e6d8-1b3f-4a5c-9e7d-0f2b4c6d8e1a" }
}
When the app’s allowance is set to unlimited (free_quota_units of 0),
remaining_free_units is null rather than a number:
{
"data": {
"app_id": "01J8ZBQ2C4D6E8F0G2H4J6K8M0",
"date": "2026-07-15",
"free_quota_units": 0,
"used_units_today": 148,
"remaining_free_units": null,
"billing_enabled": true,
"units_per_call": 1,
"window_days": 7,
"daily": [ /* ... */ ]
},
"meta": { "request_id": "d5b7a9c1-3e2f-4d6b-8c0a-1f3e5d7b9a2c" }
}
Errors
Errors use the standard error envelope
({ "error": { "code", "message", … }, "meta": { "request_id" } }). Because the endpoint is
unmetered, it never returns 402 insufficient_credits.
| Status | Code | When |
|---|---|---|
| 401 | unauthorized | No bearer token was sent. |
| 401 | invalid_token | The bearer token is malformed or unknown. |
| 401 | token_revoked / token_expired | The key was revoked or has passed its expiry. See Authentication. |
| 403 | insufficient_scope | The key lacks usage:read. The error body includes required_scope. See Scopes. |
| 403 | app_disabled | The owning app is disabled. |
| 403 | owner_suspended | The account behind the key is suspended. |
| 404 | not_found | The Developer API is turned off platform-wide, or the app could not be resolved for this owner. |
| 405 | method_not_allowed | Any method other than GET. |
| 429 | rate_limited | The per-key rate limit was exceeded. See Rate limits. |
| 500 | internal_error | The usage counter store was unavailable. The endpoint fails closed rather than reporting a wrong number. |
Related
- Billing & quotas — the two-band metering model, the weighted AI formula, refunds, and the config keys behind these numbers.
- Rate limits — the per-key throttle and the
X-RateLimit-*headers on every response. - Scopes — what
usage:readand the other grants unlock. - Errors — the full status-code and error-code reference.