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.

ChainSlugIdPools quoted inVenues
Robinhood Chainrobinhood4663ETH, USDG, or one of 30 tokenized equitiesuniswap-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.md

For 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

  1. POST /api/v1/launch/prepare with your token's details and the chain
  2. Sign and send the approval transaction it returns, if it isn't null
  3. Sign and send the launch transaction
  4. Read the token address from the TokenLaunched event in the receipt
  5. Optionally POST an 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=arc

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

  • quoteAssets is keyed by venue, and the lists differ. Tokenized equities appear under uniswap-v4 and 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": true are the equities.
  • startingMcapQuoteWei is denominated in ETH, not converted from a dollar figure. It reads 1.5 ETH today. There is a startingMcapUsd alongside it for the assets that price through dollars, but for an ETH-quoted launch the wei figure is the authority.
  • amm is where the pool lives. Per venue in venues: the Uniswap V4 singleton (poolManager) or the V3 factory, the positionManager that holds the locked position, our router and quoter on that venue, the suite's adapter, and for V4 the fixed parts of every Quick launch's pool key. On Arc the singleton is Uniswap's official Arc PoolManager, so a token's pool is an ordinary Uniswap V4 pool: sort the pair's two currencies ascending, then keccak256(abi.encode(currency0, currency1, poolKey.fee, poolKey.tickSpacing, poolKey.hooks)) is the pool id, which is also the pair id in a DexScreener URL. A bot that already drives Uniswap V4 elsewhere needs nothing from us beyond that block. If you call our V4 router rather than the singleton, read routerVersion first, exactly as for the V3 entries: 1 answers swapExactInputSingle(address tokenIn, address tokenOut, …) and builds the pool key itself (selector 0xb6a75e4e); 2 takes the key, swapExactInputSingle(PoolKey key, address tokenIn, …) (0xf2cfd9d0). Robinhood Chain's router is 1, every newer suite's is 2. Encode for the wrong one and the call matches no function and reverts with no reason.
{
  "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…" },
      "amm": {
        "uniswap-v4": {
          "poolManager": "0x8366a39CC670B4001A1121B8F6A443A643e40951",
          "positionManager": "0x…", "adapter": "0x…", "router": "0x…", "routerVersion": 1, "quoter": "0x…",
          "poolKey": { "fee": 10000, "tickSpacing": 200, "hooks": "0x0000000000000000000000000000000000000000" }
        },
        "uniswap-v3": { "factory": "0x…", "router": "0x…", "routerVersion": 2, "quoter": "0x…", "feeTier": 10000 }
      },
      "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

Quick launches only

This endpoint builds Quick launches, on every chain it serves — today Robinhood Chain and Arc. Advanced, taxed, dynamic-tax and sniper-tax launches are not available through the API. A request carrying any tax field (buyBps, sellBps, the share fields, sniperSeconds, dynamic, tax, or a launchType other than quick) is refused with the offending fields named, rather than quietly built as a Quick launch:

  • taxed_launches_unavailable (HTTP 400) on a chain where taxed launches are open. They are made on the site's launch form.
  • taxed_launches_paused (HTTP 503) on a chain where they are switched off, which for now is every chain.

/api/v1/config reports this per chain under launchRules.launchTypes and launchRules.taxedLaunches (available, viaApi, reason).

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-v4"
  }'
FieldRequiredNotes
nameyesUp to 64 characters
symbolyesUp to 16 characters
totalSupplyyesWhole tokens, as a string. "1000000000" is one billion
creatorAddressyesThe wallet that will sign
chainnoSlug or id. Defaults to robinhood
launchTypenoOnly quick is accepted. Anything else is refused
devBuynoHow 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
quoteTokennoThe pool's other side. Defaults per chain — ETH on Robinhood Chain, USDC on Arc. Read the options from /api/v1/config
feeRecipientnoDefaults to creatorAddress
venuenouniswap-v4 where deployed (default), uniswap-v3, or sushi-v3

Omitting venue gets you Uniswap V4

This is the same venue the launch form opens on, and it is a cost decision rather than a preference: V4 opens your pool inside a PoolManager that is already deployed, where a V3 launch deploys a fresh pool contract and burns roughly 6.2M gas against V4's 1.35M for an identical token. It is derived per chain, so a chain with no V4 adapter still defaults to something it can launch on -- read launchRules.venues from /api/v1/config rather than assuming.

This default changed. It used to be uniswap-v3. If you relied on the old behaviour, name venue explicitly -- the venue is written into the token at creation and cannot be changed afterwards.

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": 5042,
  "chain": { "chainId": 5042, "slug": "arc", "name": "Arc" },
  "quote": { "symbol": "USDC", "address": "0x3600…0000", "decimals": 6, "isNative": false },
  "venue": "uniswap-v4",
  "transactions": {
    "approval": { "to": "0x3600…0000", "data": "0x095ea7b3…", "value": "0" },
    "launch":   { "to": "0x5c1F…cad0", "data": "0x1f6a5b96…", "value": "1000000000000000000" }
  },
  "cost": {
    "launchFeeWei": "1000000000000000000",
    "devBuyWei": "0",
    "devBuyQuoteAmount": "10000000",
    "totalValueRequired": "1000000000000000000",
    "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 address

Wait 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-v4", "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.

Send ammVenue, and send the venue that prepare answered with rather than the one you believe you asked for. A missing ammVenue is not read as unknown — the trade panel reads it as Uniswap V3, so a V4 launch without it has its first buys routed at a pool that does not exist until the indexer corrects the row. That matters more now that omitting venue gets you V4: a caller who named no venue in either call used to be consistent by accident, and no longer is.

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.

EndpointReturns
GET /api/tokensEvery live token with price, market cap, volume
GET /api/tokens/{address}One token, plus holders and recent trades
GET /api/tokens/{address}/candles?interval=1hOHLC price history
GET /api/activityRecent launches and trades across the platform
GET /api/statisticsPlatform totals
GET /api/wallet/{address}/portfolioA wallet's holdings, across all chains
GET /api/wallet/{address}/launchesEvery coin a wallet has launched, with its creator revenue
GET /api/wallet/{address}/profileA wallet's public profile — name and avatar
GET /api/tokens/{address}/earningsA token's creator earnings, day by day
GET /api/search/tokensType-ahead search over names, tickers and addresses
GET /api/tradersThe trader board
GET /api/eth-priceThe ETH price the site itself renders money with

/api/search/tokens is the one read that answers an unknown chain with an empty list rather than a 404, because it backs a type-ahead where an error shape would have to be drawn as a list.

/api/traders carries an isFollowing flag that is resolved from the caller, not from anything the request names, so it simply reads false for an anonymous caller rather than failing.

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.