When to batch vs. loop

Looping GET /api/ints N times costs N round-trips and N rate-limit slots. POST /api/batch takes a single round trip and counts as one rate-limit slot for the batch dispatch (each successful draw still bumps quota individually). Reach for batch when:

Don't batch when:

Request shape

The body is an object with a draws array. Each draw is { endpoint, params }; endpoint is the bare generator name (floats, ints, shuffle, pick, bytes, dice, gaussian) and params is exactly the query string that endpoint would normally take. Up to 50 entries per batch.

curl -X POST https://api.provable.io/api/batch \
    -H "Content-Type: application/json" \
    -d '{
        "draws": [
            { "endpoint": "ints",  "params": { "clientSeed": "round-1:chest-1", "count": 1, "min": 1, "max": 100 } },
            { "endpoint": "ints",  "params": { "clientSeed": "round-1:chest-2", "count": 1, "min": 1, "max": 100 } },
            { "endpoint": "pick",  "params": { "clientSeed": "round-1:loot-3",  "items": ["common","rare","legendary"], "weights": [70,25,5] } },
            { "endpoint": "dice",  "params": { "clientSeed": "round-1:boss",    "notation": "2d20+5" } }
        ]
    }'

Response shape & ordering

You always get back a results array of length N, in the same order as the request. Each element is either { ok: true, outcome } with the full outcome shape that the matching standalone endpoint would have returned (shortId, permalink, serverHash, etc.) or { ok: false, error, code? }.

{
    "results": [
        { "ok": true,  "outcome": { "outcome": [42], "shortId": "k7Qm2A9bXz", "permalink": "https://provable.io/o/k7Qm2A9bXz", "serverHash": "9f…", "clientSeed": "round-1:chest-1", "cursor": 0, "nonce": 0, "endpoint": "ints", "min": 1, "max": 100, "count": 1, "created": 1716566400000 } },
        { "ok": true,  "outcome": { "outcome": [73], "shortId": "9aB2c3D4eF", "permalink": "https://provable.io/o/9aB2c3D4eF", "serverHash": "5e…", "clientSeed": "round-1:chest-2", "cursor": 0, "nonce": 0, "endpoint": "ints", "min": 1, "max": 100, "count": 1, "created": 1716566400000 } },
        { "ok": false, "error": "items required" },
        { "ok": true,  "outcome": { "outcome": { "notation": "2d20+5", "rolls": [14, 9], "modifier": 5, "total": 28 }, "shortId": "P9q8R7s6T5", "permalink": "https://provable.io/o/P9q8R7s6T5", "serverHash": "0a…", "clientSeed": "round-1:boss", "cursor": 0, "nonce": 0, "endpoint": "dice", "count": 2, "sides": 20, "modifier": 5, "created": 1716566400000 } }
    ]
}

Partial failure handling

A single bad draw does not abort the batch — it just shows up as ok: false in that slot. The successful draws still persist their outcomes, fire webhooks (live keys), and count against quota. Your client code should iterate the array and handle each ok: false the same way it would handle a failed individual call:

const { results } = await batch(req);
const okOutcomes = [];
const errors = [];
results.forEach((r, i) => {
    if (r.ok) okOutcomes.push({ index: i, outcome: r.outcome });
    else errors.push({ index: i, draw: req.draws[i], error: r.error, code: r.code });
});
if (errors.length) console.warn("batch had partial failures:", errors);

Two specific failure codes are worth handling:

Attributing a whole batch to a single round

Two patterns work well, depending on what you need to prove later:

Pattern A — encode the round into every clientSeed

Prefix every draw's clientSeed with the round id (e.g. "round-1:chest-1", "round-1:chest-2"). Now GET /api/listOutcomes?clientSeed=round-1:chest-1 tells you exactly which seed produced each result, and players can look up any draw's permalink without seeing the others.

Pattern B — one Idempotency-Key per round

Generate one key per round, send it as the Idempotency-Key header on the batch request, and store it alongside the round in your DB. A retry replays the exact same results — no draw runs twice, no double-billing. See Using Idempotency-Key for the full retry pattern.

Pattern A makes individual draws auditable; Pattern B makes the whole round retryable. Use both together for high-stakes rounds.

Limits to be aware of

Next steps