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
Call the factory and the Locker from your own wallet. Trustless; the only path for writes.
Launch from code ↓Feed, token, trades, candles, holders; quote checks and IPFS uploads for your launches.
API reference ↓https://1000x.family/token/{address} — chart, trades and swap for any 1000X token, seconds after launch.
Launch from code
- Read
launchFee()andtokenInitCodeHash()from the factory. - Mine a salt for the wallet that will send the transaction (about 65,000 hashes, under a second).
- Call
launch(params)withmsg.value = launchFee + firstBuyfor a BNB pair. - Read
TokenLaunchedfrom 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)
| Field | Type | Rule |
|---|---|---|
| salt | bytes32 | Mined so that predictToken(msg.sender, salt) ends in 1000. Bound to the sender. |
| name | string | ≤ 30 characters, no line breaks or invisible characters. Immutable. |
| symbol | string | ≤ 12 characters, any printable text: letters, digits, spaces, symbols, emoji; no line breaks or invisible characters. Immutable. |
| metadataURI | string | "" or ipfs://… of a metadata JSON (below). Immutable. |
| quote | address | WBNB for a BNB pair, or a BEP-20 that passes the quote checks. |
| mode | uint8 | 0 Dev · 1 Burn · 2 Holders. Immutable. |
| feeRecipient | address | Who Dev mode pays. 0x0 = the sender. |
| firstBuy | uint256 | Optional buy in the quote, inside the launch tx. ≥ minFirstBuy (live below). |
| minMemeOut | uint256 | Floor for the first buy's output. It runs right after the pool is created in the same tx, so 0 is common. |
| quoteRoute | address[] | [] for WBNB and for a direct quote/WBNB pool, else the 1–2 tokens of route.via from the preflight. |
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.
POST /api/preflight/quotewith{quote, creator}→ok,reasons,route.via,min_first_buy,decimals.- 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. - Launch with
quoteRoute = route.viaandmsg.value = launchFee.
// 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.
# 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.
| Error | Meaning | Fix |
|---|---|---|
| BadSuffix(address) | predictToken(msg.sender, salt) does not end in 1000 | Mine 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 denylist | Pick another quote. |
| QuoteIsMeme() | quote equals the token being created | Use another salt. |
| RouteForWbnb() | quoteRoute must be empty for WBNB | Pass []. |
| QuoteNotERC20(quote) | not a contract, or the ERC-20 surface fails | Pick a standard BEP-20. |
| QuoteHasTransferFee(quote) | the probe transfer was taxed or skimmed | Taxed tokens cannot be quotes. |
| QuoteRebases(quote) | totalSupply changed during the probe | Rebasing tokens cannot be quotes. |
| BadRoute / NoTwap / HopTooThin / NoWbnbPath | no usable ≤ 3-pool route to WBNB with a 30-min TWAP and depth | Use the route from POST /api/preflight/quote. |
| PoolPriceMismatch / RepriceNotFree | someone pre-created the pool at the predicted address and parked liquidity | Mine a new salt. |
| ERC20InsufficientAllowance | allowance < quoteProbeAmount(quote) + firstBuy | Approve 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 fromQuoteRoutedis 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)is0x0before that.Distributor.claimForMany(claims)pays holders their epoch rewards with proofs;claimis the self-serve version.
# 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-keystoreREST 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.
| 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. |
| 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). |
| 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. |
| 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 request | 400 per 10 s per IP at the edge (then a 10 s block) |
|---|---|
| /api/* | ≈ 1,800 per minute per IP |
| POST /preflight/quote | 60 per minute per IP |
| POST /uploads/* | 20 per 10 minutes per IP |
| Searches (?q=) | busy → 503, retry after a few seconds |
| GET caching | responses 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.
| Parameter | Now | Meaning |
|---|---|---|
| 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 fee | 1% | 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.