Skip to main content
Versioned paths, coded errors, a published OpenAPI document, and two endpoints that assemble a transaction for your key to sign. Working from Claude, ChatGPT or another MCP client? The same prepare endpoints are tools on the MCP server, with a sign-in button instead of a hand-minted session. The API is free, and stays free: no key, no plan, no charge per call, with or without a bearer.
BaseStonk never signs, relays or broadcasts. /prepare returns an unsigned transaction - to, data, value, chainId. Sending is yours. A client waiting for a hash from this API waits forever.

/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 and code, 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 the prepare 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

Your key never leaves your process. This API never learns it exists. Want a person to sign instead? The MCP agent tier wraps the same answer with a 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.