Terminal API
Every launch this launchpad has put on Robinhood Chain, as exact, versioned JSON. Discovery, market history, account views, executable quotes, unsigned transactions and a live stream. A terminal signs with its own user’s wallet; this service never accepts a private key, a signed transaction or custody of anything.
Start here
Nothing is required to read. Point a request at the index and it answers with the canonical links for everything else.
curl https://launchpad.bettererc.org/v1
curl 'https://launchpad.bettererc.org/v1/tokens?sort=volume1h&order=desc&limit=25'/v1/config is the source of truth for deployed contract addresses, ABIs, the shared Uniswap v4 pool key and the launch profile every token is created against. Read it at start up and cache it for five minutes. Do not hard-code an address out of this page.
Every launch shares one immutable pool profile: native ETH is currency0, the token is currency1, and the pool exists from the launch transaction onward. There is no bonding curve, no second venue and no migration step, so a terminal that models graduating launchpads has nothing to bind a graduation state to.
Reading a response
Every successful response carries the same envelope, and the envelope always says how far behind the chain the answer is.
{
"data": { },
"meta": {
"chainId": 4663,
"indexedBlock": "41893204",
"indexedAt": "2026-08-21T10:41:12.000Z",
"headBlock": "41893224",
"confirmations": 20,
"lagSeconds": 4,
"stale": false
}
}indexedBlock is the deepest block folded into the answer, and it trails headBlock by confirmations by design. When stale is true the indexer has fallen further behind than its configured tolerance and the response should be treated as a last known good value rather than a current one.
Paginated endpoints add a page block.
{
"data": [ ],
"page": { "nextCursor": "opaque-value-or-null", "hasMore": true },
"meta": { }
}Errors replace the envelope with a stable code. Match on the code, never on the message.
{
"error": {
"code": "TOKEN_NOT_FOUND",
"message": "No launch is indexed at that address",
"details": { "address": "0x..." }
}
}- Raw amountsstring
- Every on-chain quantity is a base-10 integer in the asset’s smallest unit. ETH and launch tokens both use 18 decimals. Parsing one as a JavaScript number loses value silently above 253.
- Decimal fieldsstring
- Convenience values such as priceEthPerToken and marketCapEth are formatted for display. The raw amounts beside them remain authoritative.
- Addressesstring
- Case-insensitive in requests, normalised to lowercase in indexed responses.
- Cursorsstring
- Opaque. Do not read, construct or persist one across a sort change; a cursor is bound to the sort and order it was issued under.
- Unknown fieldsany
- Additive and backward-compatible. Ignore what you do not recognise. Breaking changes ship under a new path version.
Common codes are INVALID_ADDRESS, INVALID_CURSOR, INVALID_PARAMETER, INVALID_API_KEY, SCOPE_DENIED, ORIGIN_DENIED, TOKEN_NOT_FOUND, AMOUNT_EXCEEDS_RANGE, NO_EXECUTABLE_QUOTE, RATE_LIMITED, FEATURE_DISABLED, INDEXER_STALE and RPC_UNAVAILABLE.
Keys and limits
Reads are open. A partner key raises the ceiling and unlocks the transaction builders, and nothing else.
Authorization: Bearer berc_live_...- Anonymousno header
- 60 requests per minute per IP. Read scope only. Page size caps at 200.
- Partnerbearer key
- A per-key limit, a scope set drawn from read, tx and metadata, and an optional origin allowlist. Page size caps at 500.
Keys are SHA-256 hashed before storage, so the plaintext is shown once at issue and never again. A browser request that sends an Origin header alongside a partner key must send an origin registered on that key. A server-to-server terminal registers no origins at all.
A key grants rate and endpoint scope. It never authorises an on-chain action. A transaction builder returns calldata that still needs the user’s wallet signature.
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-Reset
Retry-After only on 429Endpoints
The full contract, with request and response schemas, is served as OpenAPI 3.1 at /v1/openapi.json.
Reference
Tokens and market data
Accounts
Trading
Streaming
Discovery
The token list is the endpoint a new-pairs pane and a trending board are both built on.
curl 'https://launchpad.bettererc.org/v1/tokens?sort=volume1h&order=desc&limit=50'- sortenum
- launchedAt, volume24h, volume1h, volume5m, marketCap, raised, holders, progress, lastTradeAt, trades24h. Defaults to launchedAt.
- orderasc | desc
- Defaults to desc.
- standardberc-20 | berc-721
- Filter by token standard.
- verifiedboolean
- Whether the clone still matches the runtime it claims.
- creatoraddress
- Restrict to one deployer.
- qstring
- Free text across name and symbol.
- minMarketCapEth · maxMarketCapEth · minVolume24hEthdecimal
- Decimal ETH, converted server-side. Not wei.
- minHolders · minProgressnumber
- Holder floor, and how far through the seeded range the pool has been walked, from 0 to 1.
- launchedAfter · launchedBeforeRFC 3339
- Bound the launch window.
- limit · cursornumber · string
- 50 by default, 200 anonymous, 500 on a partner key.
Trade and transfer history additionally accept since and until, each of which takes either an indexed block number or an RFC 3339 timestamp. This is the pair to backfill with.
curl 'https://launchpad.bettererc.org/v1/tokens/0x.../trades?since=41890000&limit=200'
curl 'https://launchpad.bettererc.org/v1/tokens/0x.../candles?interval=5m"e=eth&limit=1000'Candles omit empty buckets. Carry the previous close forward if a chart library needs continuous bars. A fill’s maker is the end user who sent the transaction and router is the contract that swapped, so a terminal attributing its own flow filters on router.
An operator can hide a token from public discovery. Hidden addresses never appear in lists or account aggregates, but their direct token endpoint stays available and returns "listed": false, so an audit trail is never broken by a listing decision.
Quotes and transactions
A quote is a simulation against the deployed v4 quoter. A builder turns that quote into calldata. Neither one moves anything.
curl 'https://launchpad.bettererc.org/v1/quote?token=0x...&side=buy&amountIn=1000000000000000000&slippageBps=500'The response carries the exact amountOut, a slippage-adjusted minAmountOut, quoter gas, the current directional Uniswap protocol fee, the 1.25% LP fee breakdown, the effective execution price and price impact. Fee amounts are estimates because v4 rounds per swap step; the quoted output is the executable result.
A buy that would exhaust the seeded range returns AMOUNT_EXCEEDS_RANGE. A sell with no executable path returns NO_EXECUTABLE_QUOTE.
curl -X POST 'https://launchpad.bettererc.org/v1/tx/buy' \
-H 'Authorization: Bearer berc_live_...' \
-H 'Content-Type: application/json' \
-d '{
"token": "0x...",
"recipient": "0x...",
"amountInWei": "1000000000000000000",
"minTokensOut": "3999500000000000000000000",
"deadlineSeconds": 600
}'{
"chainId": 4663,
"to": "0x...",
"data": "0x...",
"value": "1000000000000000000",
"gasLimit": "350000",
"function": "buy(address,address,uint256,uint256)",
"expiresAt": "2026-08-21T10:51:00.000Z"
}Verify chainId, show the transaction to the user, sign it with the user’s wallet and broadcast it through your own RPC. Every builder requires a partner key with the tx scope, and refuses with FEATURE_DISABLED while the matching release gate is off.
- POST /v1/tx/sell
- Takes token, amountIn, minNativeOut, recipient, and an optional owner and deadlineSeconds. When allowance is short the response carries an approval.transaction that must confirm first. The router uses a plain ERC-20 transferFrom, so Permit2 is not on this path.
- POST /v1/tx/launch
- Takes creator, name, symbol, metadataUri, and optionally salt, initialBuyWei, minInitialTokensOut and deadlineSeconds. Omit the salt and the server generates and returns one. The response also carries predictedTokenAddress and, when an initial buy is requested, initialBuyQuote.
- POST /v1/tx/claim-fees
- Takes tokenId, creator and recipient. The locker position is read first and a mismatched creator is rejected rather than handed calldata that would revert.
Terminals pin their own metadata and pass an immutable ipfs:// URI. The indexer fetches that document, within a 256 KiB ceiling, and reads description, image, external_url and properties.socials from it, so a launch created through a terminal renders completely on the betterERC site without touching the site’s domain-bound upload route.
Live updates
One Server-Sent Events connection carries up to twenty subscriptions, and every event is replayable.
GET https://launchpad.bettererc.org/v1/stream/sse?channels=launches,prices,trades:0x...,candles:0x...:5m,reorg
Accept: text/event-stream- launchesevent: launch
- A new token, in the shape the single-token endpoint returns.
- trades:{token}event: trade
- One fill against that token.
- pricesevent: price
- Batched tick and price movement.
- candles:{token}:{interval}event: candle
- The current bar, on every update.
- reorgevent: reorg
- A rewind, and the tokens it touched.
Subscribe to reorg. When the chain reorganises, rows at or above the rewind block are removed from the index, and a terminal that never hears about it keeps showing fills that no longer exist.
Every event carries an opaque id. A browser replays it automatically as Last-Event-ID on reconnect; any other client should persist that id and send the header itself. The server writes comment heartbeats while idle. The stream is backed by the same canonical index as the REST endpoints, so it respects the same 20-confirmation boundary and is not a mempool feed.
Caching and CORS
- Config and standardsmax-age=300
- Plus stale-while-revalidate=600. Read once at start up.
- Token lists and account valuesmax-age=5
- Plus stale-while-revalidate=30.
- Trades, holders, transfers, candlesmax-age=2
- Short enough to poll, long enough to absorb a burst.
- Quotes and transaction buildersno-store
- Never cached anywhere.
Anonymous responses send Access-Control-Allow-Origin: * without credentials. A partner browser request has its origin echoed only after it matches the key’s allowlist. Rate limit headers are exposed to browser clients in both cases.
What v1 does not do
Four things a terminal will look for and not find. Each is a deliberate boundary rather than an oversight.
- USD pricinggated
- quote=usd returns FEATURE_DISABLEDuntil an ETH/USD observation source is configured. ETH-denominated candles, values and volumes are fully available without it.
- Complete PnLtransferAware: false
- Cost basis is average-cost over indexed fills. Transfers and airdrops receive none. Compare walletBalance with tradeModelBalance before presenting the number as complete.
- WebSocket and webhooksnot in v1
- Server-Sent Events is the only push transport. Fan-out over WebSocket and signed webhooks are not part of this version.
- Unconfirmed stateby design
- Nothing is served before it is 20 confirmations deep. A terminal that wants pending fills reads its own RPC.
The design rationale behind these choices, and the long-term shape of the integration, is recorded alongside the service in docs/TERMINAL_API.md. To have a key issued, or to raise a limit, talk to the operator running this deployment.