A lottery API for verifiable number draws

Provable.io is a lottery RNG provider for the draw step of your integration. Use the REST API for scheduled jackpots, daily games, bingo, or high-frequency lottery draws where each result needs its own timestamped, replayable proof. Ticketing, payouts, licensing, and jurisdiction-specific controls stay in your lottery platform.

Lottery API integration for high-frequency draws

Give every round a unique clientSeed and idempotency key, publish its commitment before that round closes, and record the returned outcome permalink. Your integration should schedule rounds, enforce ticket cutoffs, handle retries, and retain each receipt. See the API reference for authentication, idempotency, and rate limits.

What it is

A lottery-grade RNG built on /api/shuffle. You pick the range (1–49 for a classic 6/49, 1–69 for Powerball-style main balls, 1–90 for bingo), the API returns a verifiable draw, and every player can re-derive the numbers from the published seed pair.

No more "the wheel sometimes lands on 7" conspiracy threads. Every ball is a deterministic function of HMAC_SHA256(serverSeed, clientSeed + ":" + nonce + ":" + round).

A draw and its proof

This is what anyone can check. The three values in the steps are all a verifier needs, and the replay button recomputes the draw right here, without calling Provable.

GET /api/shuffle?clientSeed=lottery_draw_2026_05_25&items=1,2,…,49Example record
{
  "clientSeed": "lottery_draw_2026_05_25",
  "nonce": 0,
  "serverHash": "c472d822ddbfb6d5d0e7c7c713ec61ebfd01616fd4effadd9ecbf4f0799d34fd",
  "outcome": ["34", "33", "10", "44", "9", "4", "3", "17", "23", "1", "18", "27", "21", "25", "19", "14", "46", "29", "37", "28", "42", "48", "38", "40", "24", "32", "39", "30", "43", "35", "41", "5", "6", "16", "13", "45", "11", "47", "20", "2", "26", "49", "15", "12", "36", "31", "8", "7", "22"]
}
  1. Before the draw, the server commits to a seedPublish this hash before the draw, so the seed can't be swapped afterwards.serverHash = sha256(serverSeed) = c472d822ddbfb6d5d0e7c7c713ec61ebfd01616fd4effadd9ecbf4f0799d34fd
  2. The draw is a pure function of both seedsHMAC-SHA256(serverSeed, "lottery_draw_2026_05_25:0:0") → Fisher-Yates shuffle of 49 balls, first 6 drawn = [34, 33, 10, 44, 9, 4]
  3. After the draw, the seed is revealed and anyone can replay itserverSeed = 938ce8c85b55cfaec8af00fbea24281f8116a6039a67beb483a67efbc7c29a59

Try it live — draw 6 numbers from 1–49

Shuffle the drum and read the first six balls. A ball can't come out twice, exactly like a real draw.

curl "https://api.provable.io/api/shuffle?clientSeed=lottery_draw_2026_05_25&items=1,2,3,...,49"

Powerball-style: 5 main balls from a drum of 69, plus one bonus ball from a separate drum of 26:

curl "https://api.provable.io/api/shuffle?clientSeed=powerball_2026_05_25_main&items=1,2,3,...,69"
curl "https://api.provable.io/api/ints?clientSeed=powerball_2026_05_25_bonus&count=1&min=1&max=26"

Integration snippet

// 1. When ticket sales close, freeze the ticket list.
// 2. Commit, and publish the serverHash on the draw page header.
const commit = await fetch("https://api.provable.io/api/commit", {
  method: "POST",
  headers: { "x-api-key": process.env.PROVABLE_KEY }
}).then((r) => r.json());

// 3. Reveal within 10 minutes with a clientSeed nobody knew at commit time,
//    e.g. a block hash produced after the commit. Commit-reveal returns
//    integers, so over-draw and keep the first 6 distinct balls. The chance
//    that 20 draws from 1-49 hold fewer than 6 distinct is about 1 in 37 trillion.
const draw = await fetch("https://api.provable.io/api/reveal", {
  method: "POST",
  headers: { "x-api-key": process.env.PROVABLE_KEY, "content-type": "application/json" },
  body: JSON.stringify({
    commitId: commit.commitId,
    clientSeed: `lottery_${drawId}_block_${blockHash}`,
    endpoint: "ints",
    params: { count: 20, min: 1, max: 49 }
  })
}).then((r) => r.json());
const numbers = [...new Set(draw.outcome)].slice(0, 6);

// 4. Publish: numbers, the dedupe rule, serverHash, serverSeed and clientSeed.

Drawing without replacement

Real lotteries don't draw the same ball twice, and independent random integers can repeat: six draws from 1–49 share a number about 27% of the time. The live example above avoids that with a Fisher-Yates shuffle. The integration snippet uses option 2, because commit-reveal returns integers.

  1. Shuffle the drum with /api/shuffle. Pass every ball, keep the first k. Each set of k balls is equally likely, and each ball has the same odds as pulling it from a physical drum without putting balls back. One call, and the replay covers the whole drum.
  2. Over-draw and dedupe. Request more integers than you need and keep the first k distinct values. Also fair, but publish the dedupe rule and how many you requested, or the result can't be replayed.

Why this is fair

Where it fits

Related