Usage, spending and quotas
Everything your API keys did, what it costs you and how much of each limit is left — per key, project, category, day or hour, in JSON or CSV. The figures come from the same metering and rating as your invoice, so they add up to it to the cent.
Who sees what#
Your key's prefix tells you what it can see:
| Key | Sees |
|---|---|
cma_… account admin key |
every key (revoked ones included) and project of your account, usage recorded before per-key attribution (key: null), account-level lines, plan allowances |
cmr_… standard key |
only its own usage, requests, limits and alerts |
Asking about a key or project you cannot see answers 404, exactly like one
that does not exist. Every response carries X-Cormorant-Key-Role, and
usage responses say what they cover in scope.
The endpoints#
| Endpoint | Use it for |
|---|---|
GET /v1/usage |
Totals and groups (group_by=key,project,category,day,hour) with charges |
GET /v1/usage/timeseries |
The same metrics per hour or day, zero-filled, for charts |
GET /v1/usage/domains |
Your busiest fetched domains (90 days) |
GET /v1/requests |
Every request, newest first, with cursor paging (90 days) |
GET /v1/quota |
Every limit and allowance: used, left, projected, status |
GET /v1/keys, GET /v1/keys/self |
Your keys, their roles and limits |
POST /v1/alerts |
A signed webhook at 50 / 80 / 100 % of a limit |
Details of every field: API contract.
Charges, not costs#
charge is what you pay: your plan's prices (with your contract rates) in
your invoice currency, as a decimal string. billable shows, per price-plan
meter, the measured quantity, the included units it used and the
amount. Cormorant's own provider costs are not part of customer figures.
Base fee, minimum, contract discount and the free-plan credit belong to the
account, not to a key: an account admin key finds them per month under
account. On a free plan you still see list-price charges, followed by the
100 % free line, so you know what your usage is worth.
Fresh and final#
Usage is aggregated every 5 minutes. as_of is the moment up to which every
request is included; a day is final two hours after it ended, and its
figures will not change afterwards. The request ledger is real time — a
request's charge appears once it has been aggregated.
Examples#
Charges per key this month, then per key and day as CSV:
curl -sS "https://api.cormorant.ch/v1/usage?group_by=key" \
-H "Authorization: Bearer $CORMORANT_API_KEY"
curl -sS "https://api.cormorant.ch/v1/usage?from=2026-10-01&to=2026-10-31&group_by=key,day" \
-H "Authorization: Bearer $CORMORANT_API_KEY" -H "Accept: text/csv" -o usage-october.csv
import csv
import io
import os
import httpx
client = httpx.Client(
base_url="https://api.cormorant.ch",
headers={"Authorization": f"Bearer {os.environ['CORMORANT_API_KEY']}"},
)
usage = client.get("/v1/usage", params={"group_by": "key"}).json()
print(f"{usage['totals'].get('charge')} {usage['currency']} as of {usage['as_of']}")
for group in usage["groups"]:
print(group["key"], group["key_role"], group["requests"], group.get("charge"))
# The same as CSV, e.g. for a spreadsheet or pandas.read_csv.
text = client.get(
"/v1/usage",
params={"group_by": "project,category", "from": "2026-10-01", "to": "2026-10-31"},
headers={"Accept": "text/csv"},
).text
rows = list(csv.DictReader(io.StringIO(text)))
Every failed request of a key yesterday, page by page:
params = {"key": "cmr_AbCdEf", "outcome": "failed", "from": "2026-10-18", "to": "2026-10-18"}
cursor = None
while True:
page = client.get("/v1/requests", params={**params, "cursor": cursor} if cursor else params)
body = page.json()
for row in body["requests"]:
print(row["time"], row["domain"], row["error"], row["request_id"])
cursor = body["next_cursor"]
if not cursor:
break
Where do I stand? Each limit has limit, used, remaining,
projected_end_of_period and status (ok, warning from 80 %,
exhausted):
quota = client.get("/v1/quota").json()
for key in quota["keys"]:
for limit in key["limits"]:
print(key["key"], limit["name"], limit["used"], "/", limit["limit"], limit["status"])
for allowance in (quota.get("customer") or {}).get("allowances", []):
print(allowance["name"], allowance["used"], "of", allowance["limit"], "included")
Limits and what happens at them#
The operator can give a key a monthly spend cap, monthly request quotas
(search, fetch, fetch_paid), a rate limit and allowed categories — see
Limits & politeness. Every response tells you
how much is left:
RateLimit-Policy: "project";q=200;w=1, "key";q=10;w=1
RateLimit: "project";r=187;t=1, "key";r=9;t=1
X-Cormorant-Key-Budget-Remaining: 12.40 USD
X-Cormorant-Quota-Remaining: fetch=18766, search=900
At a limit, requests answer 429 key_quota_exhausted or
429 key_budget_exhausted with Retry-After until the month resets — do
not retry those in a loop.
Alerts#
Register a webhook and get an event when a key, project or your account crosses 50, 80 or 100 % of a limit or allowance (once per threshold and month):
curl -sS https://api.cormorant.ch/v1/alerts \
-H "Authorization: Bearer $CORMORANT_API_KEY" -H 'Content-Type: application/json' \
-d '{"url": "https://hooks.example.com/cormorant", "thresholds": [80, 100]}'
Store the secret from the answer: it is shown once. Verify each delivery's
X-Cormorant-Signature (t=<unix>,v1=<hex>, the HMAC-SHA256 of
"<t>.<raw body>") before trusting it — the API contract has a
ready-made function. The same events are listed in GET /v1/quota
(events).
From an agent#
The MCP server's cormorant_usage tool answers "what did I use and what is
left?" in a compact form — see MCP server.