Public API
Launch and read Runitup tokens on any supported chain, from a bot, an agent, or your own app.
Runitup has a public API so you can launch a token from a script, a trading bot, an AI agent, or your own front-end. A token launched this way is identical to one launched on the website: same factory contract, same 75/25 fee split, same permanently locked liquidity.
No API key. No sign-up. Nothing here moves money or exposes anything private, so there is nothing to gate.
We never touch your keys
The API builds your transaction. It does not send it. You sign with a key Runitup never sees and broadcast it yourself. There is no endpoint that launches a token for you, and there never will be — that would mean holding your private key, which would turn one break-in into everyone's loss.
Chains
The API answers for every chain the platform runs on. Name one with chain, using either the slug
from token URLs or the numeric id — ?chain=robinhood and ?chain=4663 are the same request.
| Chain | Slug | Id | Pools quoted in | Venues |
|---|---|---|---|---|
| Robinhood Chain | robinhood | 4663 | ETH, USDG, or one of 30 tokenized equities | uniswap-v3, sushi-v3, uniswap-v4 |
Omit chain and you get Robinhood, which is what the API has always defaulted to.
Don't hardcode this table. GET /api/v1/config returns it live, including which quote assets each
venue accepts, and it will be right when this page is not. This page named a testnet for longer than
it should have, which is the argument for reading the endpoint rather than the docs.
Tokenized equities pair on Uniswap V4 only
An equity-quoted launch must pass venue: "uniswap-v4". This is enforced by the contracts, not by
the interface: the V3 adapters refuse the equities, so the same launch on uniswap-v3 or
sushi-v3 reverts. Equity pools are also quoted in an ERC-20, so a dev buy against one is pulled
from your wallet and needs an approval first — the API builds it for you, see
two transactions below.
These are third-party instruments whose issuer excludes US persons from holding them. That restriction applies however the token was acquired, and Runitup cannot enforce it.
Using it from an AI agent
There's a ready-made skill file — drop it into Claude Code, OpenCode, Hermes, OpenClaw or anything else that loads markdown skills, and the agent knows the whole flow without you explaining it:
https://runitup.gg/skills/runitup/SKILL.mdFor Claude Code, save it as .claude/skills/runitup/SKILL.md in your project (or
~/.claude/skills/ to have it everywhere). Most other frameworks take the raw URL directly. There's
a full walkthrough on the Agent Skill page.
There's also an llms.txt at the root, which is what agents look for when they want to find their way around a site on their own.
The flow
POST /api/v1/launch/preparewith your token's details and the chain- Sign and send the
approvaltransaction it returns, if it isn't null - Sign and send the
launchtransaction - Read the token address from the
TokenLaunchedevent in the receipt - Optionally
POSTan image and description to the metadata endpoint
Get the current rules
curl https://runitup.gg/api/v1/config
curl https://runitup.gg/api/v1/config?chain=arcWithout chain you get a chains array covering all of them, with the default chain's fields also
flattened at the top level so older callers keep working. With chain, you get that one.
Per chain you get the contract addresses, the venues actually deployed there, what each venue can pair against, and the live launch rules — the fee, the supply bounds, the starting market cap. These are read from the contract on every request and can change, so read them rather than pinning them.
Two things worth reading rather than assuming:
quoteAssetsis keyed by venue, and the lists differ. Tokenized equities appear underuniswap-v4and nowhere else; the V3 venues carry ETH and USDG. That is enforced by the contracts, so a launch submitted with an equity on a V3 venue reverts. Assets carrying"isRwa": trueare the equities.startingMcapQuoteWeiis denominated in ETH, not converted from a dollar figure. It reads 1.5 ETH today. There is astartingMcapUsdalongside it for the assets that price through dollars, but for an ETH-quoted launch the wei figure is the authority.
{
"defaultChainId": 4663,
"chains": [
{
"chain": { "chainId": 4663, "slug": "robinhood", "name": "Robinhood Chain",
"nativeCurrency": { "symbol": "ETH", "decimals": 18 } },
"quoteAsset": { "symbol": "ETH", "decimals": 18, "isWrappedNative": true, "isUsdStable": false },
"contracts": { "tokenFactory": "0x3677…", "launchConfig": "0x…" },
"launchRules": {
"launchFeeWei": "500000000000000",
"startingMcapQuoteWei": "1500000000000000000",
"venues": ["uniswap-v3", "sushi-v3", "uniswap-v4"],
"quoteAssets": {
"uniswap-v3": [{ "symbol": "ETH", "isNative": true, "decimals": 18, "isRwa": false }, …],
"uniswap-v4": [{ "symbol": "NVDA", "isNative": false, "decimals": 18, "isRwa": true }, …]
}
}
},
]
}A chain whose RPC is unreachable is left out of chains rather than failing the whole response.
Prepare a launch
curl -X POST https://runitup.gg/api/v1/launch/prepare \
-H 'Content-Type: application/json' \
-d '{
"chain": "arc",
"name": "My Coin",
"symbol": "MYC",
"totalSupply": "1000000000",
"creatorAddress": "0xYourWallet",
"devBuy": "10",
"venue": "uniswap-v3"
}'| Field | Required | Notes |
|---|---|---|
name | yes | Up to 64 characters |
symbol | yes | Up to 16 characters |
totalSupply | yes | Whole tokens, as a string. "1000000000" is one billion |
creatorAddress | yes | The wallet that will sign |
chain | no | Slug or id. Defaults to robinhood |
devBuy | no | How much of your own token to buy at launch, in whatever the pool is quoted in — ETH on an ETH pool, USDG on a USDG pool, shares on an equity pool |
quoteToken | no | The pool's other side. Defaults to ETH. Read the options from /api/v1/config |
feeRecipient | no | Defaults to creatorAddress |
venue | no | uniswap-v3 (default), sushi-v3, or uniswap-v4 where deployed |
Amounts in are decimal strings, not raw integers — write "10" for ten USDC, not "10000000".
Values coming back are raw integers as strings, because a JSON number would silently round a supply
of 1e21.
devBuyUsdc is the old name for devBuy
devBuyUsdc still works and means the same thing. It used to be parsed as six decimals while being
spent as eighteen, so a dev buy of "25" bought about 25 millionths of a millionth of an ETH.
Both names now read as the quote asset's own units. If you were working around that bug by
multiplying, stop.
One transaction, or two
It depends on what the pool is quoted in, and the response tells you which you got.
A pool quoted in the chain's native asset — ETH on Robinhood — takes the fee and the dev buy as
msg.value. There is nothing to approve, transactions.approval is null, and you send one
transaction.
A pool quoted in an ERC-20 — USDG or any of the tokenized equities — has no
native leg, so the factory pulls the dev buy with transferFrom. That needs an allowance first, so
transactions.approval is a real transaction and you send two. The launch still carries the fee as
msg.value; only the dev buy moves as a token.
approval is also null when your allowance already covers the dev buy, so a repeat launch doesn't
pay gas to approve what is already approved.
You get back:
{
"chainId": 4663,
"chain": { "chainId": 4663, "slug": "robinhood", "name": "Robinhood Chain" },
"quote": { "symbol": "USDC", "address": "0x3600…0000", "decimals": 6, "isNative": false },
"venue": "uniswap-v3",
"transactions": {
"approval": { "to": "0x3600…0000", "data": "0x095ea7b3…", "value": "0" },
"launch": { "to": "0x9Ec9…D19f", "data": "0x1f6a5b96…", "value": "500000000000000" }
},
"cost": {
"launchFeeWei": "500000000000000",
"devBuyWei": "0",
"devBuyQuoteAmount": "10000000",
"totalValueRequired": "500000000000000",
"currentBalance": "29057017849920000000",
"sufficientBalance": true,
"quoteBalance": "29057017",
"sufficientQuoteBalance": true
}
}totalValueRequired is what the launch transaction must carry as msg.value — already set on
transactions.launch.value, so you can send it as-is.
devBuyWei is the dev buy when it rides as native value; devBuyQuoteAmount is the same buy when it
moves as an ERC-20. Exactly one of them is non-zero.
sufficientBalance covers the native side and sufficientQuoteBalance the token side. Both are
reported, not enforced — if either is false the transaction is still built, on the assumption
you're about to fund the wallet.
Send it
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const wallet = createWalletClient({ account, transport: http(RPC_URL) });
const { transactions } = await fetch(".../api/v1/launch/prepare", { ... }).then(r => r.json());
// Only present for an ERC-20-quoted pool, and only when the allowance is short.
if (transactions.approval) {
const hash = await wallet.sendTransaction(transactions.approval);
await publicClient.waitForTransactionReceipt({ hash });
}
const hash = await wallet.sendTransaction(transactions.launch);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
// TokenLaunched in the receipt logs carries your new token's addressWait for the approval's receipt, not just its hash. Sending the launch while the approval is
still pending reverts with ERC20: transfer amount exceeds allowance, which does not obviously mean
"you went too early".
Add an image and description
These aren't on-chain, so they're a separate call after the launch confirms:
curl -X POST https://runitup.gg/api/tokens/0xYourToken/metadata \
-H 'Content-Type: application/json' \
-d '{ "name":"My Coin", "ticker":"MYC", "totalSupply":"1000000000000000000000000000",
"creatorAddress":"0xYourWallet", "imageUrl":"https://…", "description":"…",
"ammVenue":"uniswap-v3", "launchType":"quick", "chainId":4663 }'Do this straight after launching. It makes your token appear on the site immediately rather than waiting for the indexer, and sets the venue so the first buys route correctly.
Note totalSupply here is in raw units (18 decimals) — the opposite convention to the prepare
call, because this endpoint mirrors what the indexer stores.
Reading data
These are open too, and need nothing. All of them take ?chain= to narrow to one chain; without it
they answer across every chain.
| Endpoint | Returns |
|---|---|
GET /api/tokens | Every live token with price, market cap, volume |
GET /api/tokens/{address} | One token, plus holders and recent trades |
GET /api/tokens/{address}/candles?interval=1h | OHLC price history |
GET /api/activity | Recent launches and trades across the platform |
GET /api/statistics | Platform totals |
GET /api/wallet/{address}/portfolio | A wallet's holdings, across all chains |
An address is not unique across chains
Every chain runs the same factory deployed from the same wallet, so two chains can mint tokens at
the same address. When you look a token up by address, pass ?chain= if you know which one you
mean — otherwise you may get its namesake on another network. Every token returned carries
chainId, and every token page URL includes the chain: /token/arc/0x….
Reading amounts
Every amount on a token — market cap, volume, trade sizes — is denominated in that token's pool's
quote asset, not in ETH and not in dollars. Tokens carry quoteToken, quoteDecimals and
quoteSymbol to say which; all three are null for an ETH-quoted token, and null there means ETH
rather than missing data.
Getting this wrong is the single most expensive mistake against this API. A USDG-quoted token reports six-decimal amounts that are already dollars; reading them as eighteen-decimal ETH and then applying an ETH rate is wrong by about a trillion, and produces a plausible-looking number rather than an error.
Errors
Validation failures return 400 with the offending field named:
{ "error": "invalid_request", "field": "venue",
"message": "venue must be one of: uniswap-v3, sushi-v3, uniswap-v4 on Robinhood Chain." }An unknown chain returns 404 and lists the ones that exist:
{ "error": "unknown_chain", "message": "No such chain: solana.",
"available": [{ "chainId": 4663, "slug": "robinhood" }] }Parameters are checked against the live contract rules on the chain you named, so a request that
returns 200 should not revert for a reason we could have caught. Gas, balance and network
conditions are still yours to handle.
