Production checklist
Before you ship, the ten things that bite you in production.
Before you flip the switch
These are the ten failure modes we've seen real builders hit between dev and prod. Run through the list before pointing real traffic at your integration.
01. Rotate test keys out
sk_test_… keys authenticate, meter, and rate-limit exactly like live keys: the prefix
exists so you can separate sandbox traffic from production traffic in your own logs and
rotate the two independently. If anything in your prod path still references a test key,
your production usage lands on the key you'll casually revoke during the next experiment.
Grep your secrets manager and CI config.
git grep -n "sk_test_"02. Set a real User-Agent
Identify yourself so we can email you before deprecating something you depend on, and so support requests pull faster traces.
curl -H "User-Agent: yourapp/1.4 (you@yourdomain.com)" ...03. Persist meta.request_id
Capture meta.request_id in your logs for every Statshawk call. When something goes wrong
you'll save a 30-minute back-and-forth: paste the IDs and we pull the traces directly.
04. Retry 5xx with back-off; branch 429 on the error code
502/503 are transient: start at 1 second, double up to a 30-second cap, give up
after 3–5 attempts. A 429 carries one of two codes and they need different handling:
RATE_LIMIT_EXCEEDED means you outpaced your plan's requests-per-second ceiling: honor
the Retry-After header (whole seconds) and continue. QUOTA_EXCEEDED means the monthly
unit cap: retrying won't help; the X-Account-Quota-Reset header carries the period-end
timestamp (RFC3339) it resets at.
05. Watch both header families
X-RateLimit-Remaining is your per-second budget: pace parallel work so it never hits
zero (a burst above your plan's RPS throttles the excess immediately).
X-Account-Quota-Remaining is your monthly unit budget: implement back-pressure or an
upgrade prompt once it drops below 10% of X-Account-Quota-Limit. Watching both is a
thousand times better than getting throttled.
06. Don't retry deterministic errors
A 400, 401, 403, or 404 will return the same result on retry, and so will 501 NOT_SUPPORTED (a capability gap, not an outage). Surface them to the caller; don't loop.
07. Pin to meta.version
We don't bump versions casually, but every breaking change does bump unless the
changelog records an exception. Documented exceptions: the 2026-07-24
pre-GA analysis-surface change, and the 2026-09-11 quota hard-stop (paid self-serve
metered routes now return 429 QUOTA_EXCEEDED at the cap under /v1). If you key on
response shape, log meta.version and watch the changelog for no-bump contract notes.
08. Use feature detection
Before assuming a competition exposes every stat phase or measure, call
/v1/competitions/{comp}/capabilities once and cache the result for an hour. A
501 NOT_SUPPORTED in production is a poor look when the detection call is two lines.
And if you do hit one, its error body carries machine-readable league and capability
fields you can key a fallback off.
09. Cache budgets, not responses
We cache responses on our side. Caching them on yours adds a TTL stale-set you'll have to
debug during a live game. Trust meta.cache instead: HIT is a fresh cached copy, and
STALE means a background refresh is already running, so re-requesting shortly gets the
fresh value.
10. Plan for quota exhaustion
429 QUOTA_EXCEEDED is a hard stop on every metered route for Free through Growth until
the period resets (the X-Account-Quota-Reset header carries the reset timestamp) or you
upgrade. There are no overage charges. Wire upgrade-or-degrade logic into your code path
before launch: degrading gracefully to "tonight's lineups unavailable" beats a 500.