Launch a token
Developers

Build on 1000X

Launching is permissionless. Any wallet, bot or trading terminal can call the factory directly: one transaction deploys the token, opens its PancakeSwap V3 pool, locks the liquidity forever and makes an optional first buy. No API key, no allowlist.

Integration paths

1 · ContractsLaunch, collect, kick

Call the factory and the Locker from your own wallet. Trustless; the only path for writes.

Launch from code ↓
2 · REST APIRead and prepare

Feed, token, trades, candles, holders; quote checks and IPFS uploads for your launches.

API reference ↓
3 · LinksSend users to a token

https://1000x.family/token/{address} — chart, trades and swap for any 1000X token, seconds after launch.

Manifest & ABIs ↓

Launch from code

  1. Read launchFee() and tokenInitCodeHash() from the factory.
  2. Mine a salt for the wallet that will send the transaction (about 65,000 hashes, under a second).
  3. Call launch(params) with msg.value = launchFee + firstBuy for a BNB pair.
  4. Read TokenLaunched from the receipt: token, pool, mode.
// npm i viem   ·   RPC_URL=… PRIVATE_KEY=0x… npx tsx launch.ts
import {
  concat, createPublicClient, createWalletClient, encodeAbiParameters, http, keccak256,
  parseAbi, parseEther, parseEventLogs, slice, toHex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { bsc } from "viem/chains";

const FACTORY = "0x027ad1Bc0C4fdA010566d4535A00eAFb19798f22";
const WBNB = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";

const factoryAbi = parseAbi([
  "function launchFee() view returns (uint256)",
  "function tokenInitCodeHash() view returns (bytes32)",
  "function predictToken(address creator, bytes32 salt) view returns (address)",
  "struct LaunchParams { bytes32 salt; string name; string symbol; string metadataURI; address quote; uint8 mode; address feeRecipient; uint256 firstBuy; uint256 minMemeOut; address[] quoteRoute; }",
  "function launch(LaunchParams p) payable returns (address token)",
  "event TokenLaunched(address indexed token, address indexed creator, address indexed quote, address pool, uint8 mode, address feeRecipient, uint256 positionId, bytes32 salt, int24 initialTick)",
]);

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const transport = http(process.env.RPC_URL);
const pub = createPublicClient({ chain: bsc, transport });
const wallet = createWalletClient({ chain: bsc, transport, account });

// 1. Mine a salt: the token address must end in 1000. The salt is bound to the creator (msg.sender).
function mineSalt(creator: `0x${string}`, initCodeHash: `0x${string}`) {
  let n = BigInt(keccak256(toHex(`${creator}${Date.now()}`))) >> 32n; // random start
  for (;;) {
    const salt = toHex(n++, { size: 32 });
    const inner = keccak256(encodeAbiParameters([{ type: "address" }, { type: "bytes32" }], [creator, salt]));
    const token = slice(keccak256(concat(["0xff", FACTORY, inner, initCodeHash])), 12);
    if (token.endsWith("1000")) return { salt, token };
  }
}

const [launchFee, initCodeHash] = await Promise.all([
  pub.readContract({ address: FACTORY, abi: factoryAbi, functionName: "launchFee" }),
  pub.readContract({ address: FACTORY, abi: factoryAbi, functionName: "tokenInitCodeHash" }),
]);
const { salt, token } = mineSalt(account.address, initCodeHash);
const predicted = await pub.readContract({
  address: FACTORY, abi: factoryAbi, functionName: "predictToken", args: [account.address, salt],
});
if (predicted.toLowerCase() !== token) throw new Error("salt check failed");

// 2. Launch: BNB pair, Dev mode, 0.01 BNB first buy in the same transaction.
const firstBuy = parseEther("0.01");
const { request } = await pub.simulateContract({
  account, address: FACTORY, abi: factoryAbi, functionName: "launch",
  value: launchFee + firstBuy, // BNB pair: launch fee + first buy
  args: [{
    salt, name: "My Coin", symbol: "MYCOIN", metadataURI: "",
    quote: WBNB, mode: 0, feeRecipient: account.address,
    firstBuy, minMemeOut: 0n, quoteRoute: [],
  }],
});
const hash = await wallet.writeContract({ ...request, gas: 8_000_000n }); // estimates can fall short
const receipt = await pub.waitForTransactionReceipt({ hash });

// 3. Read the new token and its pool from the event.
const [ev] = parseEventLogs({ abi: factoryAbi, eventName: "TokenLaunched", logs: receipt.logs });
console.log("token", ev.args.token, "pool", ev.args.pool, "tx", hash);

Gas used is ≈ 6.0–7.0M. Send with a gas limit of about 8,000,000: the node's estimate can fall short because the launch forwards gas to the pool, the position manager and the treasury. Unused gas is not charged. All three samples were run against the live factory on a mainnet fork.

The 1000 salt

Every token is an EIP-1167 clone deployed with CREATE2, and the factory reverts with BadSuffix unless the address ends in 1000. The salt is wrapped with the sender, so a salt seen in the mempool is useless to anyone else:

onchainSalt = keccak256(abi.encode(creator, salt))       // creator = msg.sender of launch
token       = keccak256(0xff ++ factory ++ onchainSalt ++ tokenInitCodeHash)[12:]
valid       ⇔ token & 0xFFFF == 0x1000

Start from a random salt each time: the same creator and salt can only deploy once. Always confirm with predictToken(creator, salt) before sending.

The salt only works when this exact wallet sends launch. Mined in your browser; nothing is sent anywhere.

Launch parameters

launch(LaunchParams p) payable returns (address token)

FieldTypeRule
saltbytes32Mined so that predictToken(msg.sender, salt) ends in 1000. Bound to the sender.
namestring≤ 30 characters, no line breaks or invisible characters. Immutable.
symbolstring≤ 12 characters, any printable text: letters, digits, spaces, symbols, emoji; no line breaks or invisible characters. Immutable.
metadataURIstring"" or ipfs://… of a metadata JSON (below). Immutable.
quoteaddressWBNB for a BNB pair, or a BEP-20 that passes the quote checks.
modeuint80 Dev · 1 Burn · 2 Holders. Immutable.
feeRecipientaddressWho Dev mode pays. 0x0 = the sender.
firstBuyuint256Optional buy in the quote, inside the launch tx. ≥ minFirstBuy (live below).
minMemeOutuint256Floor for the first buy's output. It runs right after the pool is created in the same tx, so 0 is common.
quoteRouteaddress[][] for WBNB and for a direct quote/WBNB pool, else the 1–2 tokens of route.via from the preflight.
msg.value = launchFee + firstBuy when the quote is WBNB (pay native BNB, the factory wraps it), otherwise exactly launchFee. Anything else reverts with WrongMsgValue.

Name and ticker rules are enforced by the app and the metadata API, not by the contract. Keep to them so your token shows correctly everywhere.

Non-BNB quotes

A token can be paired with another BEP-20 (bStocks, stablecoins, alpha memes) if it is a plain ERC-20 with no transfer tax or rebase and has a route of at most 3 PancakeSwap V3 pools to WBNB with a 30-minute price history and minimum depth. The factory checks all of it on-chain; the preflight endpoint tells you the result and the route before you spend gas.

  1. POST /api/preflight/quote with {quote, creator} → ok, reasons, route.via, min_first_buy, decimals.
  2. Approve the factory for quoteProbeAmount(quote) + firstBuy. The probe is a tiny round trip (out of your wallet and straight back) that proves the token is not taxed.
  3. Launch with quoteRoute = route.via and msg.value = launchFee.
TypeScript · non-BNB quote
// Pair with a BEP-20 instead of BNB (here USDT). Reuses pub / wallet / factoryAbi / mineSalt from the BNB sample.
const QUOTE = "0x55d398326f99059fF775485246999027B3197955";

// 1. Ask the API for a route the factory will accept (read-only; the factory re-checks it on-chain).
const report = await fetch("https://1000x.family/api/preflight/quote", {
  method: "POST", headers: { "content-type": "application/json" },
  body: JSON.stringify({ quote: QUOTE, creator: account.address }),
}).then((r) => r.json());
if (!report.ok) throw new Error(report.reasons.join("; "));
const quoteRoute = report.route.via; // [] for a direct quote/WBNB pool, else 1–2 tokens

// 2. Approve the probe (a tiny round-trip transfer that proves "no tax") + the first buy.
const erc20 = parseAbi(["function approve(address,uint256) returns (bool)"]);
const probe = await pub.readContract({
  address: FACTORY, abi: parseAbi(["function quoteProbeAmount(address) view returns (uint256)"]),
  functionName: "quoteProbeAmount", args: [QUOTE],
});
const firstBuy = 5n * 10n ** BigInt(report.decimals); // 5 USDT, ≥ report.min_first_buy
await pub.waitForTransactionReceipt({
  hash: await wallet.writeContract({ address: QUOTE, abi: erc20, functionName: "approve", args: [FACTORY, probe + firstBuy] }),
});

// 3. Launch: msg.value is only the launch fee; the first buy is pulled in the quote.
const hash = await wallet.writeContract({
  address: FACTORY, abi: factoryAbi, functionName: "launch", value: launchFee, gas: 8_000_000n,
  args: [{ salt, name: "My Coin", symbol: "MYCOIN", metadataURI: "", quote: QUOTE, mode: 1 /* Burn */,
           feeRecipient: account.address, firstBuy, minMemeOut: 0n, quoteRoute }],
});

Metadata & artwork

metadataURI is optional and permanent. Use our upload endpoints (they pin to IPFS and return the URI) or pin your own JSON with the same shape: name, symbol, description, optional image (ipfs://…) and socials (website, twitter, telegram). Name and ticker always come from the chain, never from the JSON. Without artwork the 1000X logo is shown.

curl
# Optional artwork → CID
curl -F [email protected] https://1000x.family/api/uploads/image
# → {"cid":"Qm…","uri":"ipfs://Qm…","gatewayUrl":"…","size":48211}

# Metadata JSON → the URI you pass as metadataURI
curl -X POST https://1000x.family/api/uploads/metadata -H 'content-type: application/json' \
  -d '{"name":"My Coin","symbol":"MYCOIN","imageCid":"Qm…","description":"gm","twitter":"https://x.com/mycoin"}'
# → {"cid":"Qm…","uri":"ipfs://Qm…","metadata":{"name":"My Coin","symbol":"MYCOIN","description":"gm","image":"ipfs://Qm…","socials":{"twitter":"https://x.com/mycoin"}}}

Errors

Custom errors, decoded by any ABI-aware client (full ABIs below). Simulate before sending and you never pay gas for these.

ErrorMeaningFix
BadSuffix(address)predictToken(msg.sender, salt) does not end in 1000Mine the salt for the wallet that sends the tx.
WrongMsgValue(expected, actual)msg.value is not launchFee (+ firstBuy for a BNB pair)Read launchFee() right before sending.
FirstBuyTooSmall(min, actual)first buy under minFirstBuy (converted to the quote)Raise firstBuy.
QuoteDenied(quote)quote is on the platform denylistPick another quote.
QuoteIsMeme()quote equals the token being createdUse another salt.
RouteForWbnb()quoteRoute must be empty for WBNBPass [].
QuoteNotERC20(quote)not a contract, or the ERC-20 surface failsPick a standard BEP-20.
QuoteHasTransferFee(quote)the probe transfer was taxed or skimmedTaxed tokens cannot be quotes.
QuoteRebases(quote)totalSupply changed during the probeRebasing tokens cannot be quotes.
BadRoute / NoTwap / HopTooThin / NoWbnbPathno usable ≤ 3-pool route to WBNB with a 30-min TWAP and depthUse the route from POST /api/preflight/quote.
PoolPriceMismatch / RepriceNotFreesomeone pre-created the pool at the predicted address and parked liquidityMine a new salt.
ERC20InsufficientAllowanceallowance < quoteProbeAmount(quote) + firstBuyApprove the factory for both.

Index launches

Every launch emits TokenLaunched on the factory. That event, or isThousandXToken(token) == true, is the only proof a token is a 1000X token: anyone can mine a 1000 address on another factory.

TokenLaunched(address indexed token, address indexed creator, address indexed quote, address pool, uint8 mode, address feeRecipient, uint256 positionId, bytes32 salt, int24 initialTick)
topic0 0xde30572a5d0e09be84a0a9d5225a741b07fe0ae1f30fc7d5b35656e4acb4b3cd
One per launch. The only proof a token is a 1000X token.
FirstBuy(address indexed token, address indexed buyer, uint256 quoteIn, uint256 memeOut)
topic0 0x0beb6c120fb450ea4109a19546dd3fae89fdf4d6f7d76c1925f32d6c471f3a30
Creator's optional buy inside the launch transaction.
QuoteRouted(address indexed token, address[] via, int24 wbnbPerQuoteTick)
topic0 0xc03ee0549a49e6490a9175811045aae94dc0f2d7183be5ca62317fcd24530531
Non-BNB quotes only: the route used to price the launch.
import { createPublicClient, http, parseAbiItem } from "viem";
import { bsc } from "viem/chains";

const pub = createPublicClient({ chain: bsc, transport: http(process.env.RPC_URL) });
const launched = parseAbiItem(
  "event TokenLaunched(address indexed token, address indexed creator, address indexed quote, address pool, uint8 mode, address feeRecipient, uint256 positionId, bytes32 salt, int24 initialTick)",
);

pub.watchEvent({
  address: "0x027ad1Bc0C4fdA010566d4535A00eAFb19798f22",
  event: launched,
  onLogs: (logs) => {
    for (const { args, transactionHash } of logs) {
      // args.pool is a PancakeSwap V3 pool, fee 10000 (1%), token/quote. Trade it with any V3 router.
      console.log(args.token, args.quote, args.pool, ["dev", "burn", "holders"][args.mode!], transactionHash);
    }
  },
});

getLaunch(token) returns the full record (creator, feeRecipient, quote, pool, positionId, mode, createdAt, initialTick, salt). Prefer the API's /tokens?sort=new if you do not run a node.

Trading

  • Each token has exactly one launch pool: PancakeSwap V3, fee tier 10000 (1%), token/quote. Trade it with the SmartRouter, any aggregator, or the pool directly. 1000X adds no router of its own.
  • The token is a plain BEP-20: no tax, no max wallet, no blacklist, no pause, no trading switch. It is tradable from the launch block.
  • The quote may not be WBNB. Read it from TokenLaunched.quote; a non-BNB pair needs a multi-hop path (the launch route from QuoteRouted is a good one).
  • The liquidity is single-sided from the start price upward: early buys move the price fast. Quote with QuoterV2 and set slippage.

Fees & keepers

Fee actions are permissionless and cannot redirect money: recipients come from the launch record. A bot can run them for any token, for example right before showing a creator their earnings.

  • Locker.collect(token) pulls the LP fees, sells the token side (impact-capped), pays 20% to the platform treasury and 80% to the creator mode. A refused transfer is credited, never reverts.
  • Distributor.kick() (Burn and Holders tokens): Burn buys back and burns; Holders closes an epoch for the merkle payout. Cooldown and minimum apply (KickCooldown, BelowMinKick). The Distributor is created by the token's first collect: distributorOf(token) is 0x0 before that.
  • Distributor.claimForMany(claims) pays holders their epoch rewards with proofs; claim is the self-serve version.
Shell · cast
# Collect a token's LP fees and pay everyone (permissionless; recipients are fixed on-chain).
cast send 0x79a7CAc0e1ed7CcF18b39B4C0954BCb1c2c63d99 "collect(address)" 0xToken --rpc-url $RPC_URL --account my-keystore

# Burn / Holders tokens: the Distributor exists after the token's first collect (0x0 before).
D=$(cast call 0xcf7E9883A57eBd26af57ddA051B4165D777facf0 "distributorOf(address)(address)" 0xToken --rpc-url $RPC_URL)
# Buyback-and-burn (Burn) or start an epoch (Holders). Reverts KickCooldown / BelowMinKick when early.
[ "$D" != 0x0000000000000000000000000000000000000000 ] && \
  cast send $D "kick()" --rpc-url $RPC_URL --account my-keystore

REST API

Base URL https://1000x.family/api. Read-only JSON, no key. Addresses are checksummed, token amounts are strings in raw units, prices are numbers in the quote (priceUsd already converted, null when there is no BNB route). It has no CORS headers: call it from your server or bot, not from another site's browser code.

Tokens
GET
/tokens?sort=new|last_trade|volume|mcap&q=&limit=1..200&offset=&pin=false
Feed page; total in the X-Total-Count header. pin=false for a plain ranking.
GET
/tokens/{token}
One token: creator, feeRecipient, mode, quote, pool, positionId, launchTx, burned, prices.
GET
/tokens/{token}/trades?limit=1..500&before_id=&offset=
Indexed swaps, newest first (trader = tx signer).
GET
/tokens/{token}/ohlcv?interval=1s|1m|5m|15m|1h|4h|1d&since=
Candles in the quote; usdPerQuote converts.
GET
/tokens/{token}/holders?limit=1..500
Top holders.
GET
/tokens/{token}/epochs
Holders mode: epochs, roots, payouts.
GET
/tokens/{token}/burns
Transfers to 0x…dEaD with a running total.
Accounts
GET
/accounts/{wallet}/tokens
Tokens a wallet launched (creator = sender of launch), newest first.
GET
/accounts/{wallet}/claims
What a wallet can claim (holder rewards, credited fees).
Launch helpers
POST
/preflight/quote
Body {quote, creator, via?}. Read-only check of a non-BNB quote: reasons, route.via, start_tick, min_first_buy, decimals.
POST
/uploads/image
multipart file → {cid, uri}. Re-encoded, EXIF stripped, ≤ 1024 px.
POST
/uploads/metadata
Body {name, symbol, imageCid?, description?, website?, twitter?, telegram?} → {uri} for metadataURI.
GET
/launches/by-tx/{txHash}
Token of a launch tx, even before the indexer has it.
GET
/quotes/launched
Non-BNB quotes used by real launches, most used first.
Platform
GET
/stats?window=24h|all
Launches, trades, volume, creators.
GET
/health
Indexer head, lag in blocks, BNB/USD.
curl 'https://1000x.family/api/tokens?sort=new&limit=20'
curl 'https://1000x.family/api/tokens/0xToken'
curl 'https://1000x.family/api/tokens/0xToken/trades?limit=100'
curl 'https://1000x.family/api/tokens/0xToken/ohlcv?interval=1m&since=1790000000'

Limits

Every request400 per 10 s per IP at the edge (then a 10 s block)
/api/*≈ 1,800 per minute per IP
POST /preflight/quote60 per minute per IP
POST /uploads/*20 per 10 minutes per IP
Searches (?q=)busy → 503, retry after a few seconds
GET cachingresponses are up to ~2 s old (shared edge cache)

Over a limit you get 429 with retry-after. Poll the feed every few seconds at most, and watch TokenLaunched on your own node for anything faster.

Machine-readable

  • /developers/manifest.json — chain, contracts, deploy block, event topics, launch rules, limits. One fetch to configure a bot.
  • /developers/abi/{factory | locker | distributor | distributor-factory | quote-pricer | token}.json — full ABIs, e.g. factory.json.
  • /llms.txt — this page as plain text for AI agents.
  • All contracts are source-verified on BscScan; tokens link to the verified implementation.

Contracts & live parameters

Read from BNB Smart Chain right now. Every address links to BscScan.

Factory↗
Locker↗
Distributor factory↗
Distributor implementation…
Quote pricer…
Token implementation…
ParameterNowMeaning
Launch fee…flat, per launch, paid in BNB
Minimum first buy…optional first buy floor (BNB value)
Open FDV…fully diluted value at the start price
Pool fee1%PancakeSwap V3 fee tier of every launch pool
Max route length…non-BNB quote → … → WBNB
Minimum depth per hop…every pool on a quote's route
Price average…TWAP used to price non-BNB quotes
Price-impact cap…max price move per Collect sale or buyback
Kick cooldown…between two buybacks (Burn) or two epochs (Holders) of a token

Rules for bots

  • Label 1000X tokens by the factory event, never by the suffix alone.
  • Show the creator mode (Dev, Burn, Holders) and the quote asset; both are permanent.
  • Link to https://1000x.family/token/{address} for the token page.
  • Respect the limits above; heavy readers should index the chain themselves. Repeated abuse is blocked at the edge.
  • The contracts are live on mainnet and not audited yet. Tokens can go to zero; nothing here is investment advice.

How the protocol works for users: Docs. Ready by hand? Launch a coin.