This page describes how the API meters and charges requests. It applies
when your Organization has an active subscription and uses an atk_*
API key.
What counts as a billable request
| Counts | Does not count |
|---|
POST /v1/evaluate | GET /v1/sources |
POST /v1/intersections | GET /v1/rulesets (list/get) |
POST /v1/distance | POST /v1/rulesets (create) |
POST /v1/subjects | ?explain=true (metadata only) |
Catalog reads, ruleset writes, and ?explain=true calls never consume
quota and are never charged.
Per-request gates
Every billable request passes three gates, in order:
- Authentication — the bearer token must be a valid
atk_* key
issued to your Organization.
- Subscription — your subscription must be active. An expired or
suspended subscription returns
402 SUBSCRIPTION_INACTIVE and the
request is not executed.
- Quota — you must be within your contracted monthly cap for the
current billing period. Over the cap returns
429 QUOTA_EXCEEDED
and the request is not executed.
A request that fails any of these gates is not charged.
Billing period
Quota is counted against your Stripe billing period, not against
the calendar month. The boundaries come from your subscription —
typically the day of the month you signed up — and are returned in
the period_started_at / period_ends_at fields on a 429
response. See Quotas & rate limits.
Idempotency
Send an Idempotency-Key header on every billable request:
POST /v1/evaluate
Idempotency-Key: client-job-2026-04-18-7842
Content-Type: application/json
Authorization: Bearer atk_live_...
Format: [A-Za-z0-9_:.-]{8,128}. Anything else returns
422 IDEMPOTENCY_KEY_INVALID.
Keys are scoped per Organization. The contract:
| Outcome | HTTP | Meaning |
|---|
| Same key, same body, charged | 200 | Replay — original response snapshot is returned. |
| Same key, different body | 422 | IDEMPOTENCY_KEY_CONFLICT — pick a new key. |
| Same key, too many retries | 429 | IDEMPOTENCY_KEY_EXHAUSTED — pick a new key. |
| Same key, snapshot expired | 410 | IDEMPOTENCY_REPLAY_EXPIRED — key reusable for a fresh charge. |
Replay returns the original response without re-running the
evaluation and without charging again. This is the safe way to retry
on network errors: the second request either replays the prior result
(no extra charge) or runs fresh (one charge total).
DEGRADED responses are not charged
A response with status: "degraded" means at least one source could
not be evaluated for an infrastructure reason (upstream provider
timeout, transient error). The response is incomplete, not wrong.
By default, DEGRADED responses do not consume quota and are not
charged. The Idempotency-Key stays reusable, so you can retry the
same request once the upstream source recovers and the retry will
either succeed (one charge total) or come back DEGRADED again (still
zero charges).
For the wrappers /v1/intersections, /v1/distance, and
/v1/subjects, a single source that fails is reported with
status: "failed" on its SourceOutcome. The overall response can
still be useful — the other sources returned data — but the request
is not charged.
See Verdicts for the full status/outcome
semantics.
Recommended retry strategy
- Generate a fresh
Idempotency-Key per logical job (a UUID, or your
own job id).
- On
5xx, network error, or status: "degraded": retry with the
same key. You will either replay a charged result, or run fresh.
- On
429 QUOTA_EXCEEDED: stop until period_ends_at or contact us
to bump the cap.
- On
429 RATE_LIMIT_EXCEEDED: back off for Retry-After seconds
with exponential backoff + jitter.
- On
422 IDEMPOTENCY_KEY_CONFLICT or 429 IDEMPOTENCY_KEY_EXHAUSTED:
pick a new key (the original key is locked to a different payload).
Errors
All billing-related errors follow the standard envelope. Match against
the code field, not against title or detail. See
Errors for the complete code list.