What it is

A raffle picker built on the /api/pick and /api/ints endpoints. You hand it the list of entries (or a count of numbered entries); it returns the winner, a deterministic seed trail, and a serverHash that anyone can paste into /verify to confirm the draw wasn't reshuffled after the fact.

Optional weights let you run weighted raffles (e.g. extra entries for paid tiers, loyalty multipliers) without inventing your own RNG or asking entrants to trust your math.

The pain point

"Congrats to @whoever!" is not a winner announcement people trust anymore. Communities want receipts: a public commitment before entries close, a clear rule for how the draw mapped entrants to numbers, and a way to re-run the calculation independently. Building that yourself is doable but tedious; the raffle picker API gives you all three out of the box.

Try it live — pick a winner from a list

curl "https://api.provable.io/api/pick?clientSeed=giveaway_2026_05_24&items=alice,bob,carol,dave,eve,frank"

Same call with weighted entries (paid tier gets 3× the odds):

curl "https://api.provable.io/api/pick?clientSeed=giveaway_2026_05_24_weighted&items=free_user,free_user_2,paid_user&weights=1,1,3"

Or pick by entry number

If you have thousands of entries, don't ship the whole list — number them 1..N and draw an integer:

curl "https://api.provable.io/api/ints?clientSeed=giveaway_2026_05_24_by_number&count=1&min=1&max=1750"

Integration snippet

// 1. Before entries open, fetch /api/commit and publish the serverHash
// (Discord post, X tweet, blog header). This pins the draw upfront.

// 2. When entries close, freeze the entrant list:
const entries = await db.entrants.findAll({ where: { campaignId } });

// 3. Use a deterministic, publicly known clientSeed.
//    Block hashes and tweet IDs work well — nobody controls them.
const clientSeed = `raffle_${campaignId}_${publicBlockHash}`;

// 4. Reveal the winner.
const url = new URL("https://api.provable.io/api/pick");
url.searchParams.set("clientSeed", clientSeed);
url.searchParams.set("items", entries.map((e) => e.id).join(","));

const res = await fetch(url, {
  headers: { "x-api-key": process.env.PROVABLE_KEY }
});
const { outcome: winnerId, serverHash, shortId } = await res.json();

// 5. Announce: "Winner is X — verify at https://provable.io/o/{shortId}"

Why this is fair

  1. The serverHash commitment goes out before the entries close, so the server can't shop seeds against the entrant list.
  2. The clientSeed is something nobody controls (a future block hash, a tweet ID at a future time, a Bitcoin block at a fixed height) — so entrants can't grind their own usernames against it.
  3. The mapping (uniform integer in [1, N] or weighted bucket lookup) is documented and re-derivable from the open-source provable-core library.

Common patterns

The full recipe

This page is the keyword-friendly intro to the raffle workflow. For the long-form walkthrough with commitment patterns, public-seed strategies, and post-draw publication tips, see the dedicated recipe: Building Fair Raffles.

Where it fits

Related

FAQ

How do I draw a verifiable winner with the raffle picker API?

Call /api/pick with the entrant list (or /api/ints with min=1, max=N if you numbered entries). The response includes the winner, a deterministic seed trail, and a serverHash anyone can paste into /verify to confirm the draw wasn't reshuffled.

Should I pass items or pick by entry number?

For small entrant lists, pass the items array directly to /api/pick. For thousands of entries, don't ship the whole list — number them 1..N and draw a single integer with /api/ints. Both paths are equally verifiable.

How do I run a weighted raffle?

Pass a weights array alongside items on /api/pick. For example, weights [1,1,3] gives the third entrant triple odds. The mapping is documented and reproducible, so weighted draws stay auditable.

How do I draw multiple prizes (1st, 2nd, 3rd)?

Iterate /api/pick N times with the same seed and remove the previous winner from the items list between calls. The cursor auto-advances, so each call returns the next place with no duplicates.