Seeded Giveaway Winner Picker
This seeded giveaway winner picker chooses one name from an ordered entrant list and produces the same result whenever the list, weights, and seed stay unchanged.
Run — free
Give every entrant one entry, or assign larger entry counts to reflect bonus actions, ticket bundles, or other published rules. Because the calculation never uses a clock or hidden random source, organizers can save the seed and input alongside the result, rerun the draw later, and demonstrate exactly how the named winner was selected.
Prepare an ordered entrant list with honest weights
Start with one row per entrant and keep the rows in a deliberate, stable order. Each row needs a non-empty name. The optional entries value is a positive whole number: omit it for one entry, use two for twice the selection weight, and so on. A participant with five entries occupies five positions in the conceptual ticket pool, while a participant with one entry occupies one position. The picker does not deduplicate names because repeated names may represent legitimate separate records, and silently merging them would change the published odds. If your source can contain duplicates, decide whether to consolidate them before the draw and preserve that decision in your campaign notes. Also freeze the final ordered list before announcing the seed. Sorting, adding a row, removing a row, renaming somebody, or changing an entry count changes the weighted ranges and may therefore change the winner. The calculation accepts up to 10,000 entrant rows and validates every weight rather than rounding fractions or accepting zero-weight placeholders. This keeps the input contract clear: every submitted row participates, and every declared entry count contributes directly to that entrant's chance.
Use a seed to make the draw reproducible
A seed is any non-empty text chosen for the draw, such as a campaign identifier combined with a closing-date label. The picker converts that exact text into a fixed unsigned 32-bit value using a documented hash, then maps the value to one position in the total weighted entry range. It walks the entrants in their supplied order and returns the row whose cumulative range contains that position. The same seed, entrant order, names, and entry counts always produce the same output on the API and in the browser. Capitalization, spaces, and punctuation in the seed matter, so save the exact original string instead of a retyped approximation. For a public process, consider committing to how the seed will be obtained before entries close—for example, a clearly identified external event value published after the deadline. This tool remains deterministic rather than cryptographically random: its purpose is repeatable selection and straightforward auditing, not generating an unpredictable secret. Do not reveal a freely chosen seed early if participants or organizers could still alter the entrant list in response to the projected result.
Record and verify the winning result
The result includes the winner's name, the zero-based index of the winning row, the selected zero-based weighted entry position, the total number of weighted entries, and the numeric hash derived from the seed. Keep the complete input with those fields in your giveaway record. A reviewer can submit the same ordered rows and seed, confirm that the total matches, and receive the identical winner without trusting a screenshot or an unexplained random button. The row index also distinguishes two records that happen to share the same displayed name, while the weighted position shows where the selection landed inside the conceptual ticket pool. If a rerun differs, compare the saved input character by character: even a trimmed name, reordered row, adjusted weight, or differently capitalized seed represents a different draw. The browser version is useful for a transparent manual selection, while the API costs $0.002 per request when you need to automate the same procedure in campaign software or an audit trail. In either channel, the algorithm has no network calls, current-time dependency, or mutable state, so execution environment and run date do not alter the answer.
What you can do with it
Draw a social giveaway winner
Freeze eligible names after the deadline, publish or archive the seed, and select one reproducible winner from the final list.
Honor bonus-entry rules
Assign higher entry counts for qualifying actions while keeping the winner selection proportional to the recorded weights.
Audit a campaign selection
Store the ordered input and returned hash details so another person can independently reproduce the announced result.
FAQ
What does one draw cost?
The API price is $0.002 per request. The same deterministic calculation can also run free in your browser.
What happens when an entry count is omitted?
That entrant receives one entry. Explicit weights must be positive whole numbers.
Will the same seed always select the same winner?
Yes, provided the seed and the complete ordered entrant list, including names and entry counts, are identical.
Does changing entrant order matter?
Yes. Order defines each entrant's range in the weighted pool, so reordering rows creates a different draw input.
Is this cryptographically random?
No. It is a transparent deterministic picker designed for reproducibility. Use a seed source participants cannot manipulate when unpredictability matters.
Why are duplicate names kept separate?
The picker treats every row as supplied because duplicate records may be intentional. The returned row index identifies which record won.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/game/giveaway-winner-pick \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"entrants":[{"name":"Avery","entries":1},{"name":"Blake","entries":3},{"name":"Casey"}],"seed":"spring-campaign-2026"}'const res = await fetch("https://api.kit.forhosting.com/game/giveaway-winner-pick", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"entrants": [
{
"name": "Avery",
"entries": 1
},
{
"name": "Blake",
"entries": 3
},
{
"name": "Casey"
}
],
"seed": "spring-campaign-2026"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/game/giveaway-winner-pick",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"entrants": [
{
"name": "Avery",
"entries": 1
},
{
"name": "Blake",
"entries": 3
},
{
"name": "Casey"
}
],
"seed": "spring-campaign-2026"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/game/giveaway-winner-pick", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"entrants":[{"name":"Avery","entries":1},{"name":"Blake","entries":3},{"name":"Casey"}],"seed":"spring-campaign-2026"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"entrants":[{"name":"Avery","entries":1},{"name":"Blake","entries":3},{"name":"Casey"}],"seed":"spring-campaign-2026"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/game/giveaway-winner-pick", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"entrants": [
{
"name": "Avery",
"entries": 1
},
{
"name": "Blake",
"entries": 3
},
{
"name": "Casey"
}
],
"seed": "spring-campaign-2026"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "game.giveaway_winner_pick",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_items | 10000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |