Skip to main content

Price Predictions

Price Prediction is the largest automation surface in the v2 API. You can read rounds and results anonymously, submit for the wallet your API key is pinned to, or let an end user submit from a partner site by signing a short-lived delegated intent with their verified wallet. For the product rules — how rounds are scheduled, how prizes are funded, and how winners are ranked — see Price Prediction.

Operations

Base URL is https://api.blank.build/api/v2. See Scopes for granting predictions:read, predictions:write, and predictions:delegate, and Conventions for pagination, caching, rate limits, retries, and idempotency.

Client setup

API keys are server-side only. Every example on this page is Node.js server code.
Every SDK call resolves to { data, metadata, response }. All prices are canonical decimal strings and all lamport amounts are unsigned integer strings — parse them with BigInt or a decimal library, never Number.

Rounds

listPredictionRounds returns rounds newest-first with cursor + limit (1–100, default 50).
You can only submit while a round is open. locksAt is the scheduled boundary — treat the server’s status as authoritative rather than comparing clocks yourself, since the lock is a durable state transition, not a timestamp comparison.
If the token has never had Price Prediction enabled, listPredictionRounds returns 404 prediction_token_not_enabled.

Privacy model

This is the part most integrations get wrong. Exact predicted values are withheld until the round durably locks, so nobody can copy or front-run other entries. listRoundPredictions returns a discriminated union on visibility:
Before the lock:
After the lock:
data holds at most 100 PublicPredictionEntry items: id, walletAddress, username, predictedPriceInSol, rank (nullable), distancePpm (nullable), and submittedAt.
Never branch on locksAt having passed to decide whether exact values will be present. Branch on visibility. The switch happens on the durable lock transition, which is what the API enforces.

Standings

listPredictionStandings returns { data: [...] } with at most 100 ranked entries. It returns 409 prediction_exact_values_locked until the round has durably locked.
Each standing carries the PublicPredictionEntry fields plus a non-null rank and payoutLamports (unsigned integer string, nullable — null when no payout applies).

Your own prediction

getOwnedPrediction returns your full immutable prediction at any time, including before the round locks. The privacy rules apply to other people’s entries, not to yours. Predictions created through this API always carry submissionSource: "api_v2", which is how you tell your own automated entries apart from ones made in the Blank app.

Eligibility

getPredictionEligibility evaluates your API key’s pinned wallet against finalized chain state and returns the evidence behind the verdict.
This route can return 503 prediction_balance_unavailable when finalized balance data cannot be read. That is retryable — back off and try again.
Eligibility is advisory evidence, not a reservation. The server re-verifies every condition when you submit, so an eligible: true response can still be followed by a 409 if the round locks or your balance drops in between. Check eligibility to give users a clear reason up front, then handle the submit errors anyway.
Eligibility does not stop at the first failure. For example, a closed round and a zero token balance produce both round_not_open and insufficient_balance. Submission errors keep one primary code for normal branching and include any additional failures discovered during the same evaluation in problem.causes.

Submitting a prediction

createPrediction takes a strict body — extra properties are rejected. Canonical means no leading zeros on the integer part and no trailing zeros in the fraction: send "0.00001234", not "0.000012340".
A successful call returns 201 with the immutable Prediction.
Predictions cannot be edited or withdrawn. One prediction per wallet per round — a second attempt returns 409 prediction_already_submitted. Validate the value in your own code before you send it.

Idempotency

createPrediction requires a durable Idempotency-Key header: 8–128 printable ASCII characters. If you do not pass idempotencyKey in the mutation options, the SDK generates one and exposes it as result.metadata.idempotencyKey. Persist the key before you send the request, and reuse it on every retry. A generated key that only exists in memory is useless if the process dies mid-flight, which is exactly the case idempotency exists for.
  • Retention is 24 hours.
  • A replayed response carries Idempotent-Replayed: true; the SDK surfaces it as result.metadata.idempotentReplayed.
  • Reusing a key with a different body returns 409 idempotency_key_reused.
  • Retrying while the first attempt is still in flight returns 409 idempotency_request_in_progress with Retry-After: 1.
The full contract is in Conventions.

Raw HTTP

Run this from a server shell, never a browser:

Submission errors

Read routes surface 404 prediction_round_not_found for unknown rounds, 404 prediction_not_found when getOwnedPrediction is called with an ID your key does not own, and 409 prediction_exact_values_locked when exact values are still withheld. The shared 401 invalid_api_key and 403 insufficient_scope apply to every authenticated route. See Error Model for the problem-details body and Troubleshooting for diagnosis.

Delegated submissions

Use delegation when users should enter a prediction on your site under their own Blank account. Create a dedicated server-side API key with only predictions:delegate. Never send that key or construct BlankClient in the browser. The flow is server → browser wallet → server:
  1. Your server prepares an intent for the exact round, wallet, and price.
  2. Your browser gives intent.message to the connected wallet’s signMessage method as UTF-8 bytes.
  3. Your browser base58-encodes the 64-byte Ed25519 signature and sends the intent ID and signature to your server.
  4. Your server submits them to Blank with a second durable idempotency key.
Prepare the intent on your server:
Ask the connected wallet to sign the exact message in the browser. The example uses bs58; the Blank SDK remains server-only.
Submit from your server:
An intent expires after five minutes or when the round locks, whichever happens first. It belongs to the API key that created it and can be consumed once. The response and signed message identify the authoritative token mint and Solana network as well as the round, wallet, and price. The stored message is authoritative: reconstructing it, changing any field, or signing a different byte sequence fails verification. The signing wallet must already be the verified wallet of a Blank account with a username and the required finalized token balance. The existing one-prediction-per-account-per-round rule still applies. The integration receives the created Prediction in the submission response, while owner-scoped reads and prediction webhooks remain private to the signing user’s Blank account. All normal prediction eligibility errors can also be returned at submission time. A valid signature proves consent; it does not reserve eligibility or bypass the round, username, balance, or uniqueness rules. When several requirements fail, inspect problem.causes as well as the primary problem.code. Each cause has a canonical code, title, detail, and optional context. Balance failures include balanceRaw, minimumBalanceRaw, and evaluationSlot; closed-round failures include roundStatus and locksAt.

End to end

Find the open round, check eligibility, submit with a caller-owned durable key, then read the prediction back.
The returned prediction.submissionSource is "api_v2", and prediction.status starts as active. settlementEligible, distancePpm, and rank stay null until the round settles.

Reacting to rounds with webhooks

Polling works, but webhooks are how you drive a bot without a timer. Three event types cover the prediction lifecycle: A typical loop: subscribe to round.locked to fetch standings the moment they open up, and to round.settled to record results and payouts. Subscribe to prediction.submitted if you want a confirmation signal independent of your submit call’s HTTP response. See Webhooks for endpoint registration, signature verification, replay, and delivery retries.