Home / Docs / Credits and usage
Credits and usage
Read your balance, your spending limits and the usage behind a bill.
Router credit is not on sale yet; nobody can top up today. Use this page to read your balance, your spending limits and the usage behind a bill. Money is held in wallets and counted in mu, the unit of account.
Read your balance and spending
Today the balance and the spending page are session endpoints of the console, not relay-key endpoints:
GET /api/me/wallets
Authentication signed-in session; roles: admin, cfo_viewer, fde, hr_ops, manager, member, operator
Response
| Field | Type | Description |
|---|---|---|
wallets required | array of object | |
wallets[].id required | string | The answer's id. |
wallets[].tenantId | string | |
wallets[].ownerKind | string | |
wallets[].ownerId | string | |
wallets[].kind required | string | |
wallets[].balanceMu required | integer | |
wallets[].heldMu required | integer | |
wallets[].currencyHint | string or null | |
wallets[].expiresAt | string or null | |
wallets[].spendPriority | integer | |
wallets[].sourceKind | string or null | |
wallets[].sourceRef | string or null | |
wallets[].periodStart | string or null | |
wallets[].periodEnd | string or null | |
wallets[].rolloverCapMu | integer or null | |
wallets[].createdAt | string | |
wallets[].closedAt | string or null |
{
"wallets": [
{
"id": "<id>",
"tenantId": "<tenantId>",
"ownerKind": "<ownerKind>",
"ownerId": "<ownerId>",
"kind": "<kind>",
"balanceMu": 0,
"heldMu": 0,
"currencyHint": "<currencyHint>",
"expiresAt": "<expiresAt>",
"spendPriority": 0,
"sourceKind": "<sourceKind>",
"sourceRef": "<sourceRef>",
"periodStart": "<periodStart>",
"periodEnd": "<periodEnd>",
"rolloverCapMu": "<rolloverCapMu>",
"createdAt": "<createdAt>",
"closedAt": "<closedAt>"
}
]
}Common errors
401not signed in
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"description": "A session credential. A person with no personal workspace yet gets an empty list, not an error. Amounts are in mu (the unit of account).",
"type": "object",
"required": [
"wallets"
],
"properties": {
"wallets": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind",
"balanceMu",
"heldMu"
],
"properties": {
"id": {
"type": "string"
},
"tenantId": {
"type": "string"
},
"ownerKind": {
"type": "string"
},
"ownerId": {
"type": "string"
},
"kind": {
"type": "string"
},
"balanceMu": {
"type": "integer"
},
"heldMu": {
"type": "integer"
},
"currencyHint": {
"type": [
"string",
"null"
]
},
"expiresAt": {
"type": [
"string",
"null"
]
},
"spendPriority": {
"type": "integer"
},
"sourceKind": {
"type": [
"string",
"null"
]
},
"sourceRef": {
"type": [
"string",
"null"
]
},
"periodStart": {
"type": [
"string",
"null"
]
},
"periodEnd": {
"type": [
"string",
"null"
]
},
"rolloverCapMu": {
"type": [
"integer",
"null"
]
},
"createdAt": {
"type": "string"
},
"closedAt": {
"type": [
"string",
"null"
]
}
}
}
}
}
}GET /api/me/spending
Authentication signed-in session; roles: admin, cfo_viewer, fde, hr_ops, manager, member, operator
Response
| Field | Type | Description |
|---|---|---|
wallets required | array of object | |
wallets[].id required | string | The answer's id. |
wallets[].tenantId | string | |
wallets[].ownerKind | string | |
wallets[].ownerId | string | |
wallets[].kind required | string | |
wallets[].balanceMu required | integer | |
wallets[].heldMu required | integer | |
wallets[].currencyHint | string or null | |
wallets[].expiresAt | string or null | |
wallets[].spendPriority | integer | |
wallets[].sourceKind | string or null | |
wallets[].sourceRef | string or null | |
wallets[].periodStart | string or null | |
wallets[].periodEnd | string or null | |
wallets[].rolloverCapMu | integer or null | |
wallets[].createdAt | string | |
wallets[].closedAt | string or null | |
caps required | array of object | |
caps[].id | string | The answer's id. |
caps[].period | string | |
caps[].tier | object | |
caps[].botKey | object | |
caps[].limitMu | integer or null | |
caps[].perRequestMu | integer or null | |
caps[].reservedMu | integer or null | |
caps[].spentMu | integer | |
caps[].periodStartedAt | string or null | |
caps[].runsOutAt | string or null | |
pause required | object or null | |
pause.pausedAt | string | |
pause.pausedBy | string | |
pause.note | string or null | |
topUpPolicy | object or null | |
topUpPolicy.id | string | The answer's id. |
topUpPolicy.amountMu | integer | |
topUpPolicy.triggerBalanceMu | integer | |
topUpPolicy.monthlyCeilingMu | integer or null | |
topUpIntents required | array of object | |
topUpIntents[].id | string | The answer's id. |
topUpIntents[].amountMu | integer | |
topUpIntents[].reason | string | |
topUpIntents[].state | string | |
topUpIntents[].periodStart | string | |
topUpIntents[].confirmedAt | string or null | |
topUpIntents[].createdAt | string | |
skippedRuns required | array of object | |
skippedRuns[].id | string | The answer's id. |
skippedRuns[].reason | string | |
skippedRuns[].detail | string or null | |
skippedRuns[].createdAt | string | |
trial required | object or null | |
trial.balanceMu | integer | |
trial.expiresAt | string or null | |
trial.daysLeft | number | |
trial.freeLaneCallsThisPeriod | integer |
{
"wallets": [
{
"id": "<id>",
"tenantId": "<tenantId>",
"ownerKind": "<ownerKind>",
"ownerId": "<ownerId>",
"kind": "<kind>",
"balanceMu": 0,
"heldMu": 0,
"currencyHint": "<currencyHint>",
"expiresAt": "<expiresAt>",
"spendPriority": 0,
"sourceKind": "<sourceKind>",
"sourceRef": "<sourceRef>",
"periodStart": "<periodStart>",
"periodEnd": "<periodEnd>",
"rolloverCapMu": "<rolloverCapMu>",
"createdAt": "<createdAt>",
"closedAt": "<closedAt>"
}
],
"caps": [
{
"id": "<id>",
"period": "<period>",
"tier": "<tier>",
"botKey": "<botKey>",
"limitMu": "<limitMu>",
"perRequestMu": "<perRequestMu>",
"reservedMu": "<reservedMu>",
"spentMu": 0,
"periodStartedAt": "<periodStartedAt>",
"runsOutAt": "<runsOutAt>"
}
],
"pause": {
"pausedAt": "<pausedAt>",
"pausedBy": "<pausedBy>",
"note": "<note>"
},
"topUpPolicy": {
"id": "<id>",
"amountMu": 0,
"triggerBalanceMu": 0,
"monthlyCeilingMu": "<monthlyCeilingMu>"
},
"topUpIntents": [
{
"id": "<id>",
"amountMu": 0,
"reason": "<reason>",
"state": "<state>",
"periodStart": "<periodStart>",
"confirmedAt": "<confirmedAt>",
"createdAt": "<createdAt>"
}
],
"skippedRuns": [
{
"id": "<id>",
"reason": "<reason>",
"detail": "<detail>",
"createdAt": "<createdAt>"
}
],
"trial": {
"balanceMu": 0,
"expiresAt": "<expiresAt>",
"daysLeft": 0,
"freeLaneCallsThisPeriod": 0
}
}Common errors
401not signed in
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"description": "A session credential. With no personal workspace the page is empty (topUpPolicy is then absent).",
"type": "object",
"required": [
"wallets",
"caps",
"pause",
"topUpIntents",
"skippedRuns",
"trial"
],
"properties": {
"wallets": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind",
"balanceMu",
"heldMu"
],
"properties": {
"id": {
"type": "string"
},
"tenantId": {
"type": "string"
},
"ownerKind": {
"type": "string"
},
"ownerId": {
"type": "string"
},
"kind": {
"type": "string"
},
"balanceMu": {
"type": "integer"
},
"heldMu": {
"type": "integer"
},
"currencyHint": {
"type": [
"string",
"null"
]
},
"expiresAt": {
"type": [
"string",
"null"
]
},
"spendPriority": {
"type": "integer"
},
"sourceKind": {
"type": [
"string",
"null"
]
},
"sourceRef": {
"type": [
"string",
"null"
]
},
"periodStart": {
"type": [
"string",
"null"
]
},
"periodEnd": {
"type": [
"string",
"null"
]
},
"rolloverCapMu": {
"type": [
"integer",
"null"
]
},
"createdAt": {
"type": "string"
},
"closedAt": {
"type": [
"string",
"null"
]
}
}
}
},
"caps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"period": {
"type": "string"
},
"tier": {},
"botKey": {},
"limitMu": {
"type": [
"integer",
"null"
]
},
"perRequestMu": {
"type": [
"integer",
"null"
]
},
"reservedMu": {
"type": [
"integer",
"null"
]
},
"spentMu": {
"type": "integer"
},
"periodStartedAt": {
"type": [
"string",
"null"
]
},
"runsOutAt": {
"type": [
"string",
"null"
]
}
}
}
},
"pause": {
"type": [
"object",
"null"
],
"properties": {
"pausedAt": {
"type": "string"
},
"pausedBy": {
"type": "string"
},
"note": {
"type": [
"string",
"null"
]
}
}
},
"topUpPolicy": {
"type": [
"object",
"null"
],
"properties": {
"id": {
"type": "string"
},
"amountMu": {
"type": "integer"
},
"triggerBalanceMu": {
"type": "integer"
},
"monthlyCeilingMu": {
"type": [
"integer",
"null"
]
}
}
},
"topUpIntents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"amountMu": {
"type": "integer"
},
"reason": {
"type": "string"
},
"state": {
"type": "string"
},
"periodStart": {
"type": "string"
},
"confirmedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": "string"
}
}
}
},
"skippedRuns": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"reason": {
"type": "string"
},
"detail": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": "string"
}
}
}
},
"trial": {
"type": [
"object",
"null"
],
"properties": {
"balanceMu": {
"type": "integer"
},
"expiresAt": {
"type": [
"string",
"null"
]
},
"daysLeft": {
"type": "number"
},
"freeLaneCallsThisPeriod": {
"type": "integer"
}
}
}
}
}A wallet has a balance and an amount held. A spending cap has a period, a limit, what is spent so far, and a forecast of the day it runs out. A person can pause spending, and the page lists the runs a pause skipped.
What happens when a budget runs out
Budgets degrade before they block. When a budget runs out the router first lowers autonomy or files a request for help; a hard stop comes after. When a request does stop for money it answers 402; see Errors.
Read usage with your relay key
Four read-only endpoints under /v1 take the relay key itself as the bearer, exactly as the completion endpoints do. A revoked, expired or unknown key is refused with the same 401 as everywhere else. They read the key's own ledgers: a decision of another key or another workspace is a 404, never a 403. Each costs one request against the key's rate limit.
curl https://api.massrelay.ai/v1/key -H "Authorization: Bearer mrk_<prefix>_<secret>"Key
GET /v1/key
Authentication relay key (bearer)
Example request
curl https://api.massrelay.ai/v1/key \
-H "Authorization: Bearer $RELAY_KEY"import os, requests
r = requests.get(
"https://api.massrelay.ai/v1/key",
headers={"Authorization": f"Bearer {os.environ['RELAY_KEY']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://api.massrelay.ai/v1/key", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.RELAY_KEY}` },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());Response
| Field | Type | Description |
|---|---|---|
object required | string | The kind of object returned, for example chat.completion. Always "key". |
id required | string | The answer's id. |
prefix required | string | |
scopes required | array of string | |
budgetEnvelopeId required | string or null | |
expiresAt required | string or null | |
createdAt required | string | |
lastUsedAt required | string or null | |
unit required | string | Always "mu". |
spendMu required | integer | micro-dollars, the unit of account |
period required | object | |
period.from required | string | |
period.to required | string |
{
"object": "key",
"id": "<id>",
"prefix": "<prefix>",
"scopes": [
"<scopes>"
],
"budgetEnvelopeId": "<budgetEnvelopeId>",
"expiresAt": "<expiresAt>",
"createdAt": "<createdAt>",
"lastUsedAt": "<lastUsedAt>",
"unit": "mu",
"spendMu": 0,
"period": {
"from": "<from>",
"to": "<to>"
}
}Common errors
401invalid_api_key403insufficient_scope429over the request rate; read Retry-After
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"type": "object",
"required": [
"object",
"id",
"prefix",
"scopes",
"budgetEnvelopeId",
"expiresAt",
"createdAt",
"lastUsedAt",
"unit",
"spendMu",
"period"
],
"properties": {
"object": {
"const": "key"
},
"id": {
"type": "string"
},
"prefix": {
"type": "string"
},
"scopes": {
"type": "array",
"items": {
"type": "string"
}
},
"budgetEnvelopeId": {
"type": [
"string",
"null"
]
},
"expiresAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": "string"
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"unit": {
"const": "mu"
},
"spendMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"period": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
}
}
}
}
}Returns the key's id, prefix, scopes, budget envelope, expiry, and what it spent this calendar month in mu. Never the hash.
Credits
GET /v1/credits
Authentication relay key (bearer)
Example request
curl https://api.massrelay.ai/v1/credits \
-H "Authorization: Bearer $RELAY_KEY"import os, requests
r = requests.get(
"https://api.massrelay.ai/v1/credits",
headers={"Authorization": f"Bearer {os.environ['RELAY_KEY']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://api.massrelay.ai/v1/credits", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.RELAY_KEY}` },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());Response
| Field | Type | Description |
|---|---|---|
object required | string | The kind of object returned, for example chat.completion. Always "credits". |
unit required | string | Always "mu". |
displayCurrency required | string or null | |
totalBalanceMu required | integer | micro-dollars, the unit of account |
totalAvailableMu required | integer | micro-dollars, the unit of account |
wallets required | array of object | |
wallets[].id required | string | The answer's id. |
wallets[].kind required | object | One of: prepaid, allowance, grant, pool, sponsor. |
wallets[].balanceMu required | integer | micro-dollars, the unit of account |
wallets[].heldMu required | integer | micro-dollars, the unit of account |
wallets[].availableMu required | integer | micro-dollars, the unit of account |
wallets[].currency required | string or null | |
wallets[].expiresAt required | string or null | |
freeAllowanceLeft required | integer or null | requests left in this month's free allowance; null when the plan has none |
freeAllowance required | object or null | |
freeAllowance.meter | string | |
freeAllowance.plan | string or null | |
freeAllowance.allowance | integer | |
freeAllowance.used | integer | |
freeAllowance.resetsAt | string |
{
"object": "credits",
"unit": "mu",
"displayCurrency": "<displayCurrency>",
"totalBalanceMu": 0,
"totalAvailableMu": 0,
"wallets": [
{
"id": "<id>",
"kind": "prepaid",
"balanceMu": 0,
"heldMu": 0,
"availableMu": 0,
"currency": "<currency>",
"expiresAt": "<expiresAt>"
}
],
"freeAllowanceLeft": "<freeAllowanceLeft>",
"freeAllowance": {
"meter": "<meter>",
"plan": "<plan>",
"allowance": 0,
"used": 0,
"resetsAt": "<resetsAt>"
}
}Common errors
401invalid_api_key403insufficient_scope429over the request rate; read Retry-After
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"description": "The wallets are the key's workspace's own (owner kind workspace).",
"type": "object",
"required": [
"object",
"unit",
"displayCurrency",
"totalBalanceMu",
"totalAvailableMu",
"wallets",
"freeAllowanceLeft",
"freeAllowance"
],
"properties": {
"object": {
"const": "credits"
},
"unit": {
"const": "mu"
},
"displayCurrency": {
"type": [
"string",
"null"
]
},
"totalBalanceMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"totalAvailableMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"wallets": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"kind",
"balanceMu",
"heldMu",
"availableMu",
"currency",
"expiresAt"
],
"properties": {
"id": {
"type": "string"
},
"kind": {
"enum": [
"prepaid",
"allowance",
"grant",
"pool",
"sponsor"
]
},
"balanceMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"heldMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"availableMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"currency": {
"type": [
"string",
"null"
]
},
"expiresAt": {
"type": [
"string",
"null"
]
}
}
}
},
"freeAllowanceLeft": {
"type": [
"integer",
"null"
],
"description": "requests left in this month's free allowance; null when the plan has none"
},
"freeAllowance": {
"type": [
"object",
"null"
],
"properties": {
"meter": {
"type": "string"
},
"plan": {
"type": [
"string",
"null"
]
},
"allowance": {
"type": "integer"
},
"used": {
"type": "integer"
},
"resetsAt": {
"type": "string"
}
}
}
}
}Returns the wallets of the workspace that owns the key: balance, held and available per wallet in mu, with the currency the wallet shows in, plus freeAllowanceLeft, the requests left in this month's free allowance (null when the plan has none).
Generation
GET /v1/generation/{id}
Authentication relay key (bearer)
Path parameters
id required | string |
Example request
curl https://api.massrelay.ai/v1/generation/{id} \
-H "Authorization: Bearer $RELAY_KEY"import os, requests
r = requests.get(
"https://api.massrelay.ai/v1/generation/{id}",
headers={"Authorization": f"Bearer {os.environ['RELAY_KEY']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://api.massrelay.ai/v1/generation/{id}", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.RELAY_KEY}` },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());Response
| Field | Type | Description |
|---|---|---|
object required | string | The kind of object returned, for example chat.completion. Always "generation". |
id required | string | The answer's id. |
model required | string or null | The model id. In a request it is optional: without it the key's routing chooses. |
provider required | string or null | |
inputTokens required | integer | |
outputTokens required | integer | |
unit required | string | Always "mu". |
costMu required | integer | micro-dollars, the unit of account |
fee required | object or null | |
fee.costMu | integer | micro-dollars, the unit of account |
fee.plan | string or null | |
fee.reason | string or null | |
latencyMs required | integer or null | |
createdAt required | string or null | |
explain required | string | the /v1/route/explain/{id} path |
{
"object": "generation",
"id": "<id>",
"model": "provider/model-id",
"provider": "<provider>",
"inputTokens": 0,
"outputTokens": 0,
"unit": "mu",
"costMu": 0,
"fee": {
"costMu": 0,
"plan": "<plan>",
"reason": "<reason>"
},
"latencyMs": "<latencyMs>",
"createdAt": "<createdAt>",
"explain": "<explain>"
}Common errors
401invalid_api_key403insufficient_scope429over the request rate; read Retry-After
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"description": "404 decision_not_found for another key's, another workspace's or a missing id.",
"type": "object",
"required": [
"object",
"id",
"model",
"provider",
"inputTokens",
"outputTokens",
"unit",
"costMu",
"fee",
"latencyMs",
"createdAt",
"explain"
],
"properties": {
"object": {
"const": "generation"
},
"id": {
"type": "string"
},
"model": {
"type": [
"string",
"null"
]
},
"provider": {
"type": [
"string",
"null"
]
},
"inputTokens": {
"type": "integer"
},
"outputTokens": {
"type": "integer"
},
"unit": {
"const": "mu"
},
"costMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"fee": {
"type": [
"object",
"null"
],
"properties": {
"costMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"plan": {
"type": [
"string",
"null"
]
},
"reason": {
"type": [
"string",
"null"
]
}
}
},
"latencyMs": {
"type": [
"integer",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"explain": {
"type": "string",
"description": "the /v1/route/explain/{id} path"
}
}
}Returns one decision: model, provider, tokens, cost in mu, the request-fee line when one was charged, latency, and the path of its explanation (/v1/route/explain/{id}).
Usage
GET /v1/usage
Authentication relay key (bearer)
Example request
curl https://api.massrelay.ai/v1/usage \
-H "Authorization: Bearer $RELAY_KEY"import os, requests
r = requests.get(
"https://api.massrelay.ai/v1/usage",
headers={"Authorization": f"Bearer {os.environ['RELAY_KEY']}"},
)
r.raise_for_status()
print(r.json())const r = await fetch("https://api.massrelay.ai/v1/usage", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.RELAY_KEY}` },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());Response
| Field | Type | Description |
|---|---|---|
object required | string | The kind of object returned, for example chat.completion. Always "usage". |
unit required | string | Always "mu". |
period required | object | |
period.from required | string | |
period.to required | string | |
endUser | string | |
requests required | integer | |
inputTokens required | integer | |
outputTokens required | integer | |
costMu required | integer | micro-dollars, the unit of account |
byEndUser | array of object | |
byEndUser[].endUser required | string | |
byEndUser[].requests required | integer | |
byEndUser[].inputTokens required | integer | |
byEndUser[].outputTokens required | integer | |
byEndUser[].costMu required | integer | micro-dollars, the unit of account |
{
"object": "usage",
"unit": "mu",
"period": {
"from": "<from>",
"to": "<to>"
},
"endUser": "<endUser>",
"requests": 0,
"inputTokens": 0,
"outputTokens": 0,
"costMu": 0,
"byEndUser": [
{
"endUser": "<endUser>",
"requests": 0,
"inputTokens": 0,
"outputTokens": 0,
"costMu": 0
}
]
}Common errors
401invalid_api_key403insufficient_scope429over the request rate; read Retry-After
Every error has the same JSON shape; see Errors and Limits.
Full schema (JSON)
Response schema
{
"description": "Query: from, to (ISO date or datetime, default the UTC calendar month), end_user (one tag), group_by=end_user (one row per tag).",
"type": "object",
"required": [
"object",
"unit",
"period",
"requests",
"inputTokens",
"outputTokens",
"costMu"
],
"properties": {
"object": {
"const": "usage"
},
"unit": {
"const": "mu"
},
"period": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
}
}
},
"endUser": {
"type": "string"
},
"requests": {
"type": "integer"
},
"inputTokens": {
"type": "integer"
},
"outputTokens": {
"type": "integer"
},
"costMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
},
"byEndUser": {
"type": "array",
"items": {
"type": "object",
"required": [
"endUser",
"requests",
"inputTokens",
"outputTokens",
"costMu"
],
"properties": {
"endUser": {
"type": "string"
},
"requests": {
"type": "integer"
},
"inputTokens": {
"type": "integer"
},
"outputTokens": {
"type": "integer"
},
"costMu": {
"type": "integer",
"description": "micro-dollars, the unit of account"
}
}
}
}
}
}Returns the rollup of the key's ledger rows for a period: requests, tokens and cost. from and to take an ISO date or datetime (default: this UTC calendar month). end_user narrows to one end-user tag, and group_by=end_user returns one row per tag.
curl "https://api.massrelay.ai/v1/usage?group_by=end_user" \
-H "Authorization: Bearer mrk_<prefix>_<secret>"Read the rate-limit headers
Every /v1 answer whose request reached the rate limiter carries the state of the limit that binds that key, read from the same evaluation that admitted the request, so a header never costs a second slot.
- **
X-RateLimit-Limit** — requests per minute of the binding limit (the key's, or its workspace's if that is tighter). - **
X-RateLimit-Remaining** — whole requests still admitted right now without being queued. - **
X-RateLimit-Reset** — seconds, rounded up, until one more request would be admitted without queueing (0 while some remain). - **
Retry-After** — on a 429 only: seconds to wait, rounded up.
When no limit is configured for the key or its workspace, the three X-RateLimit-* headers are not sent. The same headers ride on streamed answers and on the 429. See Limits for how limits are applied.