statshawk
Guides

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.

quick grep
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 tripped the per-second cap — back off a second or two and continue. QUOTA_EXCEEDED means the monthly unit cap — retrying won't help; the X-RateLimit-Reset header carries the period-end timestamp (RFC3339) it resets at.

05 — Watch X-RateLimit-Remaining

Implement client-side back-pressure once X-RateLimit-Remaining drops below 10% of X-RateLimit-Limit. It's 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. If you key on response shape, log meta.version and alert when it changes — saves you a 3 a.m. incident.

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

On the free tier, 429 QUOTA_EXCEEDED is a hard stop until the period resets (the X-RateLimit-Reset header carries the reset timestamp). Paid tiers roll past the included allowance into per-unit overage, so a quota 429 there is rare and usually transient. Either way, wire upgrade-or-degrade logic into your code path before launch — degrading gracefully to "tonight's lineups unavailable" beats a 500.

See also

On this page