API

Launch a pump.fun token from a bot. No API key and no login. The deployer wallet signs and pays. The handle is who receives the fees — pin yours on every launch while other people pick the ticker. 80% goes to that Telegram, X, or GitHub handle, 20% buys and burns $PAYGRAM.

  1. 1. Prepare

    Send the ticker, handle, quote, and deployer wallet. The response is an unsigned transaction.

  2. 2. Sign

    The deployer signs that exact transaction. The mint key is already signed.

  3. 3. Submit

    Send the signed transaction back. The coin is live on pump once it confirms.

One handle, many launchers

handle is the fee destination. It does not have to be the person who picked the ticker, and it is not the wallet that signs. Set it to your account on every call. Each coin is separate. The 80% for that @ sits in one vault. You claim it on /claim by signing in as that Telegram, X, or GitHub account. This API does not send the payout.

Your bot signs. Keep one funded keypair. When someone sends a ticker, a name, and an image URL, prepare with your fixed handle, sign, and submit. They never need a wallet. The keypair must hold at least 0.025 SOL for the create, plus devBuy if you set one. The image has to be a public https URL — a file upload and a Telegram file id are refused.

const HANDLE = "yourname";
const NETWORK = "telegram";

const prepared = await fetch("https://usepaygram.com/api/v1/launches", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    wallet: keypair.publicKey.toBase58(),
    handle: HANDLE,
    network: NETWORK,
    tokenSymbol: "TICKER",
    tokenName: "Name they picked",
    image: "https://example.com/image.png",
    quote: "sol",
  }),
}).then((res) => res.json());

Then sign and submit that transaction the same way as below. Show https://pump.fun/coin/{mint} and https://usepaygram.com/token/{mint}.

They sign. Prepare with wallet set to their pubkey and handle still set to yours. Send them unsignedTransaction. They pay and sign. Fees still route to your handle.

Or send a link that opens the form with your @ filled in: https://usepaygram.com/launch?handle=yourname&network=telegram. network is telegram, x, or github. Optional ticker, name, buy, and description. The person can still change the @ before they sign. Their wallet pays the create fee — Paygram routing never does. When the destination has to stay yours, set handle in the API call and do not hand them the form.

Read the vault with no key. Telegram, X, and GitHub are three vaults, even when the @ is the same. routes lists every coin on that destination. pendingSol is the 80% not claimed yet, on SOL pairs. Claim at https://usepaygram.com/claim?handle=yourname, and add &network=x or &network=github for the other two.

GET /api/handles/yourname?network=telegram

{
  "handle": "yourname",
  "network": "telegram",
  "pendingSol": 1.2,
  "receivedSol": 4.0,
  "optedOut": false,
  "bound": false,
  "routes": [
    { "mint": "…", "tokenSymbol": "TICKER", "pendingSol": 0.4 }
  ]
}

30 prepares per minute per IP. Queue the invites. The handle must already exist and must not have opted out. A GitHub organization is refused. If submit comes back after the blockhash expires, prepare again.

Prepare

POST /api/v1/launches. The coin is not created yet. In live mode the status is needs_signature and unsignedTransaction is a versioned transaction the deployer must sign. Sign and submit before the blockhash expires (about a minute). If it expires, prepare again.

POST /api/v1/launches
Content-Type: application/json

{
  "wallet": "<deployer pubkey>",
  "handle": "durov",
  "network": "x",
  "tokenSymbol": "TICKER",
  "tokenName": "Ticker",
  "image": "https://example.com/image.png",
  "description": "optional note",
  "quote": "amznx"
}
{
  "id": "…",
  "mint": "…",
  "status": "needs_signature",
  "unsignedTransaction": "<base64 versioned tx>",
  "signature": null,
  "route": { "feeRecipient": "…", "handle": "x:durov", "quoteSymbol": "AMZNx" }
}

Do not send mint, feeRecipient, treasuryAddress, or feeShares. Paygram assigns those. The deployer is the payer, not the creator-fee wallet.

Fields

FieldRequiredNotes
walletyesDeployer pubkey. This wallet signs and pays.
handleyesPublic @ that receives the 80%. Must already exist. Pin yours on every call when other people launch through your bot. Not the deployer.
networknotelegram (default), x, or github. Each @ is its own destination. GitHub organizations are refused.
tokenSymbolyes2–10 characters, A–Z and 0–9. A leading $ is stripped.
tokenNamenoUp to 32 characters. Defaults to the handle name.
imagenoPublic https URL only, up to 2000 characters. A file upload is not accepted.
descriptionnoOptional note. Paygram appends the fee-routing line.
quotenosol, a stock symbol (amznx), or that stock's mint. Default sol.
devBuynoSOL to buy in the same transaction. SOL pairs only. Max 10,000.

Sign and submit

Deserialize unsignedTransaction, sign it with the deployer key, and POST the same bytes. Do not rebuild the transaction — the mint signature is already inside it, and a different message is rejected.

import { Keypair, VersionedTransaction } from "@solana/web3.js";

const prepared = await fetch("https://usepaygram.com/api/v1/launches", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    wallet: keypair.publicKey.toBase58(),
    handle: "durov",
    network: "x",
    tokenSymbol: "TICKER",
    image: "https://example.com/image.png",
    quote: "sol",
    devBuy: 0.1,
  }),
}).then((res) => res.json());

const tx = VersionedTransaction.deserialize(
  Buffer.from(prepared.unsignedTransaction, "base64"),
);
tx.sign([keypair]); // mint key is already signed — do not rebuild the tx

const submitted = await fetch("https://usepaygram.com/api/v1/launches/submit", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    id: prepared.id,
    signedTransaction: Buffer.from(tx.serialize()).toString("base64"),
  }),
}).then((res) => res.json());

POST /api/v1/launches/submit

POST /api/v1/launches/submit
Content-Type: application/json

{
  "id": "<id from prepare>",
  "signedTransaction": "<same tx, base64, deployer signature added>"
}

A successful submit returns status: "active" and the landing signature. Sending the same id again after it is active returns that launch. If confirmation is still pending, wait a few seconds and submit the same signed transaction again.

From mint, the coin is at https://pump.fun/coin/{mint} and https://usepaygram.com/token/{mint}.

After launch

GET /api/routes/{id} needs no key. buybacks are the 20% already spent on $PAYGRAM. payouts are what was sent to the handle. On a SOL pair, route.pendingSol is the 80% still waiting to be claimed. On a stock pair the handle's 80% stays in that stock; payout parts name the asset. The handle owner claims on /claim — this API does not send the payout.

GET /api/routes/{id}

{
  "route": {
    "mint": "…",
    "status": "active",
    "receivedSol": 0.12,
    "pendingSol": 0.096,
    "paidSol": 0,
    "quoteSymbol": null
  },
  "buybacks": [{ "sol": 0.024, "sig": "…", "ts": 1710000000000 }],
  "payouts": [{ "handle": "x:durov", "sol": 0, "parts": [], "sig": null, "ts": 1710000000000 }]
}

Quotes

quote accepts sol, a symbol below, or the mint address. On a stock pair the handle's 80% stays in that stock. A dev buy is refused on stock pairs — those launches open with no buy. SpaceX on pump is SPCX, not the Backed SPCXx mint.

  • sol
  • tslax
  • nvdax
  • aaplx
  • amznx
  • google
  • metax
  • msftx
  • avgox
  • spyx
  • qqqx
  • gldx
  • hoodx
  • coinx
  • crclx
  • mstrx
  • strcx
  • pltrx
  • gmex
  • brkx
  • kox
  • mcdx
  • intcx
  • spcx
  • vidax
  • dfdvx

Limits and errors

30 prepares and 60 submits per minute per IP. Over the limit the status is 429. Invalid JSON is 400. An unknown launch id is 404. Everything else rejected at prepare or submit is 422, with error set.

  • The handle must exist on that network, and must not have opted out.
  • The wallet must hold enough SOL for the create fee, and for the dev buy if you set one.
  • The signed transaction must be the prepared message, and the fee payer must be wallet.
  • A local or simulated server returns status: "simulated" and no transaction. Production returns needs_signature.