What you can restrict
Each API key on your account carries two optional allowlists configured from the Keys tab:
- Allowed IPs. A list of IPv4 / IPv6 addresses or CIDR ranges. Empty means "any IP".
- Allowed Referer origins. A list of scheme-qualified origins like
https://app.example.com. Empty means "any Referer (or no Referer)".
When either list is non-empty it becomes active. A request that doesn't match is rejected with 403 and a JSON body of {"error":"…","code":"ip_not_allowed"} or "referer_not_allowed". Every denial is logged to the key's activity feed so you can see "this key just got denied 12 times from 203.0.113.5" and react.
How the matching works
- Source IP. Read from the connecting socket, plus the rightmost trusted
X-Forwarded-Forhop (we setapp.set("trust proxy", true)behind our edge). For exact IPs the match is byte-equal; for CIDR ranges any in-range address passes. - Referer. Parsed as a URL; only
scheme + host + portmatter.https://app.example.comin your allowlist matches every page on that origin — but nothttp://, not a different subdomain, not a different port. - Empty allowlist = no restriction. Setting an IP allowlist does not automatically restrict Referer, and vice versa. Both fields default to "off".
Recipe 1 — Server-side service
Your backend calls Provable.io with a long-lived pk_live_… key. The risk model is "the key leaks from logs or a stale env var." Pin the key to your egress IP(s):
- Find your egress IP. From the same host that will make the calls:
curl https://api.ipify.org. - On the dashboard, edit the key. Set Allowed IPs to that single address (or a tight CIDR if your egress fronts multiple machines).
- Leave Allowed Referer origins empty — your backend doesn't send a
Refererheader.
If you're on a cloud provider with rotating egress, prefer a NAT gateway with a static address. Don't try to allowlist a wide 10.0.0.0/8 — that defeats the purpose.
Recipe 2 — Browser app
Calling Provable.io directly from the browser means the key is shipped to every visitor. Restrict the key to the origin that's allowed to use it:
- Edit the key. Set Allowed Referer origins to your production origin (e.g.
https://app.example.com) and your staging/dev origin (https://staging.example.com). - Leave Allowed IPs empty — your users' IPs are arbitrary.
- Use a
pk_test_…key in local dev (which can be allowlisted tohttp://localhost:5173etc.) so a leaked dev key can't drain your live quota.
The Referer guard is a soft control — a hostile client can omit or spoof the header. It does, however, defeat the typical "someone copy-pasted the key out of dev tools and started using it from their own site" attack, which is the realistic threat for browser deployments.
Recipe 3 — Mobile app
Mobile clients don't send a Referer and their IPs are wildcat — neither allowlist helps. The right shape is to not ship a Provable.io key inside the app at all. Stand up a thin proxy you control:
// your-backend.ts
app.get("/draw/:userId", authenticateMobileUser, async (req, res) => {
const r = await fetch(
`https://api.provable.io/api/ints?clientSeed=${encodeURIComponent(req.params.userId)}&count=1&min=1&max=100`,
{ headers: { "x-api-key": process.env.PROVABLE_API_KEY } },
);
res.status(r.status).set("Content-Type", "application/json").send(await r.text());
});
Then apply Recipe 1 to lock the upstream key to your proxy's egress IP. The mobile app authenticates against your backend, never against Provable.io.
Testing changes safely
Allowlist changes apply on the next request — there is no propagation delay. Always test from a real client before deploying:
- Make the change on the dashboard.
- From the host that's supposed to work, hit any cheap endpoint:
curl -H "x-api-key: $KEY" https://api.provable.io/api/floats?clientSeed=allowlist-smoke&count=1. You should get a 200. - From an unrelated host (your laptop on cellular, for example) make the same request. You should get a
403withcode: "ip_not_allowed". - Watch the key's activity on the dashboard — both attempts should be there.
If you lock yourself out
The allowlist guards API traffic, not the dashboard. Sign in at /dashboard from any IP and either:
- Clear the allowlist on the offending key, or
- Rotate the key (one click). The old key dies; the new one starts with empty allowlists you can configure before deploying.
If you can't sign in either (forgotten password), reset it from the login page; allowlists never block account recovery.