Your first swap
A swap is two calls: GET /quote to see what a trade returns, POST /swap to get the transaction for your wallet. You sign and send it.
1. Quote
Section titled “1. Quote”curl "https://uprally.co/api/v1/quote?token=0x94641b97010608c3827fb058074889f19868ff33&side=buy&amount=0.001"side:buyspends ETH for the coin,sellspends the coin for ETH.amount: whole units as a string: ETH for a buy ("0.001"), coins for a sale. Never wei.wallet(optional): quote for that wallet exactly. A coin with a launch-time snipe tax charges by wallet.
The answer says what you get after every fee and tax (amountOut), the least the transaction will accept (minOut, 1% under by default; set slippageBps), the route it won (venue), how many routes it compared, the price impact, and for a buy, what selling it straight back would return (sellBack). Every figure comes from simulating the exact transaction against the chain as it is now.
2. Build
Section titled “2. Build”curl -X POST https://uprally.co/api/v1/swap \ -H 'content-type: application/json' \ -d '{"wallet":"0xYourWallet","token":"0x94641b97010608c3827fb058074889f19868ff33","side":"buy","amount":"0.001"}'The same fields, plus wallet. The answer is the quote, plus:
tx: the unsigned transaction (chainId4663,from,to,data,valuein wei,gas). Send it exactly as it is.requires: anything to send first, in order. Empty for a buy. A first sale of a coin through a Uniswap pool needs two approvals (the coin to Permit2, then Permit2 to the router); through a launchpad curve, one. See Approvals.expiresAt: a router swap reverts after this (10 minutes). Build again rather than sending an old one.
The build checks your real balances: INSUFFICIENT_FUNDS (ETH) or INSUFFICIENT_BALANCE (the coin) when you don’t have enough.
3. Sign and send
Section titled “3. Sign and send”import { JsonRpcProvider, Wallet } from 'ethers';
const chain = await (await fetch('https://uprally.co/api/v1/chain')).json();const wallet = new Wallet(process.env.PRIVATE_KEY, new JsonRpcProvider(chain.rpc, chain.chainId));
const res = await fetch('https://uprally.co/api/v1/swap', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ wallet: wallet.address, token: '0x94641b97010608c3827fb058074889f19868ff33', side: 'buy', amount: '0.001' }),});const built = await res.json();if (!res.ok) throw new Error(`${built.error.code}: ${built.error.message}`);
for (const step of built.requires) await (await wallet.sendTransaction(step.tx)).wait();const sent = await wallet.sendTransaction({ ...built.tx, value: BigInt(built.tx.value), gasLimit: BigInt(built.tx.gas) });console.log(`${chain.explorer}/tx/${sent.hash}`);The whole file, with a sale and the approvals handled: swap.mjs. GET /tx/{hash} says when it confirmed.
When it says no
Section titled “When it says no”Every error is { "error": { "code", "message", "fix" } }; act on code. The ones a swap meets most:
| Code | Means |
|---|---|
NO_ROUTE | No pool or curve can make the trade right now. A coin seconds old may have no pool yet. |
HONEYPOT | Selling it straight after buying fails through every route, in simulation. |
PRICE_IMPACT_TOO_HIGH | The trade moves the price more than 50%; trade less, or send maxPriceImpactPct. |
SIMULATION_FAILED | The price moved between the quote and the check; build again. |