Credits & usage
Trace bills the public API in credits. The model is deliberately simple: you pay for company records, once per company, with a free re-access window. Separately, a high-ceiling rate limiter protects the service from abnormal request volume. The two are independent — credits are the product control, the rate limiter is operational backpressure.
Credit metering
Section titled “Credit metering”The unit of billing is a company record:
- 1 credit per net-new company returned, flat. A call that returns 50 companies charges up to 50 credits.
- Re-access within 30 days is free. Once your org has been charged for a
company, returning that same company again within 30 days costs nothing. The
window is keyed by
(your org, company). - Duplicates within one response are charged once. The same company can’t bill twice in a single call.
Which calls are billable:
| Call | What’s charged |
|---|---|
GET /v1/companies/{identifier} |
1 credit for the company returned |
GET /v1/companies (list/search) |
1 credit per company on the returned page |
GET /v1/companies/{identifier}/partnerships |
1 credit per partner company returned |
GET /v1/companies/{identifier}/partnerships/graph |
1 credit per company node in the graph (the queried company itself is free) |
The 30-day window applies to all of these, so a company you already paid for — whether you first saw it via a lookup, a search page, or a partnership result — is free on subsequent calls within the window.
Running out of credits
Section titled “Running out of credits”When your org’s balance can’t cover a call, the request fails with HTTP 402 and no data is returned. The response body:
{ "error": "credit_exhausted", "message": "Insufficient credits to complete this request.", "requested": 50, "shortfall": 12}requested is how many credits the call needed; shortfall is how many you
were short. Top up your balance in the Trace app (Settings → Usage &
Credits) and retry.
Rate limits
Section titled “Rate limits”Authenticated /v1 traffic is governed by a per-key operational limiter — a
safety valve against abnormal volume, set well above normal usage:
- 600 requests per minute per API key
- 100,000 requests per day per API key
These are infrastructure limits, not a product quota. Normal integrations never
hit them. Successful responses carry no X-RateLimit-* headers — they only
appear when you’ve been throttled.
The unauthenticated open endpoints under /v1/open/* have no API key, so they
are rate-limited by client IP instead (a lower ceiling than the
authenticated per-key limits above).
Throttle response and headers
Section titled “Throttle response and headers”When you exceed a limit you get HTTP 429 with a Retry-After header
(seconds to wait) plus the standard rate-limit headers:
{ "error": "operational_rate_limit_exceeded", "message": "Too many requests. Please wait before retrying.", "retry_after_seconds": 30}| Header | Meaning |
|---|---|
Retry-After |
Seconds to wait before retrying. |
X-RateLimit-Limit |
Per-minute request ceiling. |
X-RateLimit-Remaining |
Requests left in the current minute window. |
X-RateLimit-Reset |
Unix timestamp (seconds) when the minute window resets. |
X-RateLimit-Limit-Day |
Per-day request ceiling. |
X-RateLimit-Remaining-Day |
Requests left in the current day window. |
X-RateLimit-Reset-Day |
Unix timestamp (seconds) when the day window resets. |
402 vs 429 — how to react
Section titled “402 vs 429 — how to react”These two failures mean different things and call for different handling:
- 402 (
credit_exhausted) — you’re out of credits. Retrying won’t help until you add credits. Stop, top up in the app, then resume. Theshortfallfield tells you how many you need. - 429 (
operational_rate_limit_exceeded) — you’re sending too fast. Back off and retry. Wait the number of seconds inRetry-After(fall back toretry_after_secondsin the body), then retry. For polling loops, prefer exponential backoff with jitter rather than a tight retry.
A simple backoff loop:
import timeimport httpx
def get_with_backoff(client, url, headers, max_retries=5): for attempt in range(max_retries): resp = client.get(url, headers=headers) if resp.status_code == 429: wait = int(resp.headers.get("Retry-After", "60")) time.sleep(wait) continue if resp.status_code == 402: raise RuntimeError("Out of credits — top up before retrying.") resp.raise_for_status() return resp.json() raise RuntimeError("Rate limited after retries")Next steps
Section titled “Next steps”- Errors — every status code and the exact error shapes.
- Authentication — key classes and headers.
- API Reference (interactive) — note that live calls here consume real credits.