01Overview
Anything that can sign with a Solana key can launch a coin on cell. The API is open: there is no key to ask for, no account and no charge for calling it.
A launch is three calls. You ask for a transaction, sign it with the launcher’s key and send it back, then check until the coin is live. The launcher’s key pays, signs and is written into the coin as its creator, exactly as a wallet is on the site.
The only cost is the launch itself: 0.1 SOL to the pad plus the network's rent and fees, paid by the launcher's wallet when it signs. Every coin gets the same fee split as one launched from the form.
- Base URL
- celllaunch.app/api/v1
- Format
- JSON
- Auth
- None
02Ask for a launch
POST/api/v1/launch
Send the coin’s details. The pad checks them, reserves the coin’s address and builds the transaction that creates it.
- name
- Required. Up to 32 bytes.
- symbol
- Required. Letters and digits, up to 10, kept in the case you send; anything else is dropped.
- payer
- Required. The launcher’s address. It pays and becomes the creator.
- imageBase64
- Required. PNG, JPEG or WebP, up to 2 MB, as base64. Line breaks are fine.
- Optional. An x.com or twitter.com profile link.
- devBuyLamports
- Optional. The launcher’s first buy, in the same transaction: whole lamports as a string or number, 0.001 to 50 SOL. A wallet that cannot cover it gets a 402.
- imageContentType
- Optional. The picture is identified from its bytes either way.
{
"name": "Doge",
"symbol": "DOGE",
"payer": "<launcher's address, base58>",
"twitter": "https://x.com/yourcoin",
"imageBase64": "<the picture, base64>",
"imageContentType": "image/png"
}{
"id": "028eb504a35d393aebcffa34ee216bee",
"mint": "<the coin's address>",
"transaction": "<base64 v0 transaction, unsigned>",
"lastValidBlockHeight": 356123456,
"feeLamports": "102039280"
}The transaction expires at lastValidBlockHeight, about a minute. feeLamports is the launch’s whole fee, as a string; a dev buy is spent on top of it.
03Send it signed
POST/api/v1/launch/{id}/signed
Sign the transaction with the payer’s key and nothing else, then send the bytes back. The pad checks they are exactly what it issued, adds the coin’s own signature and sends it to the network.
{ "transaction": "<the same transaction, signed by payer, base64>" }Sign, do not send. A wallet that sends the transaction itself changes it first, and the pad will not add its signature to a transaction it did not build. With web3.js that means tx.sign([launcher]); with a browser wallet, signTransaction.
04Check the status
GET/api/v1/launch/{id}
{
"status": "live",
"mint": "<the coin's address>",
"signature": "<the launch transaction>"
}- issued
- Waiting for the signed transaction.
- minting
- Sent. Waiting for the network to confirm it.
- live
- The coin exists. Its page is celllaunch.app/coin/{mint}.
- expired
- Nothing was signed in time. Nothing was charged; ask for a new launch.
- failed
- The network refused it.
errorsays why.
05Errors
Every refusal has the same shape. field is there when one input is the problem.
{ "error": "that ticker is too long", "field": "symbol" }- 400
- A field is missing or not allowed. Fix it and send again.
- 413
- The picture is over 2 MB.
- 429
- Too many calls. Wait and send again.
- 502
- The pad could not be reached. Send again shortly.
- 503
- Launches are closed or paused.
retry-aftersays for how long when it is known.
06Limits
- Launch requests
- 3 a minute
- Status checks
- 1 a second
- Largest picture
- 2 MB
Limits are per caller and only there to stop floods. A bot launching coins one at a time will not reach them.
07Example
The three calls in TypeScript with @solana/web3.js.
import { readFile } from 'node:fs/promises';
import { Keypair, VersionedTransaction } from '@solana/web3.js';
const API = 'https://celllaunch.app/api/v1/launch';
const launcher = Keypair.fromSecretKey(/* your key */);
const picture = await readFile('coin.png');
// 1. Ask for the transaction.
const created = await fetch(API, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
name: 'Doge',
symbol: 'DOGE',
payer: launcher.publicKey.toBase58(),
imageBase64: picture.toString('base64'),
}),
}).then(r => r.json());
// 2. Sign with the launcher only, and send it back.
const tx = VersionedTransaction.deserialize(Buffer.from(created.transaction, 'base64'));
tx.sign([launcher]);
await fetch(`${API}/${created.id}/signed`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
});
// 3. Poll until it is live.
let status;
do {
await new Promise(r => setTimeout(r, 2000));
status = await fetch(`${API}/${created.id}`).then(r => r.json());
} while (status.status === 'issued' || status.status === 'minting');A complete script, with error handling and a time limit, takes a keypair file and a picture and launches from the command line.
Download the full example