Skip to main content

Staking

Five read operations. Four are anonymous, one requires an API key. Staking positions are public on-chain data, so the staker list and any wallet’s positions are readable without a key.

Setup

The anonymous operations need no key:
For positions, pass a server-side key. The SDK refuses to accept one in a browser runtime.

Amounts are raw integer strings

Fields ending in Raw, plus accumulatedRewardsLamports, are unsigned integer strings in the token’s base units. aprPercent is a decimal string. Parse raw amounts with BigInt and decimals with a decimal library — never Number.

Pool state

Cached public, max-age=10, stale-while-revalidate=30 with an ETag. Rate policy public-read-cheap.

Lock options

Each entry in lockOptions is one lock period the staking program accepts: On mainnet the periods last 7, 30, 90 and 180 days with multipliers of 1.0x, 1.25x, 1.75x and 2.5x (10000, 12500, 17500, 25000). The devnet staking program uses the same multipliers but shortens the locks to 60, 120, 300 and 600 seconds, so read durationSeconds rather than hard-coding a duration.

Leaderboard

The top 100 wallets by weighted stake.
The response is { data: [...] }. It is not cursor paginated — the top 100 is the whole result, and there is no way to page past it. Ranking is by weighted stake, so a smaller position on a longer lock can outrank a larger one on a shorter lock. Cached public, max-age=30, stale-while-revalidate=120 with an ETag. Rate policy public-read-expensive.

All stakers

Every wallet staking on a token, one row per wallet, in the same order as the leaderboard. Unlike the leaderboard it is cursor paginated, so you can walk past the top 100.
Ordered by weighted stake descending, then wallet address ascending. Returns staking_pool_not_found when the token has no visible staking pool. Cached public, max-age=30, stale-while-revalidate=120, without an ETag. Rate policy public-read-expensive.

Any wallet’s positions

Every staking position a wallet holds across Blank tokens, newest first. Pass mint to narrow it to one token.
The response uses the same position fields as Your positions. A wallet with no positions, or a mint that is unknown or has no staking, returns 200 with an empty page, never 404. Ordered by position creation time, so pages stay stable while positions sync. Cursor paginated with cursor and limit; a cursor is bound to the wallet and mint it was issued for. Cached public, max-age=10, stale-while-revalidate=30, without an ETag. Rate policy public-read-cheap.

Your positions

listOwnedStakingPositions returns only positions owned by the wallet pinned to your API key. A v2 API key is bound to exactly one Blank account and that account’s single verified wallet. It can never read another wallet’s positions, and there is no parameter that changes this.
Cursor paginated with cursor and limit, returning { data: [...], page: { nextCursor, hasMore } }. Never cached — the response is private, no-store on the api-key-read rate policy. See Conventions.

Raw HTTP

From a server shell, never a browser:

Rewards

Both position routes read every returned position’s stake account and staking pool from the Solana staking program when you call them, so the four reward fields are live, not indexed.
  • pendingRewardsLamports is what a claim pays out right now: the rewards the stake account has accrued plus its share of every reward booked into the pool since its last update. It includes creator bonus top-ups. It is the same number the Blank app shows on a token’s staking panel.
  • It does not include trading fees that are still waiting to be distributed to the staking pool. The Blank portfolio page adds an estimate of that share, and claiming in the app distributes those fees first, so a claim can pay slightly more than pendingRewardsLamports.
  • claimable is true when pendingRewardsLamports is at least the minimum claim amount: 10000000 lamports (0.01 SOL) on mainnet, 2000000 lamports (0.002 SOL) on devnet.
  • A position whose stake account was closed between indexing and the chain read reports "0", "0" and false.
  • When the chain read fails, the route answers 503 with api_dependency_unavailable and Retry-After. It never returns zeros in place of an unknown amount.

Getting staking changes pushed

Subscribe a webhook endpoint to build.blank.staking.position.updated.v1 to get staking changes pushed instead of polling walletPositions or stakers. Each event says whether a position was opened, updated or closed, and carries the same StakingPosition object these reads return. It covers every position on every visible token, not only your wallet’s. Webhook endpoints require an API key with webhooks:write, even though every staking read above is anonymous. See Webhooks for the payload.

Reads only

The v2 API exposes staking reads. There is no public API to enable staking, change the allocation, stake, unstake, or top up rewards. Those are creator-dashboard and app flows. See Staking for the product rules: lock periods, weighting, and how rewards accrue.

Errors

Full problem-details shape lives in Errors.

Next