Launch

Developers · REST + WebSocket

API

Launch coins for fomo traders, quote and trade, and stream live activity from your own scripts and bots. The server builds Robinhood Chain transactions; you sign and send them from your own wallet.

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.

CORS: browsers only allow REST calls from fomopad's own origins. Server-side code, scripts and bots are not affected. A web app on another origin must call the API from its own backend. The WebSocket accepts any origin.

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
// 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:

shell
# 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).
  • imageUrl and imageMime from 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.

response
{ "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: BUY or SELL.
  • 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, txs starts 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 explicit gas so graduation cannot run short: keep it.
  • Graduating. While status is COMPLETE, quotes and builds return 409 until the Uniswap v4 pool exists.
  • Graduated. Once status is MIGRATED, 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/config
    Paramsnone
    ReturnsLaunchpadConfigDTO

    chain (id, name, explorer), the pons and uniswap contract addresses, launchFee (ETH), creatorTaxBps, quote (ETH: the zero address, 18 decimals), fees in bps (tradeFeeBps, protocolBps, creatorBps, platformBps, buybackBps), buybackToken (mint is 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/stats
    Paramsnone
    ReturnsPlatformStatsDTO

    tokens, 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/tokens
    Paramssort new|volume|mcap|progress|lastTrade|lastBuy|buyback (new), status CURVE|COMPLETE|MIGRATED, q (name, symbol, exact address or fomo handle, ≤64), match (fomo handle), creator, limit ≤100 (30), cursor
    Returns{ items: TokenDTO[], nextCursor }

    sort=progress only lists CURVE tokens unless you pass status. lastBuy is what the site calls trending.

  • GET/tokens/:address
    Paramsnone
    ReturnsTokenDTO

    Takes the coin's address or its curve's (a coin's on-chain website is /token/<curve>); so do the sub-routes below. mint is the coin's address (lowercase), pool its Pons curve, dammPool its 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) and buybackQuote (ETH credited to them). 404 if unknown.

  • GET/tokens/:address/trades
    Paramslimit ≤200 (50), cursor
    ReturnsPaginated<TradeDTO>

    Newest first. signature is the transaction hash, slot the block number, venue CURVE or V4 (graduated).

  • GET/tokens/:address/candles
    Paramstf 1m|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/holders
    Paramslimit ≤200 (50)
    ReturnsHolderDTO[]

    share is 0..1 of total supply; label marks the curve, the Uniswap v4 pool, the platform and the creator.

  • GET/tokens/:address/fees
    Paramslimit ≤100 (20), cursor
    ReturnsPaginated<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/recent
    Paramslimit ≤50 (20)
    ReturnsTradeDTO[]

    All tokens, newest first. A plain array, not paginated.

fomo users

  • GET/fomo/search
    Paramsq (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/users
    Paramslimit ≤50 (12)
    ReturnsFomoUserDTO[]

    Traders with at least one coin, ranked by ETH bought back for them.

  • GET/fomo/users/:handle
    Paramsnone
    Returns{ 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/trades
    Paramsnone
    ReturnsFomoTradeDTO[]

    Their last 10 trades in the fomo app, on any chain; local is true for coins on fomopad.

Wallets

  • GET/wallets/:address/created
    Paramsnone
    Returns{ items: CreatedTokenDTO[], launcherPaidTotalQuote, launcherOwedTotalQuote }

    Coins the wallet launched, each with launcherPaidQuote (launcher fees already sent to it) and launcherOwedQuote (earned, sent with the next payout), in ETH, plus both totals. claimableQuote is deprecated and always 0.

  • GET/wallets/:address/holdings
    Paramsnone
    Returns{ items: WalletHoldingDTO[] }

    fomopad coins the wallet holds now, read on chain, most valuable first: amount (exact, sell it all with it) and valueQuote (ETH).

  • GET/wallets/:address/trades
    Paramslimit ≤100 (30), cursor
    ReturnsPaginated<TradeDTO>

Launch and trade

  • POST/launch/image
    Paramsimage (data: URL)
    Returns{ imageUrl, imageMime }

    Pins the image to IPFS. 20 uploads per hour per IP.

  • POST/launch/build
    Paramscreator, 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/quote
    Paramsmint, side, amount, slippageBps, owner? (query string)
    ReturnsTradeQuote

    Read-only. Shares the 60/min transaction-build limit.

  • POST/trade/build
    Paramssame 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/rpc
    ParamsJSON-RPC 2.0 request or batch
    ReturnsUpstream JSON-RPC response

    Read-only Robinhood Chain RPC, rate limited per IP. Sending transactions and eth_getLogs are refused.

  • WSwss://api.fomopad.pro/ws
    Paramssubscribe / unsubscribe / ping
    ReturnsWsServerMessage

    See WebSocket below.

  • GET/health
    Paramsnone
    Returns{ 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.

shell
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.

TopicEvents
tradestrade for every token: { trade: TradeDTO, token } with the new price, market cap, progress and status.
tokenstoken: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.

ws.ts
// 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

Read fees and thresholds from GET /config at runtime rather than hardcoding them. The values below are the defaults.
ParameterDefault
fees.tradeFeeBps280: 2.8% of the ETH side of every trade (Pons 1% curve fee + 1.8% creator tax)
fees.buybackBps150: 1.5% for the matched fomo trader, half buying the coin itself and half the platform token (buybackSelfPct)
fees.creatorBps50: 0.5% to the wallet that launched the coin, paid automatically
fees.platformBps50: 0.5% platform treasury
fees.protocolBps30: 0.3% to Pons (30% of its 1% fee)
buybackToken{ symbol: the platform token, mint: null until it launches (ETH accrues) }
token1,000,000,000 supply, 18 decimals, no mint function
curve.migrationQuoteThreshold4.2: ETH raised to graduate
  1. CURVE. Trades hit the coin's Pons bonding curve. progress is the share of the graduation threshold raised.
  2. COMPLETE. The curve is full and has been swept; its Uniswap v4 pool is being created. Trading pauses (409) for seconds to minutes.
  3. MIGRATED. Liquidity sits in a Uniswap v4 pool (dammPool is 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.

examples
// 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" }
StatusMeaning
400Validation failed. details is a list of { path, message } per field. Also amounts that round to zero.
404Unknown token, unknown fomo handle (launch, /fomo/users/:handle), or the coin is not indexed yet.
409The curve is full and graduating: trading pauses until its Uniswap v4 pool exists (seconds to minutes).
413Request body over 6 MB (the image).
422Simulation failed (slippage, balance, not enough ETH for gas) or not enough liquidity. error is human-readable; details holds the revert.
429Rate limited. Read the RateLimit and RateLimit-Policy headers and back off.
502Upstream failure: fomo.family, IPFS pinning or the chain RPC. Usually safe to retry.
503A 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.

ScopeLimit
Every endpoint600 / min / IP
/launch/build, /trade/build, /trade/quote60 / min / IP
/launch/image20 / hour / IP
/fomo/search90 / min / IP
/rpc300 / 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 /config under pons and uniswap). 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 that value is what you meant to spend and compare the quote (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.