Skip to content
cell

API

Launch from code · open, no key, no account

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.
twitter
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.
Request
{
  "name": "Doge",
  "symbol": "DOGE",
  "payer": "<launcher's address, base58>",
  "twitter": "https://x.com/yourcoin",
  "imageBase64": "<the picture, base64>",
  "imageContentType": "image/png"
}
201 reply
{
  "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.

Request
{ "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}

200 reply
{
  "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. error says why.

05Errors

Every refusal has the same shape. field is there when one input is the problem.

Error reply
{ "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-after says 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.

launch.ts
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