/api/v1
Every REST endpoint is also served under /api/v1/
as a machine contract. Errors are coded -
{"error":{"code":"rate_limited","message":"…","retryAfter":21}}. Lists
carry items and nextOffset; pass it back as offset and stop at
null. Every response carries x-bstonk-api-version: 1, and an
Idempotency-Key header is echoed.
The spec is canonical; this page is orientation. The spec is generated from the route declarations the server registers, and a test holds the two one-to-one, so it cannot drift from the server.
llms.txt carries the operations tagged agents.The same document drives the MCP server. Each agent-tier operation (the two prepares and POST /agent/metadata) declares its security as agentBearer (a bearer from /agent/session or Sign-In with Ethereum) or agentOAuth with the prepare scope (a chat host’s sign-in), and x-mcp-tools on an operation names the MCP tools that call it.Budgets
A bearer buys budget; it never gates. Every read works without one.
A bearer spends from the wallet bucket and the envelope; the tighter
one is what
x-ratelimit-remaining reports. x-bstonk-identity says
which answered - wallet or ip - and is the only way to notice a
bearer silently stopped working. /prepare costs 10 units.
Errors
Branch on status andcode, never on message.
Never sign past
expiresAtBlock - re-prepare instead.
Sessions
Keys your budget to your wallet instead of your IP. Sign once, exchange once.
The scope is in what the wallet signs: a read signature cannot be
redeemed for prepare.
This route mints
read and prepare only. The write scopes come from
Sign-In with Ethereum.
Sign-In with Ethereum
The same bearer, from a standard EIP-4361 message instead of typed data, and the only way to a bearer that can write.
Scopes are in what the wallet signs, so its own screen lists what it is
delegating. A message naming none grants
read only; asking in the body
for a scope the message did not name is 403, and an unknown scope is
400. The server re-renders the message and refuses any difference
(400 not a canonical EIP-4361 message), so build it with a SIWE library
such as viem’s createSiweMessage and send the text unchanged; the address
must be checksummed. An EOA signature is checked directly; a smart wallet
is checked through ERC-1271, or ERC-6492 before it is deployed, on Base.
A write bearer is a delegation:
token:edit edits every page the wallet
controls, and token:handover names no recipient. Ask for it only in a
session that needs it.
Prepared transactions
Both endpoints need theprepare scope and take chainId in the body;
absent means Base. tx.chainId echoes it; tx.to is that chain’s
launcher or router.
Any other id is
400 unknown_chain.
Launch
"standard": "b20" asks for a B20 token against a pair that is not itself a B20 (WETH, USDC, any oracle-priced pair); a B20 pair always launches B20. Omit it for an ordinary launch.
The creator is always the bearer’s wallet. devBuyPair is raw pair
units; the intent reports it as amount, USD and percentOfSupplyAtOpen.
The chain and the pair decide the leg; intent.leg names it:
A pair that is neither registered nor routable is a 422 with
tx.data
empty - nothing to sign. On Robinhood Chain a v4-priced pair needs a
checkpoint 60-3600s old; if one is missing, the answer carries the
transaction that posts it.
Trade
amountIn is raw units of what you spend: the pair on a buy, the
token on a sell.
The quote is at the token’s own tax, never a default - an assumed rate
reverts TooLittleReceived, and a token with no rate on record refuses
to quote. The eth_call fill is checked against minOut, so a stale
quote fails the verdict rather than your transaction. On a fresh launch
nobody has bought yet, buys quote from the entry boundary and a sell is
refused: the empty side has nothing to sell into.
The loop
basestonk.io/sign#… link that carries these
exact bytes; the page decodes them, checks they go to a BaseStonk launcher
or router, and only then offers the prepared-for wallet a button.
The CDN bot-scores datacenter traffic and can challenge at the edge, before the API sees the request. The tell: a
403 with no JSON error.code. Every refusal this API issues is JSON. A carve-out for /api/v1 is pending.
