# Manage Accounts (/docs/accounts) Plain HTTP on the core host. Changes reach your open [feed socket](/docs/feed) within milliseconds, with no reconnect needed. ```http https://core.j7tracker.io Authorization: Bearer Content-Type: application/json ``` `x-session-id: ` works instead of `Authorization`. Call it from a server or script: browser requests from origins other than j7tracker get **403**. Handles are case-insensitive, and `@`, quotes and whitespace are stripped. | Job | Method | Path | Body | | ------------------------------------------------- | ------ | ------------------------- | ------------- | | [Check everything](#check-everything) | GET | `/api/watched-accounts` | none | | [Add a custom account](#add-a-custom-account) | POST | `/api/accounts` | `{ handle }` | | [Add available accounts](#add-available-accounts) | POST | `/api/accounts/available` | `{ handles }` | | [Remove an account](#remove-an-account) | POST | `/api/remove-account` | `{ handle }` | Account kinds [#account-kinds] | Kind | What it is | Costs | | ------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------ | | **Main feed** | Accounts J7 tracks for everyone | Nothing. You already get these | | **Custom** | An X account you pay a slot for. J7 starts tracking it if nobody else was | 1 slot | | **Available** | An account another user already pays for. It stays in the pool only while at least one user pays for it | Free | Free slots come from your deploys, plus any purchased slots: **15+** deploys = 1 slot, **50+** = 2, **100+** = 3. Check everything [#check-everything] GET /api/watched-accounts [#get-apiwatched-accounts] Every platform's watched list, your custom and available lists, the pool you can still add, and your hidden list. Add `?fresh=1` to skip the 10 s server cache. ```ts interface WatchedAccountsResponse { success: true; x: { accounts: WatchedX[]; plan: object | null }; // Main-feed X accounts, newest first truth: PlatformList; ig: PlatformList; bsq: PlatformList; tiktok: PlatformList; youtube: PlatformList; // Identify channels by channelId custom: { accounts: string[]; // Your custom (slot) accounts, oldest first availableAccounts: string[]; // Your free copies from the pool, oldest first maxAccounts: number; // Your slot count deployCount: number; }; available: { accounts: string[]; // Pool handles you can still add for free total: number; // Pool size, including what you already have }; hidden: string[]; // Main-feed X handles you removed (hidden) feedPrefs: { noAutoAddMainFeed: boolean; // true = accounts newly added to the main feed start hidden for you }; publishedAt: Record; // When each list was last refreshed. null = not loaded yet } interface PlatformList { accounts: T[]; limits: { current: number; max: number | '?' }; // '?' when unknown } interface WatchedX { id: string; handle: string; name: string; avatar: string; } interface WatchedSocial { handle: string; name: string; avatar?: string | null; } ``` Slots left = `custom.maxAccounts - custom.accounts.length`. Errors on this route use `success: false` instead of `ok: false`. Add a custom account [#add-a-custom-account] POST /api/accounts [#post-apiaccounts] Uses one slot and starts tracking the handle if nobody else was. | Body | Type | Description | | ---------- | ------ | ----------- | | `handle`\* | string | X handle | ```ts interface AddAccountResponse { ok: true; action: 'add'; account: string; addedAsAvailable: boolean; // true = added from the pool instead, no slot used deployCount: number; maxAccounts: number; message: string; // e.g. "Added @frankdegods to your custom accounts" } // Main-feed handle: no slot used interface MainFeedResponse { ok: true; action: 'unhide'; account: string; unhidden: boolean; message: string; } ``` * Out of slots but someone else already tracks the handle? It's added as **available** instead (`addedAsAvailable: true`). * Can take a few seconds when J7 has to start tracking a brand-new handle. **Errors:** `invalid_handle` 400 · `already_added` 409 · `no_deploys` 403 · `limit_reached` 403 · `tracking_failed_invalid` 422 · `tracking_failed_quota` 429 · `tracking_failed_other` 502 · `internal_error` 500. Add available accounts [#add-available-accounts] POST /api/accounts/available [#post-apiaccountsavailable] Adds one or many handles from the pool (`available.accounts` above). Free, never uses a slot. | Body | Type | Description | | ----------- | --------- | --------------------------------- | | `handles`\* | string\[] | One or more handles, up to 25,000 | ```ts // One handle interface AddAvailableResponse { ok: true; action: 'add' | 'unhide'; account: string; message: string; // e.g. "Added @ansem from available accounts" } // Two or more handles interface AddAvailableBatchResponse { ok: true; action: 'add_batch'; added: number; addedHandles: string[]; unhidden: number; message: string; // e.g. "Added 2 available account(s)" } ``` * **One** handle: fails with `already_added` 409 if you have it, `not_available` 404 if nobody tracks it. * **Several**: handles not in the pool are skipped silently. Compare `addedHandles` with what you sent. Remove an account [#remove-an-account] POST /api/remove-account [#post-apiremove-account] One call for every removal. The server works out what the handle is to you. | Body | Type | Description | | ---------- | ------ | ------------ | | `handle`\* | string | Any X handle | | The handle is… | Result | `action` | | ------------------------------ | --------------------- | ------------------- | | Your custom account | Removed, slot freed | `removed_custom` | | One of your available accounts | Removed | `removed_available` | | Anything else | Hidden from your feed | `hidden` | ```ts interface RemoveAccountResponse { ok: true; action: 'removed_custom' | 'removed_available' | 'hidden'; account: string; } ``` To bring an account back, add it again. If you were the last user paying for a custom account, J7 stops tracking it and it leaves the pool. **Errors:** `invalid_handle` 400 · `untrack_failed` 502 (nothing changed, retry) · `internal_error` 500. All error codes and rate limits are on [Errors & Limits](/docs/errors#accounts-api). Example [#example] ```bash BASE=https://core.j7tracker.io AUTH="Authorization: Bearer " JSON="Content-Type: application/json" curl -s $BASE/api/watched-accounts -H "$AUTH" curl -s -X POST $BASE/api/accounts -H "$AUTH" -H "$JSON" -d '{"handle":"frankdegods"}' curl -s -X POST $BASE/api/accounts/available -H "$AUTH" -H "$JSON" -d '{"handles":["ansem","cobie"]}' curl -s -X POST $BASE/api/remove-account -H "$AUTH" -H "$JSON" -d '{"handle":"frankdegods"}' ``` ```js const BASE = "https://core.j7tracker.io"; const headers = { Authorization: "Bearer ", "Content-Type": "application/json" }; const call = async (method, path, body) => { const res = await fetch(BASE + path, { method, headers, body: body && JSON.stringify(body) }); return res.json(); }; const all = await call("GET", "/api/watched-accounts"); console.log(`${all.custom.accounts.length}/${all.custom.maxAccounts} slots used`); const r = await call("POST", "/api/accounts", { handle: "frankdegods" }); if (!r.ok) console.error(r.code, r.error); await call("POST", "/api/accounts/available", { handles: ["ansem", "cobie"] }); await call("POST", "/api/remove-account", { handle: "frankdegods" }); ``` ```python import requests BASE = "https://core.j7tracker.io" s = requests.Session() s.headers["Authorization"] = "Bearer " all_ = s.get(f"{BASE}/api/watched-accounts").json() print(f"{len(all_['custom']['accounts'])}/{all_['custom']['maxAccounts']} slots used") r = s.post(f"{BASE}/api/accounts", json={"handle": "frankdegods"}).json() if not r["ok"]: print(r["code"], r["error"]) s.post(f"{BASE}/api/accounts/available", json={"handles": ["ansem", "cobie"]}) s.post(f"{BASE}/api/remove-account", json={"handle": "frankdegods"}) ``` # API keys (/docs/api-keys) {/* Screenshots: `public/images/how-to-api-key/` — step-1-login.png, step-2-key-control.png, step-3-copy-key.png */} Every wallet has its own encrypted key. It is the signing key for **that wallet only** — not an account-wide token. 1\. Open Deploy Settings [#1-open-deploy-settings] Deploy Settings 2\. Go to Wallets [#2-go-to-wallets] Wallets page 3\. Click the key icon on the wallet [#3-click-the-key-icon-on-the-wallet] Key icon Which key goes where [#which-key-goes-where] | Chain | Use in `api_key` for | | ------------- | ---------------------------------------------------------------------------------------------------------------------- | | Solana wallet | `pump`, `bonk`, `stonk`, `bags`, `ansem` — and every entry in `bundle_wallets` / `sniper_wallets` | | EVM wallet | `bnb`, `genius`, `flap`, `pons`, `long`, `poolstrade`, `robinhood`, `o1`, `klik`, `stroid`, `clanker`, `argus`, `foci` | Never send a raw private key — only the encrypted string the app shows. # Bundle API (/docs/bundle) **`POST {base}/submit`** with **`"type": "bundle"`**. Up to **4** transactions in `txs`, executed in array order, all in the same slot or none. Each leg carries its own `api_key`; there is no top-level one. Required fields [#required-fields] | Field | Type | Notes | | -------------- | ------ | ------------------------------- | | `type` | string | `bundle` | | `session_id` | string | JWT, or `Authorization: Bearer` | | `mint_address` | string | Same coin for every leg | | `txs` | array | 1–4 legs (below) | Legs [#legs] | Leg | Fields | | ---- | --------------------------------------------------------------------------------------------------------------- | | Buy | `{ "type": "buy", "sol_amount": 0.5, "api_key": "" }` — `lamports` takes priority over `sol_amount` | | Sell | `{ "type": "sell", "percent": 100, "api_key": "" }` — `1–100` % of that wallet's tokens, or `basis_points` | Optional fields (shared by every leg) [#optional-fields-shared-by-every-leg] | Field | Type | Notes | | -------------------------------------------- | ------- | ------------------------------------------------------------ | | `mode` | string | `pump` (default), `bonk`, `ray`, `usd1`. Sells are pump-only | | `creator_wallet` | string | Coin creator pubkey — pass it when you have it | | `quote_token` | string | `"usdc"` for USDC-quoted pump coins | | `mayhem_mode`, `bonkers_mode`, `locked_mode` | boolean | Platform selectors | | `bribe_fee_sol` | number | Relay tip per tx | | `slippage_bps` | integer | USDC swap slippage, default `200` | | `request_id` | string | Echoed back | ```json { "type": "bundle", "session_id": "", "mint_address": "MintAddressHere", "mode": "pump", "creator_wallet": "CreatorPubkeyHere", "txs": [ { "type": "buy", "sol_amount": 0.3, "api_key": "" }, { "type": "sell", "percent": 100, "api_key": "" }, { "type": "buy", "sol_amount": 0.1, "api_key": "" }, { "type": "sell", "percent": 50, "api_key": "" } ] } ``` Success response [#success-response] `txs` comes back in the order you sent. ```json { "type": "bundle_success", "mint_address": "...", "mode": "pump", "bundle_id": "...", "txs": [ { "index": 1, "type": "buy", "wallet": "...", "sol_amount": 0.3, "signature": "..." }, { "index": 2, "type": "sell", "wallet": "...", "percent": 100, "signature": "..." } ] } ``` Errors are `{ "type": "bundle_error", "error": "tx 2: …" }` and name the leg that failed validation. # Bundles & snipers (/docs/bundles) Extra wallets that buy alongside the create. Every entry uses the same encrypted key format as `api_key` ([API keys](/docs/api-keys)). Solana — `bundle_wallets` [#solana--bundle_wallets] Buys land in the **same block** as the create. Works on `pump`, `bonk`, `stonk`, `bags`. | Field | Type | Notes | | ---------------- | ------- | ------------------------------------------ | | `bundle` | boolean | `true` | | `bundle_wallets` | string | Comma-separated `encrypted_key:sol_amount` | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "pump", "name": "Bundle Token", "ticker": "BNDL", "buy_amount": 1, "bundle": true, "bundle_wallets": ":0.5,:0.3,:0.2" } ``` Wallet limits: SOL quote **9**, pump stock quote **6**, pump USDC quote **3**. On a stock quote each wallet's SOL is swapped into the stock first; a wallet whose swap does not land in 5 s sits the deploy out. EVM — `bundle_buys` [#evm--bundle_buys] Legs execute in the **same transaction** as the launch. Works on `flap` (max 8, BNB Chain only), `pons` (max 10) and `poolstrade` (max 10). Each leg is a bare number (native amount, tokens to the deployer) or an object: ```json { "bundle_buys": [ 0.1, { "amount": 0.15, "wallet": "0xAAA0000000000000000000000000000000000001", "api_key": "" } ] } ``` `wallet` receives the tokens; `api_key` makes that wallet pay its own leg (otherwise the deployer fronts it). Snipers — `sniper_wallets` [#snipers--sniper_wallets] Buys fire **after** the create lands, each with its own delay. Send to **NA East** (`https://tx-nyc.j7tracker.io/deploy`). Not available on stock quotes, `stonk`, or EVM platforms. | Field | Type | Notes | | ---------------- | ------ | --------------------------------------------------- | | `sniper_wallets` | string | Comma-separated `encrypted_key:sol_amount:delay_ms` | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "pump", "name": "Sniped Token", "ticker": "SNIPE", "buy_amount": 1, "sniper_wallets": ":0.5:0,:0.3:500,:0.2:2000" } ``` Auto-sell [#auto-sell] `auto_sell: true` with `auto_sell_wallets` (`encrypted_key:percent:delay_ms`) sells after launch on `pump`, `bonk`, `bags` and `stonk`. # Buy API (/docs/buy) **`POST {base}/submit`** with **`"type": "buy_token"`**. Solana platforms and `pons`; the buyer is the wallet in `api_key`. Required fields [#required-fields] | Field | Type | Notes | | -------------- | ------ | ----------------------------------------------------------------------------------------------------- | | `type` | string | `buy_token` | | `session_id` | string | JWT, or `Authorization: Bearer` | | `api_key` | string | Encrypted key — Solana, or EVM for `pons` | | `mint_address` | string | Token mint / contract | | `sol_amount` | number | Amount to spend in SOL (ETH on `pons`). `lamports` (integer) takes priority; `buy_amount` is an alias | Optional fields [#optional-fields] | Field | Type | Notes | | ----------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `mode` | string | Omit or `auto` (default): the server reads the mint on-chain and picks the venue — pump curve / PumpSwap, LaunchLab (bonk / ray / stonk), Bags DBC, or the migrated Raydium / Meteora pool. Explicit `pump`, `bonk`, `ray`, `usd1`, `stonk`, `bags`, `pons` skip that read and build straight from the request | | `creator_wallet` | string | Only with an explicit `mode`: the creator pubkey (from [`/token/coin-config/:mint`](/docs/pair-registries)). Resolved on-chain when absent | | `quote_mint` | string | Only with an explicit `mode` on a quoted coin (USDC / PUMP / stock `pump`, USD1 / stock `bonk`, StonkFun pair): the quote CA. Auto mode reads it off the pool. `quote_token: "usdc"` is the legacy label | | `config_key` | string | Only with `mode: "bags"`: the coin's DBC config key | | `slippage_bps` | integer | Swap-leg slippage, default `200` (`500` on `pons`) | | `mayhem_mode` | boolean | Mayhem path for `pump` | | `bonkers_mode`, `locked_mode` | boolean | LaunchLab pool selectors | | `bribe_fee_sol` | number | Relay tip in SOL | | `request_id` | string | Echoed back on success / error | Quoted coins (stock / USDC / USD1 pairs) are bought in one transaction: SOL → quote through the quote's pool route, then the venue's buy leg. `batch_buy_token` (`buys: [{ sol_amount, api_key }]`, max 10) takes the same fields. ```json { "type": "buy_token", "session_id": "", "api_key": "", "mint_address": "MintAddressHere", "sol_amount": 0.1 } ``` Success response [#success-response] ```json { "type": "buy_success", "signature": "...", "mint_address": "...", "sol_amount": 0.1, "lamports": 100000000, "wallet_address": "...", "mode": "pump" } ``` Errors are `{ "type": "buy_error", "error": "…" }`; a `0` amount, an EVM `mode` other than `pons`, or a `mode` that is not a Solana launchpad (`Buys not supported for … mode`) are rejected. # Data Types (/docs/data-types) Every [feed event](/docs/feed-events) payload is made of the types below. Fields marked `?` are only present on some items. Tweet [#tweet] The object sent in `tweet`, `tweet_update`, `initialTweets` and pinned lists. ```ts interface Tweet { id: string; // X status id. The dedupe key across `tweet` and `tweet_update` createdAt: string | number; // ISO string or epoch ms depending on the source. `new Date(createdAt)` handles both type: 'TWEET' | 'REPLY' | 'QUOTE' | 'RETWEET'; author: Author; text: string; // Full text with t.co links expanded. Retweets carry the original post's text translation: Translation | null; // Set when X provided a translation components: Component[] | null; // Rich blocks for long-form posts isRetweet: boolean; isQuote: boolean; isReply: boolean; isSelfReply?: boolean; // Reply to the author's own post (thread) replyTo: NestedTweet | TweetRef | null; // Replies: the parent post, or just its ref while it resolves quotedTweet: NestedTweet | TweetRef | null; // Quotes (and retweets of quotes): the quoted post originalAuthor: OriginalAuthor | null; // Retweets: the original author media: Media; // Always empty on retweets, see `originalMedia` originalMedia: Media | null; // Retweets: the original post's media retweetedQuote: NestedTweet | null; // Retweet of a quote: the quoted post repliedQuote: NestedTweet | null; // Reply to a quote: the quoted post retweetedReplyTo: NestedTweet | null; // Retweet of a reply: the post that was replied to card: Card | null; article: Article | null; grok: GrokChat | null; poll: Poll | null; metrics: Metrics | null; tweetUrl: string; // Link to the post on X provider: string; // Which J7 ingest path delivered it (`p_v0`, `p_v1`, ...). Faster paths can carry less data isCustomAccount?: boolean; // Delivered because it is one of your custom / available accounts retweetTranslation?: Translation; // Retweets: translation of the original post // Only on `p_v1` posts, when the upstream detected a contract address: contractAddress?: string; chain?: string; contractAddressLabel?: string; preferredTradingUrl?: string; extraContractAddresses?: string[]; // Up to 4 more addresses } ``` Author [#author] ```ts interface Author { id: string; // X user id handle: string; // Without the @ name: string; avatar: string | null; verified: boolean; badge: string | null; // X verification badge as reported upstream parody: boolean; followersCount: number; bio?: string; location?: string; banner?: string; website?: string; verifiedType?: string; affiliateDescription?: string; // Affiliation label shown next to the name affiliateBadgeUrl?: string; affiliateUrl?: string; affiliateLabelType?: string; } interface OriginalAuthor { id: string; handle: string; name: string; avatar: string | null; verified: boolean; badge: string | null; } ``` NestedTweet [#nestedtweet] `replyTo`, `quotedTweet`, `retweetedQuote`, `repliedQuote` and `retweetedReplyTo` use a flat shape with the author fields inlined. A nested post can nest again (`quotedTweet.quotedTweet`, `quotedTweet.replyTo`, `replyTo.quotedTweet`) up to 3 levels deep. ```ts interface NestedTweet { id: string; handle: string; // Author handle name: string; // Author name avatar: string | null; verified: boolean; badge: string | null; parody: boolean; text: string; type?: 'TWEET' | 'REPLY' | 'QUOTE' | 'RETWEET'; createdAt?: string | number; media: Media; translation: Translation | null; card: Card | null; article: Article | null; grok: GrokChat | null; poll: Poll | null; metrics?: Metrics | null; components?: Component[] | null; quotedTweet?: NestedTweet | TweetRef | null; replyTo?: NestedTweet | TweetRef | null; followersCount?: number; tweetLink?: string; authorBio?: string; authorLocation?: string; authorWebsite?: string; authorBanner?: string; } // Sent in place of a NestedTweet while the referenced post hasn't been resolved yet. // A later `tweet_update` replaces it with the full NestedTweet. interface TweetRef { id: string; handle: string; } ``` Media [#media] ```ts interface Media { images: { url: string; width: number; height: number }[]; // width / height are 0 (not measured) videos: { url: string; thumbnail: string; width: number; height: number }[]; } ``` Rich content [#rich-content] ```ts interface Translation { from: string; // Source language to: string; // Target language text: string; } interface Component { type: 'text' | 'image' | 'video'; text?: string; url?: string; } interface Card { url: string; image: string | null; title: string; description: string; } interface Article { id: string; title: string; url: string; thumbnail: string | null; description: string; author: string; created_at: string; updated_at: string; body: { text: string; components: Component[] }; } interface GrokChat { id: string; conversation: { from: string; message: string; images: string[] }[]; } interface Poll { ends_at: string; choices: { label: string; count: number; image: string | null }[]; } interface Metrics { likes: number; quotes: number; replies: number; retweets: number; views: number; } ``` TwitterUser [#twitteruser] The user object carried by account events (follows, profile changes, pins, affiliations). Unlike `Author` it keeps X's nested layout, and extra upstream keys pass through untouched. ```ts interface TwitterUser { id: string; handle: string; verified: boolean; private: boolean; // Can also appear as `protected`, or under `profile` metrics: { followers: number }; profile: { name: string; avatar: string; banner: string; description: string | { text: string }; // Handle both forms location: string; url: string | { url: string }; // Handle both forms badge: string | null; parody: boolean; }; } ``` RawTweet [#rawtweet] The upstream copy of a post, used by `tweet_deleted` and by pinned / unpinned lists on custom accounts. ```ts interface RawTweet { id: string; created_at: string; type: string; author: { id: string; handle: string; profile: { name: string; avatar: string } }; body: { text: string }; } ``` SocialPost [#socialpost] The base of every non-X post (Truth Social, Instagram, TikTok, YouTube, Binance Square). Platform fields are listed under [`external_message`](/docs/feed-events#external_message). ```ts interface SocialPost { id: string; // Unique per post. Dedupe key createdAt: string; // ISO time type: 'EXTERNAL'; source: 'external'; platform: 'TRUTH SOCIAL' | 'INSTAGRAM' | 'TIKTOK' | 'YOUTUBE' | 'BINANCE SQUARE'; author: { handle: string; name: string; avatar: string; verified: boolean }; text: string; // HTML-entity encoded (& < > "). Decode before display media: { images: string[]; // Plain URL strings, unlike X videos: { url: string; poster?: string }[]; thumbnails?: string[]; }; tweetUrl: string; // Link to the post on its platform isRetweet: boolean; isQuote: boolean; isReply: boolean; quotedTweet: object | null; // Platform-specific originalAuthor: object | null; // Platform-specific replyTo: object | null; // Platform-specific card: object | null; // Link preview (Truth Social only) timestamp?: number; // Epoch ms. Only on items replayed in `initialTweets` } ``` # Deploy API (/docs/deploy) **`POST {base}/submit`** with **`"type": "create_token"`**. The launchpad goes in **`mode`** — see [Platforms](/docs/platforms) for the values and their extra fields. Required fields [#required-fields] | Field | Type | Notes | | ---------------- | ------- | ------------------------------------------------------------------------------------------------------ | | `type` | string | `create_token` | | `external` | boolean | Must be `true` for every API deploy | | `session_id` | string | JWT, or send `Authorization: Bearer` instead | | `api_key` | string | Encrypted key of the deploying wallet — Solana or EVM to match the `mode` ([API keys](/docs/api-keys)) | | `mode` | string | Platform. See [Platforms](/docs/platforms) | | `name`, `ticker` | string | Token name and symbol | Common optional fields [#common-optional-fields] | Field | Type | Notes | | ----------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `buy_amount` | number | Dev buy in the chain's native coin (SOL / BNB / ETH; USDC on Arc). `0` = no buy. Always native, even on a stock pair — the server swaps | | `image_url` | string | URL or `data:` URI | | `image_type` | string | `letter`, `ascii`, `sol_ascii` generate an image server-side | | `description`, `website`, `twitter`, `telegram` | string | Metadata / socials | | `private_desc` | boolean | Omit the default "Deployed using j7tracker.io" description | | `ref` | string | Referral code | | `bundle`, `bundle_wallets` | boolean, string | Solana same-block bundle — [Bundles & snipers](/docs/bundles) | | `bundle_buys` | array | EVM same-transaction bundle — [Bundles & snipers](/docs/bundles) | | `sniper_wallets` | string | Delayed buys after the create (NA East only) | | `auto_sell`, `auto_sell_wallets` | boolean, string | Auto-sell after launch (`pump`, `bonk`, `bags`, `stonk`) | | `multi_deploy` | number | 1–10 mints in one call (`pump`, `bonk`); `multi_deploy_buy_amount` per mint | | `vanity_mint_keypair` | string | Base58 keypair for a custom Solana mint | Quote pairs [#quote-pairs] Stock / stable-paired launches pick the pair per platform. `buy_amount` stays in the native coin. | Platform | Field | Live list | | -------------------------------------------------------------- | ------------------ | ------------------------------------------------ | | `pump` | `quote_mint` | [`/token/pump-quotes`](/docs/pair-registries) | | `bonk` | `quote_mint` | any LaunchLab quote (USD1, USDC, xStocks, …) | | `stonk` | `stonk_quote_mint` | [`/token/stonk-pairs`](/docs/pair-registries) | | `bnb` | `pair` | [`/token/fourmeme-pairs`](/docs/pair-registries) | | `genius` | `pair` | [`/token/genius-pairs`](/docs/pair-registries) | | `flap` | `pair` + `chain` | [`/token/flap-pairs`](/docs/pair-registries) | | `pons` | `pair` | [`/token/pons-pairs`](/docs/pair-registries) | | `argus` | `pair` | [`/token/argus-quotes`](/docs/pair-registries) | | `o1`, `long` | `pair` | ERC-20 address | Success response [#success-response] ```json { "type": "token_create_success", "mode": "PUMP", "mint_address": "...", "signature": "...", "buy_amount": 0.5, "token_amount": 12345678.9, "creator_wallet": "...", "all_signatures": ["..."] } ``` `mode` is echoed **upper-cased** on Solana platforms (`PUMP`, `STONK`, `BNB`) and lower-cased on most EVM ones (`flap`, `pons`, `stroid`) — compare case-insensitively. Stock / stable-quoted launches add `quote_mint`, `quote_symbol`, `quote_decimals` and `stock_per_sol`; EVM launches add `tx_hash` and `address`. Common errors [#common-errors] | Error | Cause | | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | `Unauthorized` | `api_key` could not be decrypted, or it is for the wrong chain | | `Missing name or ticker` | Empty `name` / `ticker` | | `… isn't on pump.fun's quote allow-list yet` | `quote_mint` not admitted by the launchpad | | `A deploy is already in progress` | Same wallet + ticker still in flight | | `Instant bond is SOL-only` / `Batch (build-only) deploys aren't available on swapped quotes` | Feature not available on a stock / USDC quote | Errors are `{ "type": "token_create_error", "error": "…", "mode": "…" }`. # Errors & Limits (/docs/errors) Limits [#limits] | Where | Limit | Over it | | ------------------------------ | ----------------------------------------------------- | ------------------------------------------------------------- | | Feed socket | **5** connections per user, shared with your app tabs | `Too many connections` | | Feed socket | **15** client → server messages per second per socket | Dropped silently | | Feed socket | Server pings every 30 s | Dropped after 60 s without a pong (Socket.IO answers for you) | | `GET /api/watched-accounts` | **30** per 10 s per user | **429** `rate_limited`, blocked 30 s | | `POST` account writes | **20** per 10 s per user | **429** `rate_limited`, blocked 45 s | | `POST` account writes | **3** running at once per user | **429** `too_many_inflight` | | `POST /api/accounts/available` | **25,000** handles per call | **400** `too_many` | Every **429** carries a `Retry-After` header (seconds). A block lasts its full time even if you stop, so back off instead of retrying in a loop, and send account writes one after another rather than in parallel. Feed socket [#feed-socket] Connect errors arrive on Socket.IO's `connect_error` as `err.message`. Errors after `user_connected` arrive as an [`auth_error`](/docs/feed-events#auth_error) event. ```ts { // The JWT in `auth.token` is expired or isn't an access token. Log in again; don't retry with the same token. INVALID_TOKEN: { event: 'connect_error', message: 'Invalid token', }, // The account behind the JWT is disabled. Final. ACCOUNT_DISABLED: { event: 'connect_error', message: 'Account disabled', }, // The JWT doesn't map to a user. UNKNOWN_USER: { event: 'connect_error', message: 'Unknown user', }, // You already have 5 feed sockets open (app tabs count). Close one. TOO_MANY_CONNECTIONS: { event: 'connect_error' | 'auth_error', message: 'Too many connections', }, // The JWT sent in `user_connected` failed. Log in again. INVALID_SESSION: { event: 'auth_error', message: 'Invalid session', }, } ``` Accounts API [#accounts-api] Every [accounts](/docs/accounts) route answers errors with an HTTP status and a JSON body: ```ts interface ErrorResponse { ok: false; // `success: false` on GET /api/watched-accounts code: string; // Short stable code, match on this error: string; // Human-readable message } ``` Two errors come before auth and have no `code`: `{ "error": "unauthorized" }` (**401**) and `{ "error": "origin not allowed" }` (**403**). ```ts { // No JWT, or an invalid one, in `Authorization: Bearer` / `x-session-id`. UNAUTHORIZED: { status: 401, error: 'unauthorized', }, // A browser request from an origin other than j7tracker. Call the API from a server or script. ORIGIN_NOT_ALLOWED: { status: 403, error: 'origin not allowed', }, // Your account is disabled. disabled: { status: 403, error: 'Account disabled', }, // The JWT is valid but its user no longer exists. no_user: { status: 404, error: 'User not found', }, // Empty handle, or no handles in a batch body. invalid_handle: { status: 400, error: 'Invalid account handle' | 'handle is required' | 'handles[] required', }, // More than 25,000 handles in one body. too_many: { status: 400, error: 'too many handles', }, // The handle is already in your custom or available list. already_added: { status: 409, error: '@ is already in your custom accounts', }, // Nobody tracks this handle, so it can't be added as an available account. not_available: { status: 404, error: '@ is not in the available accounts list', }, // Fewer than 15 deploys and no purchased slots. no_deploys: { status: 403, error: 'You need at least 15 deploys to add custom accounts. You have deploys.', }, // Every slot is used. Remove a custom account or buy more slots. limit_reached: { status: 403, error: | "You've reached your limit of custom accounts ( deploys)." | 'You can track 1 custom account (15+ deploys). Remove @ first, or reach 50 deploys for 2 accounts.', }, // The X account doesn't exist or is unavailable. tracking_failed_invalid: { status: 422, error: 'Could not track @ - account does not exist or is unavailable', }, // J7's tracking capacity is full. Contact an admin. tracking_failed_quota: { status: 429, error: 'Could not track @ - API account limit reached. Contact admin to upgrade.', }, // The tracking provider failed. Retry shortly. tracking_failed_other: { status: 502, error: 'Could not track @ - tracking provider error, try again shortly', }, // The provider refused to stop tracking. Nothing was changed; retry. untrack_failed: { status: 502, error: 'Could not remove @ - tracking provider error, nothing was changed. Try again shortly.', }, // Over a rate limit. Wait `Retry-After` seconds. rate_limited: { status: 429, error: 'Too many requests, slow down', }, // 3 account writes are already running for you. Wait for them to finish. too_many_inflight: { status: 429, error: 'Too many account changes in progress, wait for them to finish', }, // Server error on our end. Retry shortly. internal_error: { status: 500 | 503, error: 'Could not save your account, try again shortly', }, } ``` # Events (/docs/feed-events) Every event is an **event name** plus **one JSON payload**: listen by name with `socket.on(name, handler)`. Object types are in [Data Types](/docs/data-types). Non-X posts all come as [`external_message`](#external_message). How a post arrives [#how-a-post-arrives] A new post is sent **once** as `tweet`, then again as `tweet_update` each time a fuller version comes in. J7 ingests X from more than one provider, and each provider sends a fast version first and richer versions later. Whichever copy reaches J7 first becomes the `tweet`; every later copy of the same `id` becomes a `tweet_update`. ``` tweet → first copy: text, author, often no media yet tweet_update → media / quoted post / card resolved tweet_update → long-form body, article, translation hydrated ``` Keep one map keyed on `id` and merge. A later update can carry *less* than an earlier one (a fast path that skipped media), so never overwrite a non-empty field with an empty one. initialTweets [#initialtweets] The **backlog**, sent once right after `user_connected`. Up to 100 items, newest first, with your hidden accounts removed. It **mixes** X posts with social posts and webhook cards: tell them apart by `source === 'external'` and `isWebhook`. Only sent when at least 10 items remain, so don't wait on it. Replace your list with it. ```ts type InitialTweetsEvent = (Tweet | SocialPost | WebhookCard)[]; ``` tweet [1] [#tweet-1] The **first** copy of a new post from a watched X account. Drop it if you already have its `id`: custom accounts can deliver it twice (fast and full versions). ```ts type TweetEvent = Tweet; ``` tweet_update [2] [#tweet_update-2] A **newer, fuller** copy of a post that was already sent. Sent every time media, quoted posts, articles, translations or metrics resolve, often several times per post. If you don't have the `id` yet, treat it as a new `tweet`. ```ts type TweetUpdateEvent = Tweet; ``` quoted_tweet [#quoted_tweet] The quoted post on its own, sent just before the `tweet` / `tweet_update` that quotes it. The parent already contains it as `quotedTweet`, so this can be ignored. ```ts type QuotedTweetEvent = NestedTweet; ``` tweet_deleted [#tweet_deleted] Sent when a watched account deletes a post. ```ts interface TweetDeletedEvent { id: string; // The deleted post's id deletedTweet: Tweet | null; // J7's cached copy if it still had one. Always null for custom accounts tweet: RawTweet; // Upstream copy of the post, present even when `deletedTweet` is null isCustomAccount?: true; } ``` following_update [#following_update] Sent when a watched account follows someone. On the main feed, follow / unfollow / profile events are deduplicated server-side within 30 s. ```ts interface FollowingUpdateEvent { id: string; // `follow___`. On the main feed a `follow_scan` with this id follows user: TwitterUser; // The watched account following: TwitterUser; // The account they followed others: TwitterUser[]; // Other watched accounts that recently followed the same target provider?: string; // Main feed only isCustomAccount?: true; } ``` unfollowing_update [#unfollowing_update] Sent when a watched account unfollows someone. ```ts interface UnfollowingUpdateEvent { user: TwitterUser; // The watched account unfollowing: TwitterUser; // The account they unfollowed provider: string; } ``` affiliated_update [#affiliated_update] Sent when a watched business account adds an affiliate. ```ts interface AffiliatedUpdateEvent { id?: string; // Main feed only. A `follow_scan` for `affiliate` follows user: TwitterUser; // The organisation affiliate: TwitterUser; // The account it added } ``` unaffiliated_update [#unaffiliated_update] Sent when a watched business account removes an affiliate. ```ts interface UnaffiliatedUpdateEvent { user: TwitterUser; // The organisation affiliate: TwitterUser; // The account it removed } ``` profile_update [#profile_update] Sent when a watched account changes its name, bio, avatar, banner, location, website or privacy. ```ts interface ProfileUpdateEvent { user: TwitterUser; // The profile after the change before: TwitterUser; // The profile before the change // Main feed only. On custom accounts, diff `user` against `before` yourself: modifications?: ( | 'profile.name' | 'profile.description' | 'profile.avatar' | 'profile.banner' | 'profile.location' | 'profile.url' )[]; privated?: boolean; // The account went private unprivated?: boolean; // The account went public } ``` profile_affiliation_update [#profile_affiliation_update] Sent when a watched account's **own** affiliation changes. ```ts interface ProfileAffiliationUpdateEvent { user: TwitterUser; affiliation: { from: TwitterUser | null; // Previous organisation (if any) to: TwitterUser | null; // New organisation (if any) }; } ``` profile_pinned_update [#profile_pinned_update] Sent when a watched account pins a post. ```ts interface ProfilePinnedUpdateEvent { user: TwitterUser; pinned: Tweet[]; // The newly pinned post(s) } ``` profile_unpinned_update [#profile_unpinned_update] Sent when a watched account unpins a post. ```ts interface ProfileUnpinnedUpdateEvent { user: TwitterUser; unpinned: Tweet[] | RawTweet[]; // RawTweet on custom accounts } ``` profile_suspended_update [#profile_suspended_update] Sent when a watched account is suspended. ```ts interface ProfileSuspendedUpdateEvent { user: TwitterUser; type: 'suspended'; } ``` profile_deactivated_update [#profile_deactivated_update] Sent when a watched account is deactivated. ```ts interface ProfileDeactivatedUpdateEvent { user: TwitterUser; type: 'deactivated'; } ``` token_meta [#token_meta] Token info for a contract address found in an item you received (a tweet, social post or account event). Up to 5 per item. It can arrive **before** the item, so buffer it for a few seconds. ```ts interface TokenMetaEvent { id: string | null; // The parent item's id mint: string; // Solana mint, or EVM address (lowercased) tokenMeta: { mint: string; symbol: string; name: string; image: string; marketCapUsd: number; marketCapSol: number | null; // null for EVM tokens market: string; // e.g. `pumpfun` at: number; // Epoch ms the data was fetched chain?: string; // EVM only liquidityUsd?: number; // EVM only isHoneypot?: boolean; // EVM only }; } ``` follow_scan [#follow_scan] The latest contract address posted by the account in a `following_update` / `affiliated_update`, matched on `id`. ```ts interface FollowScanEvent { id: string; // Same id as the following_update / affiliated_update handle: string; // The followed / affiliated account scan: { cached: boolean; contracts: | { ok: true; indexing: boolean; // true while the account is still being indexed (rows is empty) total: number; deleted: number; undeleted: number; rows: { ca: string; chain: string; tweetId: string; twitterHandle: string; tweetTime: string; isDeleted: boolean; content: string; }[]; // Latest CA only } | { ok: false; error: string }; }; } ``` external_message [#external_message] Every non-X post: Truth Social, Instagram, TikTok, YouTube, Binance Square, J7 webhook cards and their media patches. Social posts are global (everyone gets every one, so filter by `author.handle` yourself) and are also replayed in `initialTweets`. All platform posts extend [`SocialPost`](/docs/data-types#socialpost). ```ts type ExternalMessageEvent = SocialPost | WebhookCard | SocialPatch; socket.on('external_message', (m) => { switch (m.type) { case 'TIKTOK_VIDEO_UPDATE': case 'YOUTUBE_IMAGE_UPDATE': case 'YOUTUBE_DELETION': return patch(m); // Updates a post you already have, matched on id } if (m.isWebhook) return webhook(m); if (m.isTruthSocial) return truth(m); if (m.isInstagram) return instagram(m); if (m.isTikTok) return tiktok(m); if (m.isYouTube) return youtube(m); if (m.isBinanceSquare) return binanceSquare(m); }); ``` Flags that don't apply are `false` or missing, so test for truthy. Truth Social [#truth-social] ```ts interface TruthSocialPost extends SocialPost { platform: 'TRUTH SOCIAL'; isTruthSocial: true; truthSocialUrl: string; truthSocialPostType?: 'RETWEET' | 'QUOTE'; card: { title: string; description: string; url: string; image: string | null } | null; // ReTruth (isRetweet: true). Top-level `text` and `media` are empty; the original is in `quotedTweet` quotedTweet: TruthSocialRef | null; originalAuthor?: SocialPost['author']; retweetedUser?: SocialPost['author']; originalTweetText?: string; originalMedia?: SocialPost['media']; originalTweetUrl?: string; // Quote (isQuote: true) quotedUser?: SocialPost['author']; } interface TruthSocialRef { id: string; text: string; author: SocialPost['author']; media: SocialPost['media']; truthSocialUrl: string; card?: TruthSocialPost['card']; // ReTruths nestedQuote?: TruthSocialRef | null; // Quotes: a quote inside the quote } ``` Video posters are added to `media.images`. Instagram [#instagram] ```ts interface InstagramPost extends SocialPost { platform: 'INSTAGRAM'; isInstagram: true; instagramUrl: string; // Stories look like /stories/// instagramPostType: 'POST' | 'REEL' | 'STORY'; // Defaults to POST hashtags: string[]; mentions: string[]; mediaCount: number; } ``` TikTok [#tiktok] The video URL is often not ready yet, so `media.videos` starts empty and a `TIKTOK_VIDEO_UPDATE` patch fills it in. ```ts interface TikTokPost extends SocialPost { platform: 'TIKTOK'; isTikTok: true; tiktokUrl: string; tiktokEventType: 'UPLOAD' | 'REPOST' | 'LIVE_STARTED'; tiktokKind: 'video' | 'photo' | 'carousel' | 'live'; tiktokVideoId: string | null; tiktokRoomId: string | null; // Set for LIVE_STARTED hashtags: string[]; mentions: string[]; mediaCount: number; duration: number | null; // Seconds music: { title: string; author: string; original: boolean } | null; originalAuthor: SocialPost['author'] | null; // REPOST (isRetweet: true) } ``` `LIVE_STARTED` posts have `tiktokKind: 'live'` and `text` starting with `🔴`. YouTube [#youtube] ```ts interface YouTubePost extends SocialPost { platform: 'YOUTUBE'; isYouTube: true; id: string; // `yt-` youtubeUrl: string; youtubeVideoId: string; youtubeChannelId: string; // `UC…`. Identify channels by this: handles can change youtubeChannelUrl: string; youtubeSubtype: 'VIDEO' | 'SHORT'; youtubeTitle: string; youtubeDuration: string; // e.g. `12:34` youtubeViewCount: number; youtubeDeleted: boolean; text: string; // Title, a blank line, then up to 400 characters of the description // author.handle is the @handle when known, otherwise the `UC…` channel id } ``` Binance Square [#binance-square] Newly pinned posts are sent as regular posts. ```ts interface BinanceSquarePost extends SocialPost { platform: 'BINANCE SQUARE'; isBinanceSquare: true; id: string; // `bsq-` binanceSquareUrl: string; contentType: string; text: string; // Title, a blank line, then the body poll: { question: string; ends_at: string; closed: boolean; choices: { label: string; count: number; image: string | null }[]; } | null; metrics: { reply_count: number; like_count: number; comment_count: number; share_count: number; view_count: number; }; translation: { from: string; to: string; text: string } | null; tendency: string; coinPairs: string[]; // e.g. `BNBUSDT` hashtags: string[]; mentions: string[]; quotedTweet: BinanceSquareRef | null; replyTo: BinanceSquareRef | null; originalAuthor: SocialPost['author'] | null; // Quotes: the quoted author } interface BinanceSquareRef { id: string; text: string; author: SocialPost['author']; media: SocialPost['media']; poll: BinanceSquarePost['poll']; translation: BinanceSquarePost['translation']; coinPairs: string[]; binanceSquareUrl: string; } ``` Webhook cards [#webhook-cards] J7's own announcement cards, shaped like Discord embeds. Current accounts (`webhook.handle`): `stonkpairs` (NEW STONKFUN PAIR), `ponspairs` (NEW PONS PAIR) and `pumppairs` (NEW PUMP PAIR). Unlike social posts, webhook text is **not** HTML-encoded. ```ts interface WebhookCard { id: string; // e.g. `wh-pumppairs:AAPLx` type: 'WEBHOOK'; source: 'webhook'; isWebhook: true; createdAt: number; // Epoch ms timestamp: number; // Epoch ms author: { name: string; // Display name handle: string; // `webhook:` avatar: string; verified: false; }; text: string; webhook: { handle: string; // Stable account key displayName: string; username: string; // Same as displayName avatarUrl: string; url: string; badge: string; content: string; embeds: WebhookEmbed[]; // Up to 10 }; } interface WebhookEmbed { author: { name: string; url: string; iconUrl: string }; title: string; titleSize: string; url: string; description: string; address: string; // Solana or EVM contract address, or "" color: string; // Hex, e.g. `#4ade80` fields: { name: string; value: string; inline: boolean }[]; // Up to 25 image: string; thumbnail: string; thumbnailPosition: string; thumbnailSize: string; thumbnailShape: string; footer: { text: string; iconUrl: string }; timestamp: number; } ``` Patches [#patches] These update a post you already have instead of adding a new one. Match them on `id`. ```ts // Add `{ url: videoUrl, poster }` to the TikTok post's media.videos interface TikTokVideoUpdate { type: 'TIKTOK_VIDEO_UPDATE'; id: string; tiktokUrl: string; videoUrl: string; poster: string; } // Replace the YouTube post's thumbnail interface YouTubeImageUpdate { type: 'YOUTUBE_IMAGE_UPDATE'; id: string; videoId: string; imageUrl: string; } // The video was deleted: set `youtubeDeleted: true` on your copy interface YouTubeDeletion { type: 'YOUTUBE_DELETION'; id: string; videoId: string; youtubeUrl: string; } type SocialPatch = TikTokVideoUpdate | YouTubeImageUpdate | YouTubeDeletion; ``` charity_event [#charity_event] The pump.fun charity registry, relayed as-is from upstream as it changes. Upstream doesn't guarantee every field, so read `charity` first and fall back to `summary`. ```ts interface CharityEvent { type: 'new_charity' | 'updated_charity' | 'deleted_charity'; time?: string; // ISO time charity?: { id: string; name: string; slug: string; isVerified: boolean; logo?: string; city?: string; state?: string; country?: string; tax_id?: string; website?: string; }; summary?: { id: string; name: string; slug: string; verified: boolean }; } ``` hidden_accounts_updated [#hidden_accounts_updated] Your full hidden X list, sent after you [remove](/docs/accounts#remove-an-account) (hide) or re-add (unhide) a main-feed account. **Replace** your copy, don't merge. ```ts interface HiddenAccountsUpdatedEvent { hidden: string[]; // Lowercased handles } ``` status [#status] Whether J7's upstream X feed is connected. This is **not** your own socket's state. ```ts interface StatusEvent { connected: boolean; } ``` auth_error [#auth_error] The JWT sent in `user_connected` was rejected. See [Errors & Limits](/docs/errors#feed-socket). ```ts interface AuthErrorEvent { error: 'Invalid session' | 'Too many connections'; } ``` pong [#pong] Reply to your `ping`. No payload. # Connect (/docs/feed) The feed streams every [event](/docs/feed-events) for the accounts J7 tracks, your custom accounts and every social platform. It's a **Socket.IO v4** server, so use a Socket.IO client (a plain WebSocket won't work). | Host | Region | | -------------------------- | ---------- | | `https://nyc.j7tracker.io` | NA East | | `https://dfw.j7tracker.io` | NA Central | Both carry the same feed. `GET /api/health` returns **200** when a box is up. 1. Connect with `transports: ['websocket']` and your JWT in `auth.token`. 2. On every `connect` (reconnects too), emit `user_connected` with the same JWT. **Nothing arrives until you do.** 3. You get `initialTweets` (the backlog), then live events. ```js import { io } from "socket.io-client"; // npm i socket.io-client@4 const JWT = ""; const socket = io("https://nyc.j7tracker.io", { transports: ["websocket"], auth: { token: JWT }, }); socket.on("connect", () => socket.emit("user_connected", JWT)); socket.on("connect_error", (err) => console.error(err.message)); socket.on("auth_error", (e) => console.error(e.error)); const posts = new Map(); const upsert = (t) => posts.set(t.id, { ...posts.get(t.id), ...t }); socket.on("initialTweets", (items) => { posts.clear(); items.forEach(upsert); }); socket.on("tweet", upsert); socket.on("tweet_update", upsert); socket.on("external_message", (m) => console.log(m.platform, m.author?.handle, m.text)); ``` ```python import socketio # pip install "python-socketio[client]" JWT = "" sio = socketio.Client() posts = {} def upsert(t): posts[t["id"]] = {**posts.get(t["id"], {}), **t} @sio.event def connect(): sio.emit("user_connected", JWT) @sio.on("initialTweets") def initial(items): posts.clear() for t in items: upsert(t) sio.on("tweet", upsert) sio.on("tweet_update", upsert) sio.on("external_message", lambda m: print(m.get("platform"), m.get("author", {}).get("handle"), m.get("text"))) sio.on("auth_error", lambda e: print(e.get("error"))) sio.connect("https://nyc.j7tracker.io", transports=["websocket"], auth={"token": JWT}) sio.wait() ``` Good to know [#good-to-know] * **Dedupe by `id`.** A post arrives once as `tweet`, then as `tweet_update`s as it fills in. See [How a post arrives](/docs/feed-events#how-a-post-arrives). * **You get** every main-feed account (minus ones you [removed](/docs/accounts#remove-an-account)), your [custom and available accounts](/docs/accounts) flagged `isCustomAccount: true`, and every social post. * **Optional:** emit `ping` and the server replies `pong`, handy for measuring latency. * Limits and connect errors are on [Errors & Limits](/docs/errors). # Overview (/docs) Same flows as the app, over HTTP. One route, **`POST {base}/submit`**, with a JSON body whose **`type`** picks the action and whose **`mode`** picks the launchpad. Base URL [#base-url] Pick the region closest to you. Every base ends in `/deploy`. | Region | Base | | ------- | ------------------------------------ | | NA East | `https://tx-nyc.j7tracker.io/deploy` | | NA West | `https://tx-lax.j7tracker.io/deploy` | | Europe | `https://tx-fra.j7tracker.io/deploy` | | Asia | `https://tx-sgp.j7tracker.io/deploy` | The old `nyc` / `lax` / `eu` / `sgp` hosts are retired for the deploy API. `GET /ping` on any base returns latency for region selection. `sniper_wallets` and `mode: "ansem"` requests must go to **NA East**. Auth [#auth] ```http Authorization: Bearer ``` Or put the same JWT in the body as **`session_id`**. Wallet signing uses an **encrypted `api_key`** from the app — see [API keys](/docs/api-keys). Endpoints [#endpoints] | Method | Path | `type` | Purpose | | ------ | --------- | -------------- | ------------------------------------------------------------- | | POST | `/submit` | `create_token` | [Deploy](/docs/deploy) a token | | POST | `/submit` | `sell_token` | [Sell](/docs/sell) a % of a wallet's balance | | POST | `/submit` | `buy_token` | [Buy](/docs/buy) with a SOL amount | | POST | `/submit` | `bundle` | [Bundle](/docs/bundle) 1–4 buys / sells atomically | | GET | `/ping` | — | Returns `ok`. Call it every 15–30 s to keep the TLS path warm | Quote-pair lists live on the same hosts under `/token/…` — see [Pair registries](/docs/pair-registries). Requests [#requests] * Send the JSON as **`Content-Type: text/plain;charset=UTF-8`** to skip the CORS preflight (browsers / extensions). `application/json` works too. * `username` is taken from the JWT; anything in the body is overwritten. * A response arrives within **30 s** (`stroid` \~45 s), otherwise **504** with an optional `last_status`. * Errors are `{ "type": "_error", "error": "…" }`. The HTTP layer may also return **401** (bad JWT) or **400** (bad `type`). # Pair registries (/docs/pair-registries) Each stock-paired launchpad keeps its own list of quote tokens, and those lists change without notice. Read them live instead of hardcoding. Same regional hosts as the deploy API, under **`/token/`**: ```bash curl -H "Authorization: Bearer " https://tx-nyc.j7tracker.io/token/pump-quotes ``` * **Auth:** `Authorization: Bearer ` or `x-session-id: `. * **Rate limit:** 5 requests / 1.5 s per user across `/token/*`. Cache client-side. * **Region:** any region, except `pons-pairs` and `argus-quotes` which are **NA East only**. * Every response has `success`, the list, and `updatedAt` (ms). `updatedAt: 0` = not ready yet, retry. | Endpoint | For | Pass as | | ------------------------------ | ----------------------------------------- | -------------------------------------------------- | | `GET /token/pump-quotes` | `pump` | `quote_mint` ← `mint` | | `GET /token/stonk-pairs` | `stonk` | `stonk_quote_mint` ← `mint` | | `GET /token/fourmeme-pairs` | `bnb` | `pair` ← `symbol` or `address` | | `GET /token/genius-pairs` | `genius` | `pair` ← `symbol` or `address` | | `GET /token/flap-pairs` | `flap` | `pair` ← `symbol` or `address` (+ `chain`) | | `GET /token/pons-pairs` | `pons` | `pair` ← `ca` | | `GET /token/argus-quotes` | `argus` | `pair` ← `ca` | | `GET /token/otc-rewards` | OTC Desks | `otc_reward_mint` ← `mint` | | `GET /token/coin-config/:mint` | trading | `creator_wallet`, `quote_mint` of an existing coin | Mints pump.fun's `create_v2` accepts as a quote — SOL, USDC and allow-listed stocks. Refreshed every **10 s**. ```json { "success": true, "quotes": [ { "mint": "So11111111111111111111111111111111111111112", "symbol": "SOL", "name": "Solana", "decimals": 9, "priceUsd": 190.12, "minimumSolBuy": 0.01, "isNew": false } ], "updatedAt": 1757500000000 } ``` `POST /token/pump-quotes/refresh` forces a re-pull (3 s cooldown). Everything StonkFun lets you launch against, merged with the on-chain LaunchLab whitelist. Refreshed every **30 s**. ```json { "success": true, "pairs": [ { "mint": "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh", "symbol": "NVDAX", "name": "NVIDIA", "decimals": 8, "category": "xstock", "categoryLabel": "xStock", "isNew": false } ], "custom": [], "updatedAt": 1757500000000 } ``` `category`: `xstock`, `prestock`, `backpack`, `tessera`, `currency`, `solana`, `custom`, or `new` for a pair seen on-chain before StonkFun listed it. `custom` holds one-off mints — only show on explicit search. `POST /token/stonk-pairs/refresh` forces a re-pull. Raised tokens a four.meme curve can be priced in: BNB, stables, Binance bStocks and four.meme's own 4Stocks. Refreshed every **60 s**. ```json { "success": true, "pairs": [ { "address": "0x02fca66c1d1afb4e2a7884261eb00f63598a7436", "symbol": "NVDAB", "displaySymbol": "NVDA", "name": "NVIDIA Corp", "decimals": 18, "stock": true, "fourStock": false, "native": false, "raisedAmount": "60", "priceUsd": 181.2, "isNew": false } ], "updatedAt": 1757500000000 } ``` `fourStock: true` rows launch through the OpenFour route (no `token_tax`, no `rush_mode`). Quote tokens approved by Genius' factory on BNB Chain — BNB first, then stables and bStocks. ```json { "success": true, "pairs": [ { "address": "0x0000000000000000000000000000000000000000", "symbol": "BNB", "native": true, "stock": false, "decimals": 18, "isNew": false } ], "release": "…", "updatedAt": 1757500000000 } ``` Quote tokens flap.sh's Portal accepts, **per chain**. Hand-maintained and verified against the Portal on-chain. ```json { "success": true, "chains": { "bnb": { "pairs": [ { "address": "0x0000000000000000000000000000000000000000", "symbol": "BNB", "displaySymbol": "BNB", "decimals": 18, "stock": false, "native": true }, { "address": "0x02fca66c1d1afb4e2a7884261eb00f63598a7436", "symbol": "NVDAB", "displaySymbol": "NVDA", "decimals": 18, "stock": true, "native": false } ], "updatedAt": 1757500000000 }, "robinhood": { "pairs": [], "updatedAt": 1757500000000 } } } ``` pons.fun's approved pair-token whitelist on Robinhood Chain, rebuilt from the factory's approval events. Other regions answer `404`. ```json { "success": true, "pairs": [ { "ca": "0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec", "symbol": "NVDA", "approvedAt": 1756500000000 } ], "updatedAt": 1757500000000 } ``` Native ETH is not listed — send `pair: "eth"` or omit `pair`. Quote tokens approved on Argus' Portal on Arc. Polled every 30 s plus instant ingest on approval events. `POST /token/argus-quotes/refresh` forces a re-pull (3 s cooldown). ```json { "success": true, "quotes": [ { "ca": "0x…", "symbol": "USDC", "decimals": 6, "approved": true, "approvedAt": 1756500000000 } ], "updatedAt": 1757500000000 } ``` Pass `ca` as `pair`. `approved: false` marks a quote the Portal has since revoked. Reward tokens otcdesks.cash offers for holder-reward pump launches. Refreshed every **5 min**. ```json { "success": true, "rewards": [ { "mint": "XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp", "symbol": "AAPLx", "name": "Apple", "kind": "stock", "decimals": 8, "isNew": false } ], "pending": [ { "slug": "NKE", "symbol": "NKE", "name": "Nike", "kind": "stock" } ], "updatedAt": 1757500000000 } ``` `pending[]` stocks have no mint yet — launch them as `otc_reward_mint: "pending:"`. `POST /token/otc-rewards/refresh` forces a re-pull (15 s global cooldown). On-chain launch config of an existing Solana coin — what to pass as `creator_wallet` and which quote it launched on. ```json { "success": true, "data": { "mint": "…", "coinCreator": "", "quoteToken": "stock", "quoteMint": "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh", "quoteSymbol": "NVDAX", "launchpad": null, "stonkFeeMode": null, "stonkRewardTaxBps": null, "cashback": false, "mayhemMode": false, "graduated": false, "onBondingCurve": true, "hasFeeSharing": true, "feeShareholders": ["…"] } } ``` `quoteToken`: `sol`, `usdc`, `stock` (pump stock quote) or `stonk` (StonkFun coin, pair in `quoteMint`). `launchpad` is `"stonk"` for StonkFun coins. `400` for a malformed mint, `500` if the RPC read failed. # Platforms (/docs/platforms) Every deploy is the same [`create_token`](/docs/deploy) body plus the fields below. `buy_amount` is always in the chain's native coin. Solana [#solana] | Platform | `mode` | Key | Notes | | ---------------------------------------- | -------------------------- | ------ | ------------------------------------------------------------------------------------ | | Pump.fun | `pump` | Solana | SOL / USDC / stock quotes, fee sharing, mayhem, bundle / snipe | | OTC Desks | `pump` + `otc_reward_mint` | Solana | Pump launch whose fees buy a stock for holders | | | `pump` + `paid_mode` | Solana | Pump launch whose fees pay an X handle via usepaid.app | | Bonk | `bonk` | Solana | LaunchLab; SOL / USD1 / any admitted quote, bundle / snipe | | StonkFun | `stonk` | Solana | LaunchLab under StonkFun; xStocks / PreStocks / currency pairs, fee or dividend coin | | Bags | `bags` | Solana | Fee claimers by social handle | | Ansem | `ansem` | Solana | Pump launch + community airdrop. **NA East only** | EVM [#evm] | Platform | `mode` | Chain | Notes | | ------------------------------------------ | ------------ | ---------------------- | --------------------------------------------------------------------------- | | four.meme | `bnb` | BNB | BNB / stables / bStocks / 4Stocks, tax tokens | | Genius | `genius` | BNB | BNB / USDT / USDC / bStock pairs | | Flap | `flap` | BNB · Robinhood | Tax + holder dividends, PVE, `bundle_buys` | | Pons | `pons` | Robinhood | ETH / USDG / stock pairs, creator tax, dividends, `bundle_buys`, buy & sell | | Long | `long` | Robinhood | Stock-paired Doppler launch, no dev buy | | Pools | `poolstrade` | Robinhood | Instant Uniswap v4 pool, `bundle_buys` | | Bags | `robinhood` | Robinhood | Bags on Robinhood Chain, fee recipients | | o1 | `o1` | Base · Robinhood · BNB | Locked Uniswap v4 liquidity, quote pairs | | Klik | `klik` | Ethereum · Robinhood | Liquidity tiers | | Stroid | `stroid` | Ethereum | Fee recipients, virtual-LP tier. \~45 s | | Clanker | `clanker` | Base | Common fields only | | Argus | `argus` | Arc | USDC-quoted, tax splits | | Foci | `foci` | Arc | USDC-quoted, creator tax | Pass `chain` on `flap` and `o1`; `klik` takes `chain: "robinhood"` to leave Ethereum. Platform fields [#platform-fields] | Field | Type | Default | Notes | | ------------------------ | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | | `quote_mint` | string | SOL | Quote the coin is priced in: WSOL, USDC mint, or any stock on pump's allow-list — [`/token/pump-quotes`](/docs/pair-registries) | | `creator_fee_bps` | integer | pump default | Creator fee, max `300` (3%) | | `mayhem_mode` | boolean | `false` | Mayhem / Token-2022. SOL quote only | | `cashback_mode` | boolean | `false` | 0% creator fees. Alias `no_creator_fees` | | `pump_fee_shareholders` | array | — | Fee split, `share_bps` must total `10000` (below) | | `auto_swap_slippage_bps` | integer | `200` | SOL→USDC swap slippage on a USDC quote | **Fee shareholders** — each entry is one of `address`, `github_username`, `twitter_username`, or `{ "type": "charity", "address": "" }`, plus `share_bps`. Charity configs come from `GET https://nyc.j7tracker.io/utils/charity/search?term=…` → `POST /utils/charity/resolve` with `{ "charityBeneficiaries": [{ "charityId", "weight" }] }` (weights total 100) → `{ "id": "" }`. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "pump", "name": "Nvidia Printer", "ticker": "NVPRINT", "quote_mint": "Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh", "buy_amount": 1.5, "image_url": "https://example.com/logo.png", "pump_fee_shareholders": [ { "address": "Wallet111...", "share_bps": 5000 }, { "twitter_username": "myhandle", "share_bps": 5000 } ] } ``` Non-SOL quote rules (USDC / stocks): `sniper_wallets` and `bundle_wallets` work as on SOL — bundle wallets are trimmed to what fits the deploy bundle (up to **8**, typically **6** with a dev buy; SOL: **9**), never rejected. `mayhem_mode` is refused (pump's program only allows it on SOL), and so are `instant_bond` and build-only batch deploys. With `multi_deploy`, only token #1 carries the quote — the extra tokens are plain SOL launches. Sell / buy later with `mode` omitted and the server reads the quote off the curve. A normal `mode: "pump"` launch. Setting `otc_reward_mint` turns it into an otcdesks.cash reward coin: 100% of creator fees go to the OTC vault, which buys the reward for holders. | Field | Type | Notes | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------ | | `otc_reward_mint` | string | Reward mint from [`/token/otc-rewards`](/docs/pair-registries), or `pending:` for a stock without a mint yet | | `otc_reward_symbol` | string | Ticker as otcdesks spells it (`AAPLx`) | | `otc_reward_basket` | string\[] | 2–5 unique reward mints, cycled per payout. No `pending:` entries | | `otc_pair_symbol` | string | Only with a stock `quote_mint`: the quote's ticker | `pump_fee_shareholders` and `cashback_mode` are ignored; `website` becomes `https://otcdesks.cash/coin/`. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "pump", "name": "Apple Dividends", "ticker": "APLDIV", "buy_amount": 0.5, "image_url": "https://example.com/logo.png", "otc_reward_mint": "XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp", "otc_reward_symbol": "AAPLx" } ``` | Field | Type | Default | Notes | | -------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------- | | `quote_mint` | string | SOL | LaunchLab quote: WSOL, `USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB` for USD1, or any admitted mint (USDC, xStocks) | | `bonkers_mode` | boolean | `false` | letsbonk.fun bonkers config | | `locked_mode` | boolean | `false` | Locked-liquidity config | `mode: "usd1"` is a legacy alias for `bonk` + the USD1 quote. Supports `bundle_wallets`, `sniper_wallets`, `auto_sell`, `multi_deploy`. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "bonk", "name": "Bonk Coin", "ticker": "BONKC", "quote_mint": "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB", "buy_amount": 0.5, "image_url": "https://example.com/logo.png" } ``` | Field | Type | Default | Notes | | ------------------------ | ------- | ---------- | -------------------------------------------------------------------------------------------------------------- | | `stonk_quote_mint` | string | WSOL | Pair mint from [`/token/stonk-pairs`](/docs/pair-registries) — any LaunchLab-admitted mint. Alias `quote_mint` | | `stonk_mode` | string | `standard` | `standard` = fee coin · `reward` = Token-2022 dividend coin | | `stonk_transfer_fee_bps` | integer | `300` | Reward coin holder tax: `100` (1%) or `300` (3%) | `bundle_wallets` up to **9**, `auto_sell` supported. No `sniper_wallets`, no `multi_deploy`, no `locked_mode` / `bonkers_mode`. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "stonk", "name": "Apple Yield", "ticker": "APLY", "stonk_quote_mint": "XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp", "stonk_mode": "reward", "stonk_transfer_fee_bps": 100, "buy_amount": 0.25, "image_url": "https://example.com/logo.png" } ``` Buy and sell with `mode: "stonk"` (or `mode` omitted). Either way the server reads the pool on-chain for the platform config, creator and quote, so `creator_wallet` / `quote_mint` are optional. | Field | Type | Notes | | -------------------- | ------ | ---------------------------------------------------------------------------- | | `bags_creator_share` | number | Creator share in bps | | `bags_fee_claimers` | array | `{ "username", "bps", "provider"? }` — creator + claimers must total `10000` | | `bags_config_type` | string | Optional preset id | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "bags", "name": "My Bags Token", "ticker": "BAGS", "buy_amount": 1, "bags_creator_share": 2000, "bags_fee_claimers": [{ "username": "somehandle", "bps": 8000, "provider": "twitter" }], "image_url": "https://example.com/logo.png" } ``` Ansem.io creates the pump.fun coin and runs a community airdrop; your wallet signs the funding transfer. **NA East only**, allow \~90 s. | Field | Type | Notes | | ----------------- | ------ | ------------------------------------------------------------------------------- | | `airdrop_buy_sol` | number | SOL funding the airdrop, clamped to **0.8738–20** | | `buy_amount` | number | Fired as a zero-delay snipe from the dev wallet (there is no create-tx dev buy) | | `sniper_wallets` | string | Snipes fire on shred. `bundle_wallets` are treated as zero-delay snipers | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "ansem", "name": "Ansem Coin", "ticker": "ANSM", "airdrop_buy_sol": 1, "buy_amount": 0.5, "image_url": "https://example.com/logo.png" } ``` BNB Chain. Name and ticker max **20** characters. | Field | Type | Default | Notes | | ----------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pair` | string | `bnb` | Raised token: symbol or address from [`/token/fourmeme-pairs`](/docs/pair-registries) (`USDT`, `NVDAB`, `BNC4`, …) | | `token_tax` | object | — | `{ fee_rate: 1\|3\|5\|10, burn_rate, divide_rate, liquidity_rate, recipient_rate, min_sharing, recipient_address }` — the four rates total `100`. Not on 4Stock pairs | | `x_mode` | boolean | `false` | Launch-then-buy MPC flow. BNB pair only | | `rush_mode` | boolean | `false` | Rush window. Off on 4Stock pairs | | `fee_plan` | boolean | `false` | four.meme fee plan flag | No bundles, snipers, auto-sell or multi-deploy. Gas is pinned by the server. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "bnb", "name": "My Token", "ticker": "MTK", "pair": "NVDAB", "buy_amount": 0.1, "image_url": "https://example.com/logo.png" } ``` genius.fun on BNB Chain — bonding curve → PancakeSwap Infinity. Name max **64**, ticker max **16**. | Field | Type | Default | Notes | | --------------- | ------- | -------------- | -------------------------------------------------------------------------------------------------- | | `pair` | string | BNB | `usdt`, `usdc`, a bStock symbol or any address from [`/token/genius-pairs`](/docs/pair-registries) | | `fee_recipient` | string | deployer | Creator-fee wallet | | `slippage_bps` | integer | server default | Dev-buy slippage | No bundles. Response is sent at broadcast; the receipt confirms in the background. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "genius", "name": "Genius Coin", "ticker": "GENI", "pair": "usdt", "buy_amount": 0.1, "image_url": "https://example.com/logo.png" } ``` flap.sh on BNB Chain (default) or Robinhood Chain. Every non-PVE coin is a tax token — omit the tax and it defaults to **2% / 2%**. | Field | Type | Default | Notes | | ----------------------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `chain` | string | `bnb` | `bnb` or `robinhood`. Always send it | | `pair` | string | native | Quote token: key, symbol or address from [`/token/flap-pairs`](/docs/pair-registries). Robinhood stock pairs only with `buy_amount: 0` | | `pve` | boolean | `false` | No-tax launch (1.5× anti-sniper curve on BNB). Native pair only | | `buy_tax_rate`, `sell_tax_rate` | number | `200` | bps, max `1000`. Send both explicitly to use any split | | `dividend_bps`, `deflation_bps`, `lp_bps` | number | `0` | Share of the tax to holders / burn / LP (bps of the tax, total ≤ `10000`). Remainder → creator | | `dividend_token` | string | `quote` | `quote`, `self`, or an ERC-20 address | | `minimum_share_balance` | number | `10000` | Min tokens to earn dividends | | `fee_recipient` | string | deployer | Creator / marketing share wallet | | `bundle_buys` | array | — | Max **8** atomic legs. BNB Chain only | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "flap", "chain": "bnb", "name": "SP500 Printer", "ticker": "PRINT", "pair": "spyb", "buy_amount": 0.5, "buy_tax_rate": 200, "sell_tax_rate": 200, "dividend_bps": 10000, "image_url": "https://example.com/logo.png" } ``` pons.fun on Robinhood Chain. Launch, dev buy and bundle legs are one transaction; `buy_amount` is ETH and is auto-swapped into the pair. | Field | Type | Default | Notes | | ---------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | | `pair` | string | `eth` | `usdg`, `cbbtc`, a stock key (`nvda`, `aapl`, …) or any approved address from [`/token/pons-pairs`](/docs/pair-registries) | | `creator_tax_bps` | integer | `0` | Creator tax per trade, max `1000` | | `holder_dividends` | boolean | `false` | Route the creator tax to holders, paid in the pair token | | `buyback_enabled` | boolean | `false` | Part of the tax buys the coin back into a 5-year vest | | `fee_recipient` | string | deployer | Creator-tax wallet. Not with `holder_dividends` | | `snipe_tax_exemptions` | string\[] | `[]` | Up to 32 wallets exempt from the launch snipe tax | | `bundle_buys` | array | `[]` | Max **10** atomic legs, every pair | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "pons", "name": "Nvidia Yield", "ticker": "NVY", "pair": "nvda", "buy_amount": 0.05, "creator_tax_bps": 200, "holder_dividends": true, "image_url": "https://example.com/logo.png" } ``` `buy_token` and `sell_token` work with `mode: "pons"` and the EVM key — amounts in ETH. long.xyz (Doppler) on Robinhood Chain — a stock-paired launch prepared by long.xyz's API and sent from your wallet. Name max **64**, ticker **1–15 letters**. | Field | Type | Notes | | ------------ | ------ | ------------------------------------------------------------------- | | `pair` | string | **Required.** Contract address of the stock the launch is priced in | | `buy_amount` | number | Must be `0` — there is no dev buy on Long | No bundles. long.xyz's snipe shield (fee decay over the first 10 s) is always on. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "long", "name": "Nvidia Long", "ticker": "NVLONG", "pair": "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC", "buy_amount": 0, "image_url": "https://example.com/logo.png" } ``` pools.trade on Robinhood Chain — an instant Uniswap v4 pool, no bonding curve. `buy_amount` in ETH. | Field | Type | Default | Notes | | ------------- | ------ | -------- | ----------------------------------------------------------------------------- | | `bundle_buys` | array | — | Max **10** atomic legs: number (ETH, to deployer) or `{ "amount", "wallet" }` | | `fee_wallet` | string | deployer | LP-fee beneficiary | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "poolstrade", "name": "Pool Coin", "ticker": "POOL", "buy_amount": 0.05, "bundle_buys": [0.02, { "amount": 0.03, "wallet": "0xAAA0000000000000000000000000000000000001" }] } ``` Bags on Robinhood Chain. `buy_amount` in ETH. | Field | Type | Default | Notes | | -------------- | --------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `recipients` | array \| string | creator 100% | `[{ "wallet": "0x…", "bps" }]` and/or `[{ "username", "bps", "provider"? }]`, or CSV `"0xA:5000,0xB:5000"`. Creator gets the remainder to `10000` | | `partner` | string | — | Partner-attribution address | | `metadata_uri` | string | auto | Pre-uploaded metadata JSON | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "robinhood", "name": "Hood Bags", "ticker": "HBAG", "buy_amount": 0.02, "recipients": [{ "username": "somehandle", "bps": 5000, "provider": "twitter" }] } ``` o1 Launchpad — locked Uniswap v4 liquidity on Base (default), Robinhood Chain or BNB Chain. Name max **50**, ticker max **11**. | Field | Type | Default | Notes | | -------------- | ------- | -------------- | ---------------------------------------------------------------------------------------- | | `chain` | string | `base` | `base`, `robinhood` or `bnb` | | `pair` | string | native | USDC, a crypto major or a stock token — key or address from the factory's quote registry | | `slippage_bps` | integer | server default | Dev-buy slippage | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "o1", "chain": "base", "name": "o1 Coin", "ticker": "O1C", "buy_amount": 0.01, "image_url": "https://example.com/logo.png" } ``` Klik Finance on Ethereum (default) or Robinhood Chain. `buy_amount` in ETH. | Field | Type | Default | Notes | | -------------- | ------ | -------- | -------------------------------------------------------------------------- | | `config_id` | number | `1` | Virtual-LP tier `0–4` = 0.69 / 1 / 2 / 5 / 10 ETH. Alias `tier` | | `chain` | string | Ethereum | `robinhood` for Robinhood Chain | | `metadata_uri` | string | auto | Pre-uploaded metadata; `inline_metadata: true` bakes a `data:` URI instead | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "klik", "name": "Klik Coin", "ticker": "KLIK", "buy_amount": 0.01, "config_id": 1 } ``` stroid.fun LaunchpadV3 on Ethereum mainnet. Waits for the receipt — allow **\~45 s**. `buy_amount` in ETH. | Field | Type | Default | Notes | | ----------------- | --------------- | ------------ | ----------------------------------------------------------------------------------------- | | `recipients` | array \| string | creator 100% | `[{ "wallet", "bps" }]`, max 5, total exactly `10000`. CSV `"0xA:5000,0xB:5000"` accepted | | `partner` | string | none | Partner-attribution wallet (only earns if registered by an admin) | | `sqrt_price_tier` | integer | `1` | Virtual-LP tier; the index is the ETH label (`5` ≈ 5 ETH). Alias `tier` | | `gas_gwei` | number | dynamic | Flat gas override | | `metadata_uri` | string | auto | Pre-uploaded metadata; `inline_metadata: true` bakes a `data:` URI | No bundles or snipers. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "stroid", "name": "My Stroid Token", "ticker": "STRO", "buy_amount": 0.05, "recipients": [ { "wallet": "0xAAA0000000000000000000000000000000000001", "bps": 5000 }, { "wallet": "0xBBB0000000000000000000000000000000000002", "bps": 5000 } ] } ``` Sell with `mode: "stroid"`, `mint_address` and `sell_percent`. Base. Takes the common fields only; `buy_amount` in ETH. Name and ticker max **20**. ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "clanker", "name": "Clanker Coin", "ticker": "CLNK", "buy_amount": 0.01, "image_url": "https://example.com/logo.png" } ``` Arc. `buy_amount` is in **USDC**. Name **2–32**, ticker **2–10**. | Field | Type | Default | Notes | | ------------------------------------------------------ | ------- | ------------ | ----------------------------------------------------------------------------------------- | | `pair` | string | `usdc` | `usdc`, `weth` or an approved address from [`/token/argus-quotes`](/docs/pair-registries) | | `buy_tax_bps`, `sell_tax_bps` | integer | `100` | `100–1000` | | `creator_bps`, `buyback_bps`, `dividend_bps`, `lp_bps` | integer | creator 100% | Tax split, total `10000`; the remainder lands on creator | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "argus", "name": "Argus Coin", "ticker": "ARG", "buy_amount": 25, "buy_tax_bps": 300, "sell_tax_bps": 300, "dividend_bps": 10000, "image_url": "https://example.com/logo.png" } ``` Arc. USDC bonding curve → Uniswap v4. `buy_amount` is in **USDC**. Name **2–32**, ticker **2–10**. | Field | Type | Default | Notes | | ----------------- | ------- | ------- | --------------------- | | `creator_tax_bps` | integer | `0` | Creator tax per trade | ```json { "type": "create_token", "external": true, "session_id": "", "api_key": "", "mode": "foci", "name": "Foci Coin", "ticker": "FOCI", "buy_amount": 25, "creator_tax_bps": 200, "image_url": "https://example.com/logo.png" } ``` # Quick Start (/docs/quick-start) ```bash curl -X POST https://tx-nyc.j7tracker.io/deploy/submit \ -H "Authorization: Bearer " \ -H "Content-Type: text/plain;charset=UTF-8" \ -d '{ "type": "create_token", "external": true, "mode": "pump", "api_key": "", "name": "My Token", "ticker": "MTK", "buy_amount": 0.5, "image_url": "https://example.com/image.png" }' ``` ```js const res = await fetch("https://tx-nyc.j7tracker.io/deploy/submit", { method: "POST", headers: { "Content-Type": "text/plain;charset=UTF-8" }, body: JSON.stringify({ type: "create_token", external: true, session_id: "", mode: "pump", api_key: "", name: "My Token", ticker: "MTK", buy_amount: 0.5, image_url: "https://example.com/image.png", }), }); const data = await res.json(); if (data.type === "token_create_success") console.log(data.mint_address); else console.error(data.error); ``` ```python import json, requests r = requests.post( "https://tx-nyc.j7tracker.io/deploy/submit", headers={"Content-Type": "text/plain;charset=UTF-8"}, data=json.dumps({ "type": "create_token", "external": True, "session_id": "", "mode": "pump", "api_key": "", "name": "My Token", "ticker": "MTK", "buy_amount": 0.5, "image_url": "https://example.com/image.png", }), timeout=35, ) data = r.json() print(data.get("mint_address") or data.get("error")) ``` `external: true` is required on every API deploy. `buy_amount` is always in the chain's native coin (SOL / BNB / ETH / USDC on Arc), even on a stock-paired launch — the server swaps into the pair for you. Bundle deploy [#bundle-deploy] ```json { "type": "create_token", "external": true, "session_id": "", "mode": "pump", "api_key": "", "name": "My Token", "ticker": "MTK", "buy_amount": 0.5, "bundle": true, "bundle_wallets": ":0.3,:0.2", "image_url": "https://example.com/image.png" } ``` Solana platforms take `bundle_wallets` (`encrypted_key:sol`); EVM platforms take `bundle_buys` in the same transaction. See [Bundles & snipers](/docs/bundles). # Sell API (/docs/sell) **`POST {base}/submit`** with **`"type": "sell_token"`**. Sells are a **percentage of the wallet's balance**. The signer is always the wallet in `api_key`. Required fields [#required-fields] | Field | Type | Notes | | -------------- | ------- | ------------------------------------------------------------------------------ | | `type` | string | `sell_token` | | `session_id` | string | JWT, or `Authorization: Bearer` | | `api_key` | string | Encrypted key — Solana, or EVM for `stroid` / `pons` | | `mint_address` | string | Token mint, or `0x…` contract on EVM. Alias `token_address` | | `basis_points` | integer | `1–10000` = 0.01%–100% (`2500` = 25%). EVM also accepts `sell_percent` `1–100` | Optional fields [#optional-fields] | Field | Type | Notes | | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `mode` | string | Omit or `auto` (default): the server detects the launchpad, creator and quote on-chain. Explicit `pump`, `bonk`, `ray`, `bags`, `stonk` skip that read; `stroid`, `pons` for EVM | | `quote_mint` | string | Only with an explicit `mode` on a quoted coin (USDC / stock `pump`, USD1 / stock `bonk`, StonkFun pair): the quote CA the deploy used (`quote_token: "usdc"` is the legacy label). Auto mode reads it off the curve | | `creator_wallet` | string | Only with an explicit `mode`: the creator pubkey. Resolved on-chain when absent | | `config_key` | string | Only with `mode: "bags"`: the DBC config key from the deploy | | `mayhem_mode` | boolean | Mayhem path for `pump` | | `priority_fee_sol`, `bribe_fee_sol` | number | Priority fee / relay tip in SOL | | `gas_gwei` | number | `stroid` only — flat gas override | ```json { "type": "sell_token", "session_id": "", "api_key": "", "mint_address": "MintAddressHere", "basis_points": 10000 } ``` Success response [#success-response] ```json { "type": "sell_success", "signature": "...", "mint_address": "...", "basis_points": 10000, "wallet_address": "...", "mode": "pump" } ``` EVM sells add `tx_hash`, `amount_sold`, `eth_received_net`, `gas_paid_eth`. Stock-quoted sells sweep the received stock back to SOL / ETH in the same flow. Batch sell (several wallets, one request) [#batch-sell-several-wallets-one-request] **`"type": "batch_sell_token"`** sells the same mint from up to **16 wallets** in one call. Solana only. Each wallet gets its own independent transaction (the exact transaction `sell_token` would build for it), and all of them ship in a single Astralane `sendBatch` (MEV-protected, HelloMoon fallback), so every wallet lands in the same slot. Legs are not atomic — one wallet with no balance never fails the others. `sells: [{ api_key, basis_points, token_account? }]` replaces `api_key` / `basis_points`; every other field above (`mode`, `quote_mint`, `creator_wallet`, `config_key`, `mayhem_mode`, `priority_fee_sol`, `bribe_fee_sol`) applies to the whole batch. ```json { "type": "batch_sell_token", "session_id": "", "mint_address": "MintAddressHere", "sells": [ { "api_key": "", "basis_points": 10000 }, { "api_key": "", "basis_points": 5000 } ] } ``` ```json { "type": "batch_sell_success", "mint_address": "...", "mode": "pump", "provider": "Astralane", "results": [ { "index": 1, "wallet": "...", "basis_points": 10000, "signature": "..." }, { "index": 2, "wallet": "...", "basis_points": 5000, "signature": "..." } ], "landed": 2, "failed": 0 } ``` Signatures are known at accept time (deterministic per signed transaction); a leg can still fail on-chain. When the request is refused as a whole (a bad key, an unbuildable leg, both providers down) the answer is one `batch_sell_error` with `error` and the `wallets` it was for. Common errors [#common-errors] | Error | Cause | | ------------------------------- | ---------------------------------------------------------------------------- | | `sell_error` — `Unauthorized` | Bad or wrong-chain `api_key` | | `sell_error` — unsupported mode | `bnb` and `clanker` sells are not available through `/submit` | | **504** | No final answer within 30 s; `last_status` carries the last progress message |