Skip to content

API reference ​

Public read-only endpoints at https://api.samefirst.com. All responses are JSON; amounts are wei strings (1 BNB = 10¹⁸ wei) and times are Unix seconds.

Rate limits and caching ​

ItemRule
Most endpoints120 requests per minute per IP
/api/flap*, /api/meta, /api/tx*, /api/bind/*30 per minute per IP (reads the chain, simulates a call or calls Flap)
Over the limit429 with retry_after_s in the body
CachingResponses carry cache-control and x-cache: HIT/MISS; the same URL returns the same data within its cache window
EndpointCache
/api/launches, /api/launch/*, /api/recent3 s
/api/me/*, /api/airdrops*5 s
/api/stats, /api/profile/*10 s
/api/dao, /api/hot15 s
/api/name/*, /api/address/*, /api/search30 s
/api/flap, /api/flap/estimate, /api/vanity, /api/timeline60 s
/api/binding/*3 seconds
/api/decode5 minutes
/api/health, /api/meta, /api/admin/*, /api/tx*, /api/bind/*not cached

For anything more frequent, read the on-chain events directly (see Contracts).

Rounds ​

GET /api/launches ​

List rounds. By default open rounds come first, then full, then launched; each group is ordered by the sort.

ParamNotes
sortprogress (default, fill progress) · people · amount
stageOpen · Full · Launched · Done · Refunding; all if omitted
limitdefault 50, max 200

Each item has: address, creator (the address that sent the transaction), creator_id (the Agent ID the issuer acts for: itself, or the ID it is bound to; null if none), creator_name, template_id (game template, 0 = Classic), binding (binding contract this round locked in), kind (TaxV6/TaxV6Vault/PlainV7), name, symbol, meta, pool, quote_token, slip_bps, fee_bps, buy_tax_bps, sell_tax_bps, min_per_id, max_per_id, target, raised, left_bnb, progress (0–1, fill progress), subscribers, stage, created_time, filled_at, launched_at, graduated_at (when it graduated on Flap and liquidity moved to the DEX), token, spent, tokens_bought, fee_charged, plus tax_split (tax distribution in bps: mkt creator, burn, div dividends, lp liquidity; null for a standard token) tax_to_dao (whether the whole creator share goes to the Samefirst DAO), flap_blocked (Flap refuses the round's name, symbol or both, so it can't launch even when full; null when fine or not checked yet) and refund_at (when such a full round can be refunded; otherwise null).

GET /api/launch/:addr ​

All fields for one round, plus its current subscribers (subscribers: who is the address that paid and receives, id the Agent ID it counts for, name that ID's name; with a bound wallet who ≠ id), exits and settlements, and stats:

FieldNotes
first_sub_time, exits, exited_peoplefirst subscription time; number of exits and of people who exited
distributed, distributed_atpeople paid out at distribution, and when it finished
graduated_at, dex_poolgraduation time and DEX pool
holdersaddresses holding the token after launch (replayed from its on-chain transfers)
real_holders, real_sharehow many of them hold an Agent ID, and their share of the supply (0–1)
top10_shareshare of the 10 largest holders, leaving out contracts (bonding curve, DEX pool, the round) unless the contract itself holds an Agent ID

GET /api/launch/:addr/wall ​

Name wall: subscription events (subscribe, top up, exit). limit defaults to 100, max 500.

GET /api/me/:addr ​

addr may also be a name (with or without .ai); looking needs no wallet. An address's Agent ID, rounds it joined or created, its amounts, and the DAO airdrops it received (airdrops: one row per batch and token, with amount, delivered or not, transaction hash). Joined rounds also carry my_balance (current holding), my_since (when this holding began), my_received, my_refunded and my_exit_back; my_payee is the address that actually paid and receives in that round (the bound wallet when the ID joined through it). For an Agent ID, rounds its bound wallet joined for it are listed too. binding is the first three fields of /api/binding below. profile is as below.

GET /api/profile/:id ​

Name-card data; id is a name (with or without .ai) or an address. profile has joined (rounds joined), at_launch (rounds still in at launch), created, holding (launch tokens still held), hold_days (days the longest current holding has lasted), airdrops (airdrop batches received), early (among the first 50,000 claims) and pending_airdrops (airdrops being sent but not yet delivered, summed per token).

GET /api/hot?limit=10 ​

Real-person leaderboard: open rounds by current subscribers, launched ones by holders with an Agent ID (real_people), most first, up to 50.

Wallet binding ​

GET /api/binding/:who ​

who is an address or a name. Returns:

FieldMeaning
contractbinding contract new rounds use
walletwhen who is an Agent ID: its bound wallet (address, bound_at, tx), or null
idwhen who is a wallet: the Agent ID it acts for (address, name, bound_at, tx), or null
next_bind_at, nonceAgent ID: earliest time it may bind again (0 = now), current signature nonce
wallet_free_atwallet: earliest time it may be bound (0 = now)
historylast 50 binding events: kind (bound / unbound / revoked), id_addr, wallet, detail (how it was unbound: wallet / id / id_signature / replaced), block_time, tx_hash

Agent hand-off (order codes) ​

The Agentic Wallet only works through chat. These endpoints prepare and check a transaction or an authorization to sign, store it under a 6-character code (valid 30 minutes, fetched once) for the Agent to run with the Samefirst skill. They only ever produce calls to Samefirst round contracts or the binding contract, never token approvals or transfers.

POST /api/tx ​

json
{ "action": "subscribe", "launch": "0x…", "from": "0x…", "amount": "0.05" }
FieldMeaning
actionsubscribe (subscribe or top up) · exit · claim · refund · withdraw · withdraw_tokens
launchround contract
fromthe address that will send it (Agentic Wallet or bound wallet)
amountBNB to subscribe (or amount_wei); subscribe only
langoptional; en gives an English summary (default Chinese); /api/bind/request takes it too
checkoptional; true only checks and simulates: the same transaction comes back without an order code (no code / expires_at)

Checks, in order: the round exists and is at the right stage; which Agent ID from acts for; whether that ID already joined this round from another address; the amount against the per-person limits and the room left; finally a simulation from from. On failure: 400 with {"error": "reason (Chinese)", "code": "ErrorCode"}. On success:

json
{ "kind": "tx", "code": "NHJQU3", "expires_at": 1793008991, "chain_id": 56, "action": "subscribe",
  "launch": "0x…", "from": "0x…", "to": "0x…", "value": "50000000000000000", "value_bnb": "0.05",
  "data": "0x8f449a05", "function": "subscribe()", "summary": "…" }

POST /api/bind/request ​

json
{ "name": "alice", "wallet": "0x…" }        // bind
{ "action": "unbind", "name": "alice" }     // unbind the current wallet

Checks the name, cooldowns and whether the wallet is taken, and returns the EIP-712 authorization to sign: kind: "sign", code, signer (the Agent ID address), wallet, binding, deadline, digest (the contract's own hash, to check the signature against), summary, and typed_data (domain Samefirst Binding / 1 / 56 / binding contract, primaryType Bind or Unbind; ready for eth_signTypedData_v4).

GET /api/tx/:code ​

The Agent fetches either of the above with the code (verbatim, typed_data keys in field order). Once only: again returns 410 (CodeUsed); expired returns 410 (CodeExpired); unknown returns 404.

Verification and entry lists ​

Each round's issuer picks one or more verification methods, and meeting any one of them is enough to subscribe: identity sources registered in the factory (agent_id Binance Agent ID, babt BABT, holding token balance checked at subscription), the issuer's whitelist (whitelist, Agent IDs or ordinary addresses), or open to every address (open). In /api/launch/:addr and /api/launches, ways lists the round's methods (in the order the contract tries them), sources gives the identity-source details (the token and threshold for holdings), and open_mode says whether the round is open to every address; /api/launches?way=babt and the like filter by method. The identity settled on someone's first subscription (an Agent ID, or the address itself) never changes afterwards. The issuer can also keep their own blacklist; the platform also keeps a global blacklist and banned token names. The rules are enforced on-chain (SamefirstLists, SamefirstGate); these endpoints make them visible and usable. Blocking an Agent ID also blocks the wallet bound to it; people already in are unaffected.

GET /api/lists ​

The platform blacklist blocked (address, name, reason, time, tx), banned token names names, the issuers' removal limit max_unblocks and the appeal link appeal; enabled false means the lists aren't live yet. Public page: samefirst.com/lists.

GET /api/launch/:addr/eligible/:who ​

who is an address or an Agent ID name. Returns ok and the identity it joins as, id (an Agent ID or the address itself); when it can't join, code: 2 platform blacklist, 3 this round's blacklist, 4 meets none of the round's verification methods, 5 the round's whitelist isn't available, with the reason in text; when joining via the whitelist, proof is the whitelist proof to subscribe with. The answer comes straight from SamefirstGate.canEnter on-chain.

POST /api/whitelist ​

{"names":["alice","bob.ai"],"addresses":["0x…"]}, up to 50,000. Addresses holding an Agent ID count as that ID; any other address counts as itself. Returns the Merkle root (added to a round as a whitelist batch, at creation or later by the issuer), the valid count and what couldn't be used (invalid). Once a round on-chain uses the root, the full list is published on IPFS and its CID shows in that round's whitelist_cid and in the endpoint below.

GET /api/whitelist/:root ​

All addresses and names of a whitelist. Leaf = keccak256(bytes.concat(keccak256(abi.encode(address)))) (an Agent ID or an ordinary address), pairs hashed sorted (OpenZeppelin MerkleProof): anyone can rebuild the root from the IPFS copy.

POST /api/whitelist/snapshot · GET /api/whitelist/snapshot/:id ​

Holder snapshot: {"token":"0x…","min":"<smallest unit>","block":<block, 0 = latest>} starts a background job and returns an id; polling returns status (running / done / failed), progress, and when done the root, cid and count. The platform reads every holder at or above the threshold at that block (sorted by balance) and stores them as a whitelist batch, used just like the result of POST /api/whitelist. It needs the token's full transfer history, so tokens created too long ago return failed.

Subscribing to a whitelist round ​

Call subscribe(bytes32[] proof) (selector 0xe38c9a53) with the proof; a plain subscribe() is refused for an address that can only get in through the whitelist. POST /api/tx works out whether a proof is needed and adds it; when someone is barred it returns GloballyBlocked, BlockedInRound, NotEligible, NotWhitelisted or NoWhitelist; a round hidden by the platform returns Hidden. /api/launch/:addr also has: in launch, gated, whitelisted (exclusive round), open_to_all (the issuer lifted the whitelist), open_at (when subscriptions open; 0 = at creation), preparing (not open yet; /api/tx answers NotOpenYet) and max_unblocks; the whitelist batches whitelists (root, cid, count, time) and their total whitelist_count; the round blacklist round_blocks (who, name, blocked, unblocks, time); and the project info info (below). A new stage, Cancelled, is a round its issuer dropped while preparing.

Community, launch wallet and vaults ​

  • GET /api/launch/:addr/comments · POST /api/launch/:addr/comments: comments. Posting takes a wallet signature over a message starting with "Samefirst 评论" (message, signature); only the round's issuer, its subscribers and, after launch, token holders can post, one per minute, up to 500 characters. POST /api/comments/:id/report reports a comment.
  • Rounds hidden by the platform: hidden is true in /api/launch/:addr; they don't appear in listings and take no new subscriptions.
  • GET /api/launcher/:addr: this issuer's (counted per Agent ID if they have one) fixed launch address launcher, its balance, and the gas to prepay at creation prepay (wei; 0 when the balance is enough). For the first 10 minutes after a round fills, only this address can launch it.
  • GET /api/vaults: the Flap vault templates to choose from (those featured in Flap's vault store, without the Split Vault), with category and names and descriptions in Chinese and English; GET /api/vault/:factory?quote=0x…: whether a factory can be used (allowed, reasons), whether it's officially audited (audited), and its description of vaultData (schema).
  • dex_pair in /api/launch/:addr is the pair with the most liquidity on DexScreener after launch, used for the price chart.

Tax dividends ​

For rounds that give their tax to the DAO, the tax recipient on Flap is the round's own splitter contract (SamefirstSplitter, whose address is derived from the round's address and the DAO share). /api/launch/:addr has splitter (null if none), splitter_bps (DAO share, in basis points) and splitter_deployed. In /api/flap, samefirst has splitter_factory and split_dao_bps: the factory address and the default DAO share for new rounds.

GET /api/launch/:addr/dividends/:who ​

What who (the subscribing address) can claim now: alloc (tokens originally allocated), factor (holding factor, 1e18 = 100%), deployed, and per asset asset (the zero address for BNB), symbol, due (before the holding factor) and pay (after it). Before the splitter contract is deployed, this is estimated from the balance at its address.

GET /api/dividends/:who ​

who is an address or an Agent ID name: the rounds with a splitter contract it took part in, each as above, plus payee (the address that actually claims; subscriptions made by a bound wallet on its behalf are listed too).

Claiming ​

action: "dividend" in POST /api/tx: the transaction goes to the splitter contract and calls claim(address[]) (selector 0x318d9e5d) to claim every asset with a balance. Returns NotDeployed if the splitter contract isn't deployed yet, NotSubscriber if the address isn't a subscribing address, and NothingToDo if there's nothing to claim.

Tax to an X account ​

A tax token's tax can go to an X account (SamefirstXSplitter; its address follows from the round's address, the account's keccak256("x:" + numeric id), the account's share and the claim window). /api/flap's samefirst block has x_splitter_factory (null = not offered), x_window (claim window, seconds) and x_max_bps (8000).

  • GET /api/x/user?handle=foo: look an account up: id, handle, name, avatar, followers; 404 if none.
  • POST /api/x/intent {launch, x_id, x_bps, window}: the create page registers the round's X splitter before sending the creation; returns splitter (= the on-chain predict). Once the round exists the indexer keeps only the one that really is its tax recipient.
  • x_tax in /api/launch/:addr (null if none): handle, name, avatar, x_bps, window, deployed, deadline, status (waiting / claimed / expired: gone to the DAO), claimed_at, paid (per asset: paid to the account wallet, to the DAO dao, forfeited to the DAO forfeit). The wallet itself is not public.
  • Claiming: GET /api/x/login?back=<a page of the site> signs in with X and returns with #xs=<session>; GET /api/x/me (Authorization: Bearer <session>) lists the rounds that name the account and what waits; POST /api/x/claim {splitter, wallet} names the wallet, which the platform binds on chain with its claim key (bind; no gas or signature from the owner); POST /api/x/logout.

Project info ​

Community links, a contact and announcements the issuer shows on samefirst.com: off-chain, editable any time, every version kept and public.

POST /api/launch/:addr/info ​

{"message":"…","signature":"0x…"}: text the issuer (the creating address, or a wallet bound to the issuer's Agent ID) signs with personal_sign, carrying the content itself:

Samefirst 项目资料
首发:0x…
时间:<unix seconds>

{"links":[{"type":"telegram","url":"https://t.me/…"}],"contact":"…","note":"…"}

type is telegram, discord, x, website or other; up to 10 http(s) links; contact up to 200 characters, note up to 1,000. A signature is good for 10 minutes and must be newer than the stored version; at most 10 edits an hour.

GET /api/launch/:addr/info/history ​

Every version, newest first, with the signed text and signature, so anyone can verify them.

info in /api/launch/:addr: links, contact, note, updated_at, versions, has_address (the text contains a wallet address); when the operators hid it, only hidden and hidden_reason.

Error codes ​

Contracts revert with error codes, which an Agent's preview may show only as hex.

EndpointMeaning
GET /api/decode?data=0x…turns revert data (or a whole error message containing it) into words: code, selector, text (Chinese)
GET /api/errorsevery error code with its 4-byte selector and text

Samefirst DAO ​

GET /api/dao ​

Total income (launch_fees, exit_fees, swept), recent flows, and the DAO wallet safe: its address and live BNB balance (bnb_wei).

totals: tax commission (commission_bnb), tax share (tax_share_bnb: BNB the rounds' splitters paid the DAO), donations (donations_bnb, donations count including token gifts), spending (spent_bnb, split into token buys spent_buy_bnb and other spent_other_bnb), airdrop_batches, airdrop_people.

ledger: the multisig's inflows and outflows, newest first. Each entry has dir (in / out), kind, time, tx, n (transfers) and amounts:

kindMeaning
launch_fee · exit_fee · sweeplaunch fees, exit fees (merged per round and day), swept dust
tax_commissionFlap's tax commission from a tax token (symbol = the source token; paid in a pool token it carries token_symbol and decimals)
tax_shareThe tax share a round's splitter paid the DAO, one line per round and day; via is subscribers (subscriber splitter) or x (X-account splitter); paid in a pool token it carries token_symbol and decimals. Details: /api/dao/detail?kind=tax_share&launch=…&day=…
donationBNB sent straight to the multisig
token_intokens sent to the multisig (token_symbol, decimals)
buy · spendBNB sent by the multisig: to Flap or PancakeSwap counts as a token buy, anything else as spending (each MultiSend entry counted)
airdrop · token_outtokens sent by the multisig, one entry per transaction (tokens lists each token); entries matched to an airdrop batch carry batch_id and batch_name

GET /api/airdrops ​

Published airdrop batches (sending, done, cancelled), newest first. Each has id, name, note, rule (all_ids / launches / custom), tokens (per token: token, symbol, decimals, total_wei, sent_wei), recipients, sent (people fully paid), transfers, transfers_sent, status (active / done / cancelled), created_at, published_at, done_at, snapshot_block.

GET /api/airdrops/:id ​

One batch and its recipients, paged. Params: page, size (max 500), q (address or name prefix), status (pending still owed something / sent fully paid). Each recipient has address, handle, status (pending / partial / sent) and items (one per token: pos, amount_wei, status, tx_hash, sent_at).

GET /api/airdrops/:id/csv ​

The full list as CSV, one row per recipient and token, with delivery status.

Flap parameters ​

GET /api/flap ​

The samefirst field holds the factory's current settings for new rounds: factory, binding, fee_bps, min_target / max_target (raise range, wei), min_per_id_floor, official_vaults_only, abandon_after (seconds a full round waits for its launch), paused, and templates (each with id, name, impl, status, ready_at; active ones carry creation_cost, what creating a round costs).

The rest are Flap's current parameters read from chain: Portal version, trading fee (bps), BNB curve (r, h, k, graduation reserve), and the pool tokens Flap lists (enabled = Flap has opened it). Any other token can be checked with GET /api/flap/custom?token=…&bnb=…: whether Flap admits it (with Flap's signed price for a custom one), the PancakeSwap path the platform would launch through, what that much BNB buys, and the price impact (impact_bps; above 500 the platform does not launch).

GET /api/flap/estimate?progress=58&pool=BNB ​

Estimate the raise needed to reach a curve progress after launch, on the BNB curve. progress is in (0, 100]; non-BNB pools include 20% headroom at 100%.

GET /api/flap/namecheck?name=MyCoin&symbol=ETH ​

Asks Flap's anti-scam module whether it would refuse this name and symbol (it refuses well-known coins like ETH, BTC, BNB, USDT). Returns {"name_ok": true, "symbol_ok": false}. A refused round fails at launch (BlacklistedNameOrSymbol), so check before creating one. Cached 10 minutes.

Logo upload ​

POST /api/meta ​

Used when creating a launch: uploads the token logo and its details to Flap, pinned on IPFS, and returns a cid. That cid is the token's meta field, which Flap and samefirst.com both read the logo and links from. Browsers can't call Flap's upload endpoint directly because of cross-origin rules, so our backend forwards it.

The request is multipart/form-data:

FieldRequiredNotes
imageyesLogo: PNG, JPEG, WebP, SVG or GIF, up to 3 MB (same as Flap); square, 512×512 or more, is best
descriptionnoToken description, up to 256 characters (same as Flap)
websitenoWebsite link
twitternoX link
telegramnoTelegram link

Success returns 200:

json
{ "cid": "bafkreictuhft3yl5icnswbh52nhth3sjjcgrk7d4qnxglhuqehwgfznw3q" }

The cid points to a JSON document (description, website, twitter, telegram, image, …) whose image is the picture's own CID. You can view it through Flap's gateway, e.g. https://flap.mypinata.cloud/ipfs/<cid>.

StatusMeaning
400No image, image over 3 MB or not a supported format, description over 256 characters, or a malformed form
429Rate limited (30 per minute per IP, shared with /api/flap*)
502Flap's upload failed or returned no cid (the body's detail carries Flap's error)

Example:

bash
curl -X POST https://api.samefirst.com/api/meta \
  -F "image=@logo.png;type=image/png" \
  -F "description=One-line pitch" \
  -F "website=https://example.com" \
  -F "twitter=https://x.com/example"

Uploads are public and permanent on IPFS and can't be deleted, so only upload the material meant for the token.

Other ​

The admin endpoints under /api/admin/* are for DAO administration only (wallet sign-in) and aren't listed here.

Agent ID ​

EndpointNotes
GET /api/statsTotal Agent IDs and growth
GET /api/name/:handleLook up by name (without .ai)
GET /api/address/:addrLook up by address
GET /api/search?q=Name prefix search, up to 20
GET /api/recent?limit=Latest registrations, default 50, max 200
GET /api/timeline?bucket=3600&hours=72Registrations over time
GET /api/vanityThree-character names, lucky-number names, earliest names
GET /api/healthService status and cache stats