$ man urandom

Docs.

Everything urandom.ai exposes — endpoints per surface, the check protocol, the entropy pipeline, and security posture. One page, no framework.

Console → Examples Goals & stories MCP

Surfaces

Core

urandom.ai

Secure bytes, ints, UUIDs, passwords, entropy health.

Check

check.urandom.ai

Is anybody using this key? Answered without revealing it.

PQC

pqc.urandom.ai

ML-KEM-768 & ML-DSA-65, crypto-agile.

Lab

lab.urandom.ai

Aperiodic-≠-random exhibit + stats.

Auth & rate limits

Only /v1/pqc/* needs a key (it mints key material and is CPU-heavier). random, entropy, lab, and check are public.

# self-service key — no signup, no identity; 20 per IP per day; shown once
POST /v1/keys                         # -> {"api_key": "..."}

curl -XPOST 'https://urandom.ai/v1/keys'

Send it as Authorization: Bearer <key> or X-API-Key. Keys are stored only as hashes. Every client is rate-limited (≈300 req/min); surfaces are routed by Host.

Core endpoints

# cryptographically secure randomness (SP 800-90A Hash_DRBG, OS-seeded)
GET  /v1/random/bytes?n=32           # {"hex": "..."}  (1..=4096)
GET  /v1/random/int?min=1&max=6       # {"value": n}    unbiased
GET  /v1/random/uuid                  # {"uuid": "..."} v4
GET  /v1/random/password?length=24    # {"password": "..."}
GET  /v1/random/passphrase?words=6    # {"passphrase":"...","bits":77.5}  diceware (EFF 7776)
GET  /v1/entropy/health?samples=8192  # SP 800-90B RCT + APT + bit balance

curl 'https://urandom.ai/v1/random/bytes?n=16'

PQC endpoints

POST /v1/pqc/ml-kem-768/keypair[?register=true]   # FIPS 203 KEM keypair (hex)
POST /v1/pqc/ml-dsa-65/keypair[?register=true]    # FIPS 204 signing keypair
POST /v1/pqc/ml-dsa-65/sign     {secret_key,message}   # {algorithm,signature}
POST /v1/pqc/ml-dsa-65/verify   {public_key,message,signature}  # {valid}

curl -XPOST 'https://pqc.urandom.ai/v1/pqc/ml-kem-768/keypair'

Every response is tagged with its algorithm so a broken primitive can be retired without changing call sites. Hybrid X25519MLKEM768 is in the urandom-pqc library (HKDF-SHA256), not an HTTP endpoint.

Lab endpoint

GET /v1/lab/aperiodic?n=10000   # Fibonacci word: never repeats, yet fails monobit
                                # -> proportion_of_ones, monobit_statistic, predictable:true

The check protocol — reveal nothing

A verifiable OPRF (RFC 9497, ristretto255-SHA512, VOPRF mode). The server never sees your key or its hash — only a random-looking blinded point — and proves (DLEQ) it answered with the committed key.

fingerprint = SHA-256(secret)            # local, never sent
B = r · H(fingerprint)                    # blinded point — the ONLY thing sent
POST /v1/check/evaluate {blinded:B}       # -> {evaluated:E=k·B, proof, public_key, epoch}
verify DLEQ proof, then y = unblind(E)    # abort if the server cheated
GET  /v1/check/set                        # opaque registry set (ETag / 304); y in set?
POST /v1/check/register {y}               # add y (warns future checkers; we never see the key)
GET  /v1/check/params                     # ciphersuite, mode, public_key, epoch

Run it three ways: the browser demo at /check, the CLI (urandom check <key|file>), or the MCP tool (check_key_exposure). The registry is opt-in (keys you register, or generated with ?register=true) — not a global breach corpus.

Examples — every call, four ways

# 1. mint an API key — no signup, 20/IP/day. Needed only for /v1/pqc/*.
KEY=$(curl -s -XPOST https://urandom.ai/v1/keys | jq -r .api_key)

# 2. public endpoints need no key
curl -s 'https://urandom.ai/v1/random/bytes?n=16'
curl -s 'https://urandom.ai/v1/entropy/health?samples=8192'

# 3. ML-DSA-65 (FIPS 204) keypair -> sign -> verify (PQC needs the key)
H="Authorization: Bearer $KEY"; J="Content-Type: application/json"; B=https://pqc.urandom.ai
kp=$(curl -s -XPOST $B/v1/pqc/ml-dsa-65/keypair -H "$H")
pub=$(jq -r .public_key <<<"$kp"); sec=$(jq -r .secret_key <<<"$kp")
sig=$(curl -s -XPOST $B/v1/pqc/ml-dsa-65/sign -H "$H" -H "$J" \
  -d "{\"secret_key\":\"$sec\",\"message\":\"hello\"}" | jq -r .signature)
curl -s -XPOST $B/v1/pqc/ml-dsa-65/verify -H "$H" -H "$J" \
  -d "{\"public_key\":\"$pub\",\"message\":\"hello\",\"signature\":\"$sig\"}"   # {"valid":true}
# pip install requests
import requests

# 1. mint an API key — no signup, 20/IP/day. Needed only for /v1/pqc/*.
key = requests.post("https://urandom.ai/v1/keys").json()["api_key"]

# 2. public endpoints need no key
requests.get("https://urandom.ai/v1/random/bytes", params={"n": 16}).json()
requests.get("https://urandom.ai/v1/entropy/health", params={"samples": 8192}).json()

# 3. ML-DSA-65 (FIPS 204) keypair -> sign -> verify (PQC needs the key)
h, B = {"Authorization": f"Bearer {key}"}, "https://pqc.urandom.ai"
kp = requests.post(f"{B}/v1/pqc/ml-dsa-65/keypair", headers=h).json()
sig = requests.post(f"{B}/v1/pqc/ml-dsa-65/sign", headers=h,
                    json={"secret_key": kp["secret_key"], "message": "hello"}).json()["signature"]
print(requests.post(f"{B}/v1/pqc/ml-dsa-65/verify", headers=h,
                    json={"public_key": kp["public_key"], "message": "hello", "signature": sig}).json())
# {'valid': True}
// 1. mint an API key — no signup, 20/IP/day. Needed only for /v1/pqc/*.
const { api_key: KEY } = await (await fetch("https://urandom.ai/v1/keys", { method: "POST" })).json();

// 2. public endpoints need no key
await (await fetch("https://urandom.ai/v1/random/bytes?n=16")).json();
await (await fetch("https://urandom.ai/v1/entropy/health?samples=8192")).json();

// 3. ML-DSA-65 (FIPS 204) keypair -> sign -> verify (PQC needs the key)
const B = "https://pqc.urandom.ai";
const H = { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" };
const post = (p, b) => fetch(B + p, { method: "POST", headers: H, body: b && JSON.stringify(b) }).then((r) => r.json());
const kp = await post("/v1/pqc/ml-dsa-65/keypair");
const { signature } = await post("/v1/pqc/ml-dsa-65/sign", { secret_key: kp.secret_key, message: "hello" });
console.log(await post("/v1/pqc/ml-dsa-65/verify", { public_key: kp.public_key, message: "hello", signature }));
// { valid: true }
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
use serde_json::{json, Value};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let http = reqwest::blocking::Client::new();

    // 1. mint an API key — no signup, 20/IP/day. Needed only for /v1/pqc/*.
    let minted: Value = http.post("https://urandom.ai/v1/keys").send()?.json()?;
    let auth = format!("Bearer {}", minted["api_key"].as_str().unwrap());

    // 2. public endpoints need no key
    http.get("https://urandom.ai/v1/random/bytes?n=16").send()?.json::<Value>()?;
    http.get("https://urandom.ai/v1/entropy/health?samples=8192").send()?.json::<Value>()?;

    // 3. ML-DSA-65 (FIPS 204) keypair -> sign -> verify (PQC needs the key)
    let b = "https://pqc.urandom.ai";
    let kp: Value = http.post(format!("{b}/v1/pqc/ml-dsa-65/keypair"))
        .header("Authorization", &auth).send()?.json()?;
    let (sk, pk) = (kp["secret_key"].as_str().unwrap(), kp["public_key"].as_str().unwrap());
    let signed: Value = http.post(format!("{b}/v1/pqc/ml-dsa-65/sign")).header("Authorization", &auth)
        .json(&json!({ "secret_key": sk, "message": "hello" })).send()?.json()?;
    let sig = signed["signature"].as_str().unwrap();
    let ok: Value = http.post(format!("{b}/v1/pqc/ml-dsa-65/verify")).header("Authorization", &auth)
        .json(&json!({ "public_key": pk, "message": "hello", "signature": sig })).send()?.json()?;
    println!("{ok}"); // {"valid":true}
    Ok(())
}

Heads-up: the sign endpoint takes your secret_key over the wire and keypair mints it server-side — handy for testing, but for real signing keep the secret on your machine: use the urandom-pqc Rust crate (or the WASM build) and never send it.

Entropy & randomness

Security posture