Trace docs
One API key. Clean, normalized tracking across supported carrier sources.
Quickstart
Create an account
Sign up and open the dashboard.
Generate a key
Copy your trc_live key from the API keys page.
Test your integration
Start with a sandbox fixture, then use /v1/track for real shipments.
curl https://api.traceapi.dev/v1/sandbox/track \
-H "Authorization: Bearer trc_live_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"scenario":"in_transit"}'Start with fixed sandbox events without lookup quota. Use a real number with known scans when testing /v1/track.
Authentication
Every request goes to the same base URL. Send your API key as a Bearer token and keep live keys on your server, never in public frontend code.
https://api.traceapi.devAuthorization: Bearer trc_live_xxxxxxxxxxxxxTest before tracking live
POST /v1/sandbox/track returns fixed, clearly marked example events. Choose pending, in_transit, out_for_delivery, delivered, exception, or unknown_timezone. It requires an API key and follows your per-minute limit, but uses no monthly lookup quota, browser requests, shipment watches or webhooks.
curl https://api.traceapi.dev/v1/sandbox/track \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scenario":"delivered"}'Responses include test: true, data_freshness.state: simulated and last_checked_at: null. Send only a scenario; real tracking numbers are rejected. Both trc_live_ and trc_test_ keys use real sources and lookup quota on /v1/track. The endpoint selects sandbox mode.
/v1/track
Track one shipment. Trace detects selected number formats (or accepts a carrier override), checks that a source page contains the requested number, and parses dated events into a normalized response. A 200 response is not independent confirmation from the carrier; verify important or disputed results against the carrier. If no usable events are found, the API returns 503 rather than inventing a pending result.
{ "tracking_number": "YT2412345678" }{
"tracking_number": "YT2412345678",
"carrier": {
"code": "yunexpress",
"name": "YunExpress",
"type": "dropshipping"
},
"status": "in_transit",
"estimated_delivery": null,
"events": [
{
"status": "in_transit",
"description": "Departed origin facility",
"location": "Shenzhen, China",
"timestamp": "2026-07-03T09:30:00.000Z"
}
],
"cached": false,
"request_id": "req_abc123",
"last_checked_at": "2026-07-03T10:00:00.000Z"
}For an outage fallback, add allow_stale: true to the request body. Trace first tries a live lookup after the normal 30-minute cache expires. If sources fail, it can return a verified snapshot younger than 24 hours with stale: true and the original check time. No shipment notifications or watch changes result from stale data. Successful stale responses count toward your lookup quota.
Response fields
Successful tracking responses use the same shape for fresh and cached lookups. By default, a source failure returns a retryable 503 error. Opt in to allow_stale to receive a clearly marked previously verified snapshot during outages. Source-page data can still be stale or incorrect.
| Field | Type | Description |
|---|---|---|
| tracking_number | string | The tracking number exactly as submitted (trimmed). |
| carrier.code | string | Stable carrier identifier, e.g. yunexpress or ups. See carrier codes below; unknown when no pattern matches. |
| carrier.name | string | Human-readable carrier name. |
| carrier.type | string | premium, standard, domestic, regional, dropshipping, freight, or unknown. |
| status | string | Normalized shipment status. One of the statuses listed below. |
| estimated_delivery | string | null | ISO 8601 date when the carrier provides one, otherwise null. |
| events | array | Normalized tracking history. Successful responses contain at least one dated event parsed from a source page containing the requested number. |
| events[].status | string | Per-event status using the same enum as status. |
| events[].description | string | Human-readable event description derived from the source. |
| events[].location | string | null | City / country when available, otherwise null. |
| events[].timestamp | string | null | UTC ISO 8601 scan time, or null when the source does not supply a verifiable timezone. |
| events[].timestamp_local | string (optional) | Source date/time without an offset, present when timestamp is null. Display as written with 'timezone not supplied'; do not convert it to UTC. |
| events[].timestamp_timezone | string (optional) | unknown when timestamp is null. |
| events[].timestamp_precision | string (optional) | minute or second: precision of timestamp_local. Events with unknown timezones retain the source's history order. |
| cached | boolean | true when a verified cache snapshot was used. |
| data_freshness | object | state: live, cached, or stale; age_seconds since the last verified check; cache_ttl_seconds: 1800. |
| stale | boolean (optional) | true only when allow_stale: true was requested and sources failed. The previous verified snapshot must be younger than 24 hours. It does not create notifications or update watches. Successful stale responses count as lookups. |
| request_id | string | Unique id for this request; batch calls share one id across results. |
| last_checked_at | string | ISO 8601 time the upstream lookup last ran. |
pending Label created; the carrier has not received the package yet.
in_transit Package is moving through the carrier network.
out_for_delivery On the vehicle for final delivery.
customs Held in customs or clearance.
delivered Delivered.
exception Delivery problem: failed attempt, held, returned, or customs delay./v1/track/batch
Track up to 20 shipments in one request. Trace reserves quota for the full batch before starting, then charges only successful items, including cache hits and explicitly allowed stale results. Failed items release their reservation. Mixed success returns HTTP 200 with item errors; an entirely failed batch returns HTTP 503 with its results array. Allow 90 seconds per lookup; a large synchronous batch can take longer because work is bounded.
{
"tracking_numbers": [
"YT2412345678",
"1Z999AA10123456784"
]
}Carrier codes
This is the catalog of accepted carrier codes, not a promise of automatic detection or live coverage for every entry. Detection currently recognizes selected formats; send a carrier override for ambiguous or unrecognized numbers. An unknown number can be looked up only when a source provides verifiable events.
Prefixes below are catalog metadata, not a complete list of implemented detection rules. “No prefix listed” does not imply that Trace can identify the carrier from a numeric number alone.
| Code | Carrier | Type | Prefixes | Regions |
|---|---|---|---|---|
| yunexpress | YunExpress | dropshipping | YT | CN to US, CN to EU, CN to AU |
| epacket | ePacket (China Post) | dropshipping | LX, LY, LZ | CN to Global |
| 4px | 4PX | dropshipping | 4PX, FPXE | CN to US, CN to EU |
| cainiao | Cainiao | dropshipping | CAIN, LP | CN to Global |
| cjpacket | CJ Packet | dropshipping | CJPAK | CN to US, CN to EU |
| yanwen | Yanwen | dropshipping | YW | CN to Global |
| sfexpress | SF Express | dropshipping | SF | CN to Global |
| jtexpress | J&T Express | dropshipping | JT | SEA to Global |
| suntek | SunTek Express | dropshipping | STK | CN to US |
| dhl | DHL Express | premium | JD | Global |
| dhlecommerce | DHL eCommerce | standard | GM | Global |
| tnt | TNT | premium | GE | Global |
| aramex | Aramex | standard | ARX | Middle East to Global |
| usps | USPS | domestic | 9, LZ, EC | US |
| fedex | FedEx | premium | 7 | US, Global |
| ups | UPS | premium | 1Z | US, Global |
| royalmail | Royal Mail | domestic | AA, ZZ | UK |
| dpd | DPD | domestic | No prefix listed | EU |
| gls | GLS | domestic | No prefix listed | EU |
| hermes | Evri (Hermes) | domestic | No prefix listed | UK |
| postnl | PostNL | domestic | 3S | NL, EU |
| deutschepost | Deutsche Post | domestic | RR | DE, EU |
| laposte | La Poste | domestic | CB | FR, EU |
| correos | Correos (Spain) | domestic | PQ | ES, EU |
| canadapost | Canada Post | domestic | No prefix listed | CA |
| australiapost | Australia Post | domestic | 33, 99 | AU |
| japanpost | Japan Post | domestic | EJ, RJ | JP |
| indiapost | India Post | domestic | EE, RR | IN |
| ninjavan | Ninja Van | regional | NV | SEA |
| correiosbr | Correios (Brazil) | domestic | SS, OB | BR |
| ontrac | OnTrac | regional | C | US |
| lasership | LaserShip | regional | 1LS | US |
| chronopost | Chronopost | premium | X | FR, EU |
| colissimo | Colissimo | domestic | 6A, 6C, 8N | FR, EU |
| mondialrelay | Mondial Relay | regional | No prefix listed | FR, BE, ES, EU |
| bpost | bpost | domestic | CD, CE | BE, EU |
| postnord | PostNord | domestic | No prefix listed | DK, SE, NO, FI |
| bring | Bring | domestic | No prefix listed | NO, Nordics |
| swisspost | Swiss Post | domestic | 99 | CH, EU |
| austrianpost | Austrian Post | domestic | No prefix listed | AT, EU |
| posteitaliane | Poste Italiane | domestic | IT | IT, EU |
| inpost | InPost | regional | No prefix listed | PL, UK, EU |
| packeta | Packeta | regional | Z | CZ, SK, EU |
| yodel | Yodel | domestic | JD | UK |
| parcelforce | Parcelforce | premium | PB, PN | UK, Global |
| anpost | An Post | domestic | No prefix listed | IE |
| seur | SEUR | regional | No prefix listed | ES, PT |
| mrw | MRW | regional | No prefix listed | ES, PT |
| ctt | CTT Portugal Post | domestic | DA, LX | PT |
| ptt | PTT Posta | domestic | RR, CP | TR |
| israelpost | Israel Post | domestic | No prefix listed | IL |
| saudipost | Saudi Post | domestic | No prefix listed | SA |
| emiratespost | Emirates Post | domestic | No prefix listed | AE |
| qatarpost | Qatar Post | domestic | No prefix listed | QA |
| singaporepost | Singapore Post | domestic | RC, RF, RR | SG, Global |
| hongkongpost | Hongkong Post | domestic | RB, RC | HK, Global |
| thailandpost | Thailand Post | domestic | EE, RR | TH |
| vietnampost | Vietnam Post | domestic | No prefix listed | VN |
| poslaju | Pos Laju | domestic | EL, ER | MY |
| phlpost | PHLPost | domestic | No prefix listed | PH |
| koreapost | Korea Post | domestic | EG, RR | KR, Global |
| taiwanpost | Taiwan Post | domestic | No prefix listed | TW |
| nzpost | New Zealand Post | domestic | NZ | NZ |
| chinapost | China Post | standard | RA, RB, RC, EA, EB | CN, Global |
| ems | EMS | standard | E | Global |
| speedaf | Speedaf | regional | SP | CN, Africa, Global |
| delhivery | Delhivery | domestic | No prefix listed | IN |
| bluedart | Blue Dart | premium | No prefix listed | IN |
| ekart | Ekart | domestic | FMPC, FMPP | IN |
| dtdc | DTDC | domestic | No prefix listed | IN |
| xpressbees | XpressBees | domestic | No prefix listed | IN |
| aramexau | Aramex Australia | regional | No prefix listed | AU |
| fastway | Fastway | regional | No prefix listed | AU, IE, NZ |
| toll | Toll | regional | No prefix listed | AU, APAC |
| startrack | StarTrack | premium | No prefix listed | AU |
| purolator | Purolator | premium | No prefix listed | CA |
| canpar | Canpar | regional | No prefix listed | CA |
| tforce | TForce Freight | regional | No prefix listed | US, CA |
| estes | Estes | freight | No prefix listed | US |
| xpo | XPO | freight | No prefix listed | US, EU |
| yrc | YRC Freight | freight | No prefix listed | US |
| olddominion | Old Dominion Freight Line | freight | No prefix listed | US |
| rlcarriers | R+L Carriers | freight | No prefix listed | US |
| saia | Saia | freight | No prefix listed | US |
| estafeta | Estafeta | domestic | No prefix listed | MX |
| redpack | Redpack | domestic | No prefix listed | MX |
| chilexpress | Chilexpress | domestic | No prefix listed | CL |
| servientrega | Servientrega | regional | No prefix listed | CO, EC |
| urbano | Urbano | regional | No prefix listed | AR, PE |
Webhooks
Add an HTTPS endpoint in the dashboard. Trace durably queues a signed event after a verified lookup, including a cached retry when the same event was not already queued. Delivery is asynchronous and retried on transient failure; it can happen more than once, so deduplicate by Trace-Event-Id. The dashboard test button sends a one-shot test. Active shipments receive best-effort background refreshes while the worker is enabled. Keep a fallback lookup path: source availability and delivery timing are not guaranteed.
| GET /v1/webhooks | List your endpoints. |
| POST /v1/webhooks | Create a public HTTPS endpoint with supported event subscriptions. |
| DELETE /v1/webhooks/:id | Remove an endpoint. |
| GET /v1/webhooks/:id/deliveries | Page saved delivery outcomes with limit and cursor. |
| POST /v1/webhooks/:id/test | Send one signed test. Check ok and status_code even when Trace returns HTTP 200. |
| POST /v1/webhooks/:id/rotate-secret | Rotate with a 24-hour signing overlap; update receivers to accept multiple v1 signatures first. |
Use your API key for these routes. Receivers must accept signed POST requests at the final HTTPS URL, without login redirects. Trace does not forward credentials or follow redirects to customer destinations. Delivery history supports cursor pagination. Inspect /:id/deliveries/:deliveryId/attempts; POST /:id/deliveries/:deliveryId/replay with a reason to explicitly replay a finished event. Replay preserves event IDs and has a 60/hour account limit. PATCH /:id pauses/resumes an endpoint or updates subscriptions.
tracking.updated
tracking.delivered
tracking.exception
shipment.updated
shipment.delivered
shipment.alert{
"event_id": "evt_abc123",
"event_type": "tracking.updated",
"created_at": "2026-07-03T10:00:00.000Z",
"tracking": {
"tracking_number": "YT2412345678",
"status": "in_transit",
"carrier": {
"code": "yunexpress",
"name": "YunExpress"
},
"events": []
}
}Trace-Event: tracking.updated
Trace-Event-Id: evt_example
Trace-Timestamp: 1783072800
Trace-Signature: t=1783072800,v1=signature_hashimport crypto from "node:crypto";
const header = req.headers["trace-signature"] || "";
const parts = header.split(",").map(part => part.split("="));
const times = parts.filter(([key]) => key === "t");
const timestamp = times.length === 1 ? times[0][1] : "";
const expected = crypto.createHmac("sha256", webhookSecret)
.update(`${timestamp}.${rawBody}`).digest();
const trusted = /^\d+$/.test(timestamp) &&
req.headers["trace-timestamp"] === timestamp &&
Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300 &&
parts.some(([key, value]) => key === "v1" &&
/^[a-f0-9]{64}$/.test(value || "") &&
crypto.timingSafeEqual(Buffer.from(value, "hex"), expected));
// Store Trace-Event-Id and ignore repeats after verification.Tracking playground
Start with a simulated shipment or choose a live lookup. Your key is sent only to the Trace API.
Fixed example events. This makes no carrier requests, creates no shipment watches and sends no webhooks. Normal authentication and per-minute limits apply.
Client snippets
Copy-paste examples for your stack — no Trace-specific dependency required, and every language uses the same REST request shape.
export async function trackPackage(apiKey, trackingNumber) {
const response = await fetch("https://api.traceapi.dev/v1/track", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ tracking_number: trackingNumber }),
signal: AbortSignal.timeout(90000)
});
if (!response.ok) throw new Error("Trace request failed");
return response.json();
}import requests
def track_package(api_key, tracking_number):
response = requests.post(
"https://api.traceapi.dev/v1/track",
headers={"Authorization": f"Bearer {api_key}"},
json={"tracking_number": tracking_number},
timeout=90,
)
response.raise_for_status()
return response.json()Asynchronous tracking jobs
POST /v1/tracking-jobs accepts 1–20 tracking numbers and a required Idempotency-Key. HTTP 202 returns an owned job and Location header; GET /:id reports progress and GET /:id/results pages individual outcomes. Unlike imports, these jobs perform lookups without creating monitored shipment records.
const { job } = await trace.createTrackingJob({
tracking_numbers: ["YOUR_NUMBER", "ANOTHER_NUMBER"],
carrier: "ups",
allow_stale: false
}, "your-stable-batch-key");
const progress = await trace.trackingJob(job.id);
const page = await trace.trackingJobResults(job.id, { after_row: 0 });
// Re-fetch from after_row:0 while outcomes are pending.
// next_row is pagination, not a completed-result stream.
// await trace.cancelTrackingJob(job.id);At most two unfinished jobs and 1,000 retained jobs per account. Each job expires after 60 minutes. Quota is reserved per claimed item; submission does not guarantee available quota. A persisted successful result, including an explicitly requested cached/stale result, consumes one lookup. Failures and cancelled uncommitted items release reservations. Cancellation preserves completed results and fences late completion; an already-started source request may finish. Terminal results and idempotency are retained for at least seven days, then eligible for cleanup; a removed key may create a new job.
Inspect every row: queued, processing, succeeded, failed or cancelled. A completed job can contain failed items. Only retry the necessary failed work deliberately with a new key. SDKs retry confirmed HTTP 429/503 rejections of supported idempotent creates; they surface ambiguous transport failures.
Revisions, evidence and event identity
Owned shipment snapshots include an increasing revision. Shipment webhooks include shipment.revision and shipment_revision; update a stored projection only when the incoming revision is newer. Deduplicate event_id independently. Source scan timestamps and webhook arrival order are not snapshot versions. For older payloads without revisions, fetch the authoritative shipment through your backend instead of guessing order.
Normalized scans include stable IDs under a versioned normalization contract. data_quality reports scan counts, explicit UTC versus unknown-time counts, source-reported evidence and unknown history completeness. A new content identity is not proof of an additional physical scan. Data quality does not independently verify a delivery or promise complete carrier history. Shipment diagnostics distinguish awaiting a first observation, retrieval failure, quota postponement, monitoring and closure. Preserve null UTC timestamps and source-local metadata.
Action feed workflows support PATCH /v1/alerts/:id with owner_label, POST /:id/resolve with a required note, and GET /:id/history with cursor pagination. Assignment labels do not create team membership. Human resolution records an investigation; active remains the underlying rule condition. A new recurrence after verified clearance reopens the workflow. Acknowledge, assign and resolve are explicit mutations, not automatic retries.
Observed lookup reliability
GET /v1/analytics/reliability?days=7 reports only your account’s recorded lookup outcomes. The 1–90 day window separates live, cached, stale and failed results by origin and carrier, with sample counts and median/p95 duration. Recording starts at deployment; gaps are possible and historical data is not invented. This is not a carrier accuracy benchmark, an independent completeness check or a freshness SLA. Source qualification remains separate.
Use the reliability dashboard for account outcomes and the free developer tools for local fixtures, UI validation and webhook diagnostics.
Shipments & order records
Create a tenant-owned record for each shipment cycle. Reusing a tracking number with a new Idempotency-Key creates a separate record; reusing a key with a changed body returns 409. Order references and metadata stay separate from upstream carrier queries.
curl https://api.traceapi.dev/v1/shipments \
-H "Authorization: Bearer $TRACE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-parcel-1" \
-d '{"tracking_number":"YOUR_NUMBER","carrier":"ups",
"order_id":"1042","reference":"Order #1042",
"destination_country":"US","metadata":{"channel":"store"}}'HTTP 201 creates a record; an identical retry returns 200 with replayed=true. Initial status pending is not verified carrier data. GET /v1/shipments/:id retrieves the record; GET /v1/shipments supports order_id, status, limit and cursor. GET /:id/events pages immutable scans by observation time. PATCH /:id updates details or stops monitoring with active=false. Tracking identity is immutable; a closed record needs a new cycle to resume.
Action feed
GET /v1/alerts returns active actions; active=false includes resolved history. Carrier exception rules preserve the latest scan as evidence and suggest an action for customs, address, delivery-attempt, return or general exceptions. kind=carrier_fact attributes the source's reported exception; kind=trace_inference marks Trace's no-recent-scan rule.
No-scan inference requires a successful verified observation with UTC instants for every scan and at least stalled_after_hours (default 72, range 24–720) since the latest scan. Unknown local times and failed/stale lookups never establish a stall. POST /v1/alerts/:id/acknowledge records acknowledgement without resolving the carrier condition. Alerts resolve when subsequent verified evidence no longer supports the rule. These rules are investigation aids, not guaranteed delivery predictions.
Durable imports
POST /v1/imports accepts either rows (1–1,000 shipment inputs) or csv text (up to 750 KB), with an Idempotency-Key. It returns HTTP 202 and an owned job ID. GET /v1/imports/:id reports progress; GET /:id/results uses after_row and limit for per-row record IDs or errors. CSV columns: tracking_number, carrier, order_id, reference, destination_country. Quoted commas and newlines are supported.
{
"rows": [
{"tracking_number":"YOUR_NUMBER","carrier":"ups","order_id":"1042"},
{"tracking_number":"ANOTHER_NUMBER","carrier":"dhl","order_id":"1043"}
]
}There are at most two unfinished imports per tenant. Processing uses bounded fair chunks and recoverable leases. Completed rows are idempotent across restarts. POST /:id/cancel stops remaining rows and preserves completed records. Import success means record creation, not successful carrier acquisition; inspect the shipment's verified data and monitoring error separately. Acquisition is bounded to two concurrent jobs in this single API replica and uses the separate browser service.
Merchant analytics
GET /v1/analytics?days=30 returns account-owned carrier/destination cohorts. days is 1–90; format=csv downloads the cohort table. The denominator includes all records created in the window, including parcels without verified events. utc_duration_samples and excluded_delivered_durations accompany the median observed transit hours. Durations require transit and delivered scans and verified UTC for every scan; they exclude unknown timezones and incomplete histories. This is observed scan history, not purchase-to-delivery time, an SLA or a carrier ranking. Reduce the window if it contains more than 20,000 records.
Official clients · 0.3.0
Server-side TypeScript (Node 22+) and Python (3.11+) clients use models generated from the public OpenAPI contract. Install the versioned downloads below. Try the React timeline and Next.js starter at traceapi.dev/tracking-timeline. The source is open on GitHub and these pinned downloads work today. npm/PyPI registry publication is pending the maintainer accounts.
npm install https://traceapi.dev/downloads/traceapi-sdk-0.3.0.tgz
import { Trace, verifyWebhook } from "@traceapi/sdk";
const trace = new Trace(process.env.TRACE_API_KEY);
const { shipment } = await trace.createShipment({
tracking_number: "YOUR_NUMBER", order_id: "1042"
}, "order-1042-parcel-1");
// verifyWebhook(rawBody, signatureHeader, signingSecret)
// Always use raw bytes and deduplicate the verified event ID.pip install https://traceapi.dev/downloads/traceapi_sdk-0.3.0-py3-none-any.whl
import os
from traceapi import Trace, verify_webhook
trace = Trace(os.environ["TRACE_API_KEY"])
result = trace.create_shipment({
"tracking_number": "YOUR_NUMBER", "order_id": "1042"
}, idempotency_key="order-1042-parcel-1")Defaults: 95-second timeout per attempt, up to two retries for HTTP 429/503 on reads or supported idempotent creates, respecting Retry-After up to 60 seconds. Transport failures and non-idempotent actions (including replay/tests/rotation) are surfaced without automatic retries. TraceError keeps the status, code, request ID and retry header. Mixed batch outcomes and null timestamps are preserved. Signature helpers accept any matching v1 signature with five-minute tolerance. Do not put API keys in browser bundles. Version 0.x may have breaking changes: pin the SDK version, review release notes and migrate deliberately. Additive API fields may appear in /v1; clients must tolerate them.
Limits and quotas
| Plan | Requests / minute | Monthly lookups |
|---|---|---|
| Free | 10 | 200 |
| Startup | 60 | 5,000 |
| Business | 120 | 25,000 |
| Scale | 300 | 100,000 |
Paid billing is currently disabled. Listed plan limits apply to assigned accounts; paid checkout will become available after Stripe is configured and verified.
Paid quotas reset on the confirmed Stripe subscription period boundary, exposed in X-Monthly-Reset. Free and manually assigned accounts use UTC calendar months. Existing Free accounts keep their previous allowance through the end of the next calendar month after the plan update; Billing shows the transition date. Successful direct, cached, stale opt-in, automatic refresh and async job results share one allowance. Sandbox scenarios and failed lookups consume no lookup credits. Upgrades retain the current period's usage, and credits do not roll over. A delayed billing-period sync returns 503 billing_period_pending; retry later without resubscribing.
Errors
Every error returns the same shape: { "error": { "code", "message" } } with one of the codes below.
unauthorized401Missing or invalid API key.
invalid_request400Required request data is missing or malformed.
rate_limited429Per-minute request limit exceeded.
admission_unavailable503Shared request admission is temporarily unavailable. Honor Retry-After before retrying.
monthly_quota_exceeded402Monthly lookup quota exceeded for the current plan.
tracking_sources_unavailable503No tracking source returned usable data; retry shortly.
browser_queue_*503Browser capacity, worker readiness or acquisition deadline is temporarily unavailable. Honor Retry-After.
internal_error500Trace could not complete the request.
Health check
{ "ok": true, "service": "trace-api" }Health checks cover the database and, in queue mode, a recent heartbeat from the separate browser service. A missing dependency returns HTTP 503. This confirms service readiness, not whether a carrier website will return accurate events. /v1/status also reports the deployed version and acquisition readiness.