01
Overview
- Base URL
- https://api.fomopad.pro
- WebSocket
- wss://api.fomopad.pro/ws
- Format
- JSON in and out. Amounts are decimal strings in UI units; addresses are lowercase 0x strings.
- Auth
- None. Every endpoint is public and rate limited per IP.
- Chain
- Robinhood Chain, chain id 4663, gas in ETH. Coins are Pons v2 tokens on a Pons bonding curve priced in ETH, then a locked Uniswap v4 pool.
- Explorer
- https://robinhoodchain.blockscout.com
Every coin is launched for a fomo.family user (the match). Fees, prices and reserves are in ETH (18 decimals); every coin also has 18 decimals and a 1 billion supply. Path params named :address take a 0x address in any case.
02
Quickstart
One file that launches a coin for a fomo trader (with an optional first buy), and buys or sells an existing one. Node 18+, viem 2, and a private key in PRIVATE_KEY for a wallet holding a little ETH on Robinhood Chain.
// fomopad.ts: launch a coin for a fomo trader, buy and sell one. Node 18+.
// npm i viem
// PRIVATE_KEY=0x… npx tsx fomopad.ts launch unipcs ./logo.png
// PRIVATE_KEY=0x… npx tsx fomopad.ts buy <token> 0.01
// PRIVATE_KEY=0x… npx tsx fomopad.ts sell <token> 1000000
import { readFileSync } from 'node:fs'
import { createPublicClient, createWalletClient, defineChain, http, type Hex } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
const API = 'https://api.fomopad.pro'
const robinhood = defineChain({
id: 4663,
name: 'Robinhood Chain',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.mainnet.chain.robinhood.com'] } },
})
// The key stays on this machine; the API never sees it. Use a dedicated hot wallet.
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex)
const transport = http(process.env.RPC_URL ?? robinhood.rpcUrls.default.http[0])
const wallet = createWalletClient({ account, chain: robinhood, transport })
const client = createPublicClient({ chain: robinhood, transport })
type EvmTx = { to: Hex; data: Hex; value: string; gas?: string; label: string }
type Quote = {
inAmount: string
outAmount: string
minOut: string
feeQuote: string
priceImpactPct: number | null
route: 'curve' | 'v4'
snipeTaxBps?: number
final?: boolean // fills the curve: the rest of the ETH is refunded and the coin graduates
}
async function api<T>(path: string, body?: unknown): Promise<T> {
const res = await fetch(`${API}${path}`, {
method: body === undefined ? 'GET' : 'POST',
headers: body === undefined ? undefined : { 'content-type': 'application/json' },
body: body === undefined ? undefined : JSON.stringify(body),
})
const json: unknown = await res.json()
if (!res.ok) {
const e = json as { error?: string; details?: unknown }
throw new Error(`${res.status} ${e.error ?? 'error'} ${e.details ? JSON.stringify(e.details) : ''}`)
}
return json as T
}
/** Send each transaction in order, waiting for its receipt before the next (an approval lands before the sell). */
async function sendAll(txs: EvmTx[]): Promise<Hex[]> {
const hashes: Hex[] = []
for (const tx of txs) {
const hash = await wallet.sendTransaction({
to: tx.to,
data: tx.data,
value: BigInt(tx.value),
gas: tx.gas ? BigInt(tx.gas) : undefined, // set when estimation is unsafe (a buy that may finish the curve)
})
const receipt = await client.waitForTransactionReceipt({ hash })
if (receipt.status !== 'success') throw new Error(`${tx.label} reverted: ${hash}`)
console.log(tx.label, hash)
hashes.push(hash)
}
return hashes
}
async function launch(handle: string, imagePath: string): Promise<void> {
// 1. Pin the image. The server reads the bytes; the declared type is ignored.
const image = `data:image/png;base64,${readFileSync(imagePath).toString('base64')}`
const { imageUrl, imageMime } = await api<{ imageUrl: string; imageMime: string }>('/launch/image', { image })
// 2. Build. `match` is the fomo handle; the server checks it on fomo.family, sets the coin's
// website to its fomopad page and returns one transaction plus the coin's (predicted) address.
const built = await api<{ token: Hex; curve: Hex; tx: EvmTx; website?: string; firstBuy?: Quote }>('/launch/build', {
creator: account.address,
match: handle,
name: 'Cat Coin', // <= 32 bytes
symbol: 'CAT', // <= 10, letters and digits
description: 'Launched from a script',
twitter: 'https://x.com/example', // optional, https only
imageUrl,
imageMime,
firstBuy: { amount: '0.01', slippageBps: 500 }, // optional, ETH, in the same transaction
})
console.log('token', built.token, 'curve', built.curve, 'first buy', built.firstBuy?.outAmount ?? 'none')
// 3. Send it from the creator wallet and wait for the receipt.
await sendAll([built.tx])
console.log(`https://fomopad.pro/token/${built.token.toLowerCase()}`)
}
/** amount: ETH for a buy, coins for a sell. A sell may come back as [approve, sell]. */
async function trade(side: 'BUY' | 'SELL', mint: string, amount: string): Promise<void> {
// owner on the quote includes this wallet's snipe tax (a coin's first ~3 seconds).
const params = { mint, side, amount, slippageBps: '300', owner: account.address }
console.log('quote', await api<Quote>(`/trade/quote?${new URLSearchParams(params)}`))
const built = await api<{ txs: EvmTx[]; quote: Quote }>('/trade/build', { ...params, slippageBps: 300 })
await sendAll(built.txs)
}
const [cmd, a, b] = process.argv.slice(2)
const run =
cmd === 'launch' && a && b ? launch(a, b) : (cmd === 'buy' || cmd === 'sell') && a && b ? trade(cmd === 'buy' ? 'BUY' : 'SELL', a, b) : null
if (!run) console.log('usage: launch <fomo handle> <image> | buy <token> <eth> | sell <token> <amount>')
run?.catch((err: unknown) => {
console.error(err instanceof Error ? err.message : err)
process.exit(1)
})The coin page appears at https://fomopad.pro/token/<address> a few seconds after the launch confirms, once the indexer has picked it up. Read-only calls need nothing but HTTP:
# Live launchpad parameters (fee split, buyback token, graduation threshold)
curl -s https://api.fomopad.pro/config
# Coins still on the curve, by 24h volume
curl -s 'https://api.fomopad.pro/tokens?sort=volume&status=CURVE&limit=10'
# Every coin made for one fomo trader, and their page
curl -s 'https://api.fomopad.pro/tokens?match=unipcs'
curl -s https://api.fomopad.pro/fomo/users/unipcs
# One coin, its last 20 trades, 1h candles and fee history
curl -s https://api.fomopad.pro/tokens/<token>
curl -s 'https://api.fomopad.pro/tokens/<token>/trades?limit=20'
curl -s 'https://api.fomopad.pro/tokens/<token>/candles?tf=1h&limit=200'
curl -s https://api.fomopad.pro/tokens/<token>/fees
# Quote a 0.01 ETH buy (read-only, nothing is built or signed)
curl -s 'https://api.fomopad.pro/trade/quote?mint=<token>&side=BUY&amount=0.01&slippageBps=300'03
Launch flow
1. Pick the fomo trader
GET /fomo/search?q= returns matching fomo.family traders. Pass the chosen handle as match when you build; the server checks it exists on fomo.family (404 otherwise) and stores the latest name and avatar.
2. Upload the image
POST /launch/image with { image }, a base64 data: URL (png, jpg, gif or webp, ≤4 MB decoded; the format is read from the bytes). It returns { imageUrl, imageMime }.
3. Build the launch transaction
creator: your wallet (it sends the transaction and is recorded as the launcher).match: the fomo handle.name≤32 bytes UTF-8;symbol≤10 bytes, letters and digits (a leading$is stripped, upper-cased).imageUrlandimageMimefrom step 2;description≤1000 chars;twitter,telegram:https://links. There is no website field: the metadata's website is the coin's fomopad page.firstBuy: { amount, slippageBps }(optional, ETH) buys in the same transaction that creates the coin, so nobody buys before you.
The response is { token, curve, tx, website, firstBuy }: the coin and curve addresses the transaction creates (predicted with CREATE2), one EvmTx to the Pons contracts, the website written on chain (https://fomopad.pro/token/<curve>: a token cannot contain its own address, and that URL opens the coin) and, with a first buy, its TradeQuote. The value is the Pons launch fee (launchFee in /config) plus your first buy, in wei.
{ "token": "0x…", "curve": "0x…",
"tx": { "to": "0xe33e9e479df8802cb0866d5d05258bec4cf62948", "data": "0x…", "value": "10500000000000000",
"gas": "2500000", "label": "Launch $CAT" } }4. Send and confirm
Send tx from the creator wallet exactly as given (to, data, value, and gas when present), then wait for the receipt. If the launch terms change before it lands (rare), it reverts; build again.
04
Trading
GET /trade/quote (query string) and POST /trade/build (JSON body, plus owner) take the same fields:
mint: the coin's address.side:BUYorSELL.amount: a decimal string in UI units of what you pay in: ETH for buys, the coin for sells (both 18 decimals; extra digits are truncated).slippageBps: 10 to 5000, default 300 (3%).owner: required to build; optional on quotes, where it includes that wallet's snipe tax.
The quote is a TradeQuote: inAmount, outAmount, minOut (after slippage), feeQuote (ETH), priceImpactPct, route (curve or v4), snipeTaxBps (a coin's first ~3 seconds only) and final (the buy fills the curve). The build returns { txs, quote }: send txs in order, waiting for each receipt before sending the next.
- Approvals. A sell needs the coin approved to the curve, or after graduation to Permit2 and the Universal Router (two approvals). When it is not,
txsstarts with them. - Filling the curve. A buy larger than what is left on the curve buys exactly the remainder (
final), the unused ETH is refunded, and the coin graduates. Curve buys carry an explicitgasso graduation cannot run short: keep it. - Graduating. While
statusisCOMPLETE, quotes and builds return 409 until the Uniswap v4 pool exists. - Graduated. Once
statusisMIGRATED, the same calls route through the coin's Uniswap v4 pool.
05
Launcher fees
The launching wallet's share is paid to it automatically; there is no claim endpoint and no transaction to sign. GET /wallets/:address/created lists the coins a wallet launched with launcherPaidQuote (sent so far) and launcherOwedQuote (earned, in the next payout) per coin.
06
Endpoint reference
Paginated lists return { items, nextCursor }; pass nextCursor back as cursor until it is null. Defaults are in parentheses.
Config & stats
- GET
/configParamsnoneReturnsLaunchpadConfigDTOchain(id,name,explorer), theponsanduniswapcontract addresses,launchFee(ETH),creatorTaxBps, quote (ETH: the zero address, 18 decimals),feesin bps (tradeFeeBps,protocolBps,creatorBps,platformBps,buybackBps),buybackToken(mintis null until $… launches),buybackSelfPct(percent of the trader's share that buys the coin itself),siteUrl,curve.migrationQuoteThreshold(ETH raised to graduate), token supply/decimals. Cached 60 s. - GET
/statsParamsnoneReturnsPlatformStatsDTOtokens,graduated,volume24hQuote,buybackTotalQuote(ETH credited to fomo traders),matchedUsers,quoteUsd(USD per ETH),buybackBurned($… burned: the platform's own share, sent to the dead address).
Tokens
- GET
/tokensParamssortnew|volume|mcap|progress|lastTrade|lastBuy|buyback (new),statusCURVE|COMPLETE|MIGRATED,q(name, symbol, exact address or fomo handle, ≤64),match(fomo handle),creator,limit≤100 (30),cursorReturns{ items: TokenDTO[], nextCursor }sort=progressonly lists CURVE tokens unless you passstatus.lastBuyis what the site calls trending. - GET
/tokens/:addressParamsnoneReturnsTokenDTOTakes the coin's address or its curve's (a coin's on-chain website is
/token/<curve>); so do the sub-routes below.mintis the coin's address (lowercase),poolits Pons curve,dammPoolits Uniswap v4 pool id once graduated. Price, market cap,progress(0..1),status,quoteReserve, 24h stats, holders,feesTotalQuote,match(the fomo user:handle,displayName,avatarUrl,verified) andbuybackQuote(ETH credited to them). 404 if unknown. - GET
/tokens/:address/tradesParamslimit≤200 (50),cursorReturnsPaginated<TradeDTO>Newest first.
signatureis the transaction hash,slotthe block number,venueCURVE or V4 (graduated). - GET
/tokens/:address/candlesParamstf1m|5m|15m|1h|4h|1d (5m),from,to(unix s),limit≤1000 (500)Returns{ timeframe, items: CandleDTO[] }Ascending. OHLC in ETH per token; empty list (not 404) for unknown coins.
- GET
/tokens/:address/holdersParamslimit≤200 (50)ReturnsHolderDTO[]shareis 0..1 of total supply;labelmarks the curve, the Uniswap v4 pool, the platform and the creator. - GET
/tokens/:address/feesParamslimit≤100 (20),cursorReturnsPaginated<FeeEpochDTO>One row per claim from the Pons fee escrow:
claimedQuote,platformQuote,buybackQuote(credited to the matched user),claimSignatures(transaction hashes). The rest of each claim is the launcher's share, paid to them automatically. - GET
/trades/recentParamslimit≤50 (20)ReturnsTradeDTO[]All tokens, newest first. A plain array, not paginated.
fomo users
- GET
/fomo/searchParamsq(name or handle, ≤40)ReturnsFomoSearchHitDTO[]fomo.family traders for a launch picker:
handle,displayName,avatarUrl,verified,followers. Up to 12, cached a minute. Debounce it. - GET
/fomo/usersParamslimit≤50 (12)ReturnsFomoUserDTO[]Traders with at least one coin, ranked by ETH bought back for them.
- GET
/fomo/users/:handleParamsnoneReturns{ user: FomoUserDTO, deliveries: DeliveryDTO[] }tradingWallet(their fomo Robinhood wallet; null until found),tokens,creditedQuote,owedQuote(ETH waiting for the next buyback),pendingBuyback/sentBuyback($… held / delivered) and the latest deliveries. 404 if no coin was launched for them yet. - GET
/fomo/users/:handle/tradesParamsnoneReturnsFomoTradeDTO[]Their last 10 trades in the fomo app, on any chain; local is true for coins on fomopad.
Wallets
- GET
/wallets/:address/createdParamsnoneReturns{ items: CreatedTokenDTO[], launcherPaidTotalQuote, launcherOwedTotalQuote }Coins the wallet launched, each with
launcherPaidQuote(launcher fees already sent to it) andlauncherOwedQuote(earned, sent with the next payout), in ETH, plus both totals.claimableQuoteis deprecated and always 0. - GET
/wallets/:address/holdingsParamsnoneReturns{ items: WalletHoldingDTO[] }fomopad coins the wallet holds now, read on chain, most valuable first:
amount(exact, sell it all with it) andvalueQuote(ETH). - GET
/wallets/:address/tradesParamslimit≤100 (30),cursorReturnsPaginated<TradeDTO>
Launch and trade
- POST
/launch/imageParamsimage(data: URL)Returns{ imageUrl, imageMime }Pins the image to IPFS. 20 uploads per hour per IP.
- POST
/launch/buildParamscreator,match(fomo handle),name,symbol,imageUrl,imageMime?,description?,twitter?,telegram?,firstBuy?Returns{ token, curve, tx: EvmTx, website, firstBuy? }Checks the handle on fomo.family and builds one Pons launch from the creator wallet; token and curve are the addresses it creates, firstBuy the dev buy’s quote.
- GET
/trade/quoteParamsmint,side,amount,slippageBps,owner?(query string)ReturnsTradeQuoteRead-only. Shares the 60/min transaction-build limit.
- POST
/trade/buildParamssame as quote +owner(JSON body)Returns{ txs: EvmTx[], quote }Send txs in order from owner, each confirmed before the next. A sell may start with an approval.
RPC proxy, WebSocket, health
- POST
/rpcParamsJSON-RPC 2.0 request or batchReturnsUpstream JSON-RPC responseRead-only Robinhood Chain RPC, rate limited per IP. Sending transactions and eth_getLogs are refused.
- WS
wss://api.fomopad.pro/wsParamssubscribe/unsubscribe/pingReturnsWsServerMessageSee WebSocket below.
- GET
/healthParamsnoneReturns{ ok, db, redis, indexerLagSeconds }503 when the database or cache is down. indexerLagSeconds is how stale indexed data may be.
POST /rpc forwards read-only JSON-RPC (eth_call, eth_getBalance, eth_blockNumber, eth_getTransactionReceipt and similar) to a private Robinhood Chain node, so you can read state and wait for receipts without your own provider. It does not send transactions or serve eth_getLogs: send through your wallet or the public RPC https://rpc.mainnet.chain.robinhood.com.
curl -s https://api.fomopad.pro/rpc -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["<tx hash>"]}'07
WebSocket
Connect to wss://api.fomopad.pro/ws and send { op: 'subscribe', topics: [...] }. Up to 50 topics per socket; unknown topics are ignored. Send { op: 'ping' } every 25 s; the server replies { op: 'pong' } and drops sockets that stop responding.
| Topic | Events |
|---|---|
| trades | trade for every token: { trade: TradeDTO, token } with the new price, market cap, progress and status. |
| tokens | token:new and token:update, each with a full TokenDTO. |
| token:<address> | That token's trade and token:update, and buyback: { mint, epochId, handle, buybackQuote } when a fee claim credits the matched user. |
Messages from the server are { op: "event", topic, event }, { op: "pong" } or { op: "error", message }. Topics use the lowercase address.
// Browsers, Node 22+ (global WebSocket), or the `ws` package on older Node.
const ws = new WebSocket('wss://api.fomopad.pro/ws')
ws.onopen = () => {
ws.send(JSON.stringify({ op: 'subscribe', topics: ['trades', 'tokens', `token:${token}`] }))
setInterval(() => ws.send(JSON.stringify({ op: 'ping' })), 25_000)
}
ws.onmessage = (m) => {
const msg = JSON.parse(String(m.data))
if (msg.op !== 'event') return // 'pong' | 'error'
const e = msg.event
if (e.type === 'trade') console.log(e.trade.side, e.trade.quoteAmount, 'ETH', e.trade.symbol)
if (e.type === 'token:new') console.log('new coin', e.token.mint, 'for', e.token.match?.handle)
if (e.type === 'buyback') console.log('bought back', e.buybackQuote, 'ETH for', e.handle, 'from', e.mint)
}08
Fees & lifecycle
GET /config at runtime rather than hardcoding them. The values below are the defaults.| Parameter | Default |
|---|---|
| fees.tradeFeeBps | 280: 2.8% of the ETH side of every trade (Pons 1% curve fee + 1.8% creator tax) |
| fees.buybackBps | 150: 1.5% for the matched fomo trader, half buying the coin itself and half the platform token (buybackSelfPct) |
| fees.creatorBps | 50: 0.5% to the wallet that launched the coin, paid automatically |
| fees.platformBps | 50: 0.5% platform treasury |
| fees.protocolBps | 30: 0.3% to Pons (30% of its 1% fee) |
| buybackToken | { symbol: the platform token, mint: null until it launches (ETH accrues) } |
| token | 1,000,000,000 supply, 18 decimals, no mint function |
| curve.migrationQuoteThreshold | 4.2: ETH raised to graduate |
- CURVE. Trades hit the coin's Pons bonding curve.
progressis the share of the graduation threshold raised. - COMPLETE. The curve is full and has been swept; its Uniswap v4 pool is being created. Trading pauses (409) for seconds to minutes.
- MIGRATED. Liquidity sits in a Uniswap v4 pool (
dammPoolis its pool id, also its DexScreener pair) and is locked forever. The same fee split continues.
fomopad claims its side from the Pons fee escrow every few minutes (see /tokens/:address/fees), pays the launcher, keeps the platform share and credits the rest to the matched trader, which a buyback wallet spends on the coin and $… and delivers to their fomo Robinhood wallet (/fomo/users/:handle).
09
Errors & rate limits
Errors are { error: string, details?: unknown } with a 4xx or 5xx status. The /rpc proxy is the exception: it answers in JSON-RPC error format.
// 400: validation (details lists each field)
{ "error": "invalid request", "details": [{ "path": "symbol", "message": "symbol is letters and digits only" }] }
// 404: the fomo handle does not exist
{ "error": "fomo.family has no user @someone" }
// 409: the curve is full and the Uniswap v4 pool is not created yet
{ "error": "this coin is graduating; trading reopens on its Uniswap v4 pool in a moment" }| Status | Meaning |
|---|---|
| 400 | Validation failed. details is a list of { path, message } per field. Also amounts that round to zero. |
| 404 | Unknown token, unknown fomo handle (launch, /fomo/users/:handle), or the coin is not indexed yet. |
| 409 | The curve is full and graduating: trading pauses until its Uniswap v4 pool exists (seconds to minutes). |
| 413 | Request body over 6 MB (the image). |
| 422 | Simulation failed (slippage, balance, not enough ETH for gas) or not enough liquidity. error is human-readable; details holds the revert. |
| 429 | Rate limited. Read the RateLimit and RateLimit-Policy headers and back off. |
| 502 | Upstream failure: fomo.family, IPFS pinning or the chain RPC. Usually safe to retry. |
| 503 | A dependency is down (uploads not configured, database or cache unavailable). |
Rate limits
Per IP, fixed windows. Every response carries RateLimit and RateLimit-Policy headers; a 429 means wait for the window to reset.
| Scope | Limit |
|---|---|
| Every endpoint | 600 / min / IP |
| /launch/build, /trade/build, /trade/quote | 60 / min / IP |
| /launch/image | 20 / hour / IP |
| /fomo/search | 90 / min / IP |
| /rpc | 300 / min / IP |
10
Safety
- Never send a private key to the API. No endpoint accepts one and the server never needs one. You only send addresses; signing happens on your machine.
- A bot sends whatever the server returns, so check it first (the addresses are also in
/configunderponsanduniswap). A launch goes to the Pons factory (0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e) or LaunchAndBuy (0xe33E9E479dF8802cb0866d5d05258bEc4cF62948). A trade goes to the coin's curve (pool), the Uniswap Universal Router (0x204FAca1764B154221e35c0d20aBb3c525710498), or, for an approval, the coin itself or Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3). Check thatvalueis what you meant to spend and compare thequote(inAmount,minOut) with what you asked for. - Cap trade sizes and slippage in your own code. Use a dedicated hot wallet holding only what the bot needs, and keep the key out of version control.
- Always double-check the coin address on its fomopad page or on Blockscout.
New to fomopad? How it works covers the product side.