The 30-second summary
secrets.token_bytes() (and the rest of secrets / os.urandom) is Python's OS-backed CSPRNG. It's the right answer for session tokens, password resets, API keys, and any random material that has to be secret.
Provable.io is a different shape: it's an HTTP API that publishes a hash of the server seed before the draw, then reveals it. Anyone with the seed and hash can re-derive the bytes — which is exactly what you need when the audience is outside your process.
Feature matrix
| Capability | Provable.io | secrets.token_bytes() |
|---|---|---|
| Cryptographic strength | HMAC-SHA256 keyed stream | OS CSPRNG (os.urandom) |
| Reproducible from seed | Yes | No |
| Third-party verifiability | Yes — publish seed + hash | No — call is in-process |
| Pre-commitment | Yes | No |
| Latency | ~tens of ms (network) | Sub-microsecond (in-process) |
| Auditable history | Yes — persisted, addressable by short ID | No |
| Dependency | HTTP | Python stdlib |
| Best for | Public-audience draws that need proof | Tokens, API keys, password salts, secret material |
When secrets.token_bytes() is the right answer
Stick with the stdlib CSPRNG when there's no third-party verifier:
- API keys, password reset tokens, OAuth state parameters. Must be unguessable, must stay secret.
- Cryptographic key material. AES, HMAC, RSA seeds.
- Internal random IDs. Job IDs, request IDs, trace IDs.
When Provable.io is the right answer
The moment the result needs to be checked by someone outside your Python process, secrets can't help. Reach for Provable.io when:
- Publishing a raffle winner where entrants want to see proof. See raffle picker.
- Running an experiment whose bucket assignments have to be re-derivable in a year. See A/B bucketing.
- Generating loot drops against published odds. See weighted loot tables.
Try it now
Equivalent to secrets.token_bytes(32).hex() — except the response carries a serverHash anyone can verify.
curl "https://api.provable.io/api/bytes?clientSeed=vs-secrets-token-bytes&count=32&encoding=hex"
FAQ
Can I just hash a secrets.token_bytes() output and publish that?
You can, and you'll get pre-commitment — but you still hold the secret. The verifier has to trust you didn't generate ten candidates and pick a favorable one. Provable.io's seed flow removes that trust by making the input space publicly bound before the draw.
Is the API call as fast as secrets.token_bytes()?
No. An HTTP round trip is tens of milliseconds. If verifiability isn't a requirement, the stdlib call wins every time.
Quickstart for Python?
Yes — see the Python quickstart.