openapi: 3.2.0 info: title: MadeOnSol Tokens API version: 1.20.0 description: Real-time Solana & Robinhood Chain trading intelligence — KOL wallet trades, deployer reputation, token buyer-quality scoring, wallet tracking, and copy-trade signals across both chains (Robinhood Chain paths under `/rhc/*`). contact: name: MadeOnSol url: https://madeonsol.com/contact license: name: Proprietary url: https://madeonsol.com/terms servers: - url: https://madeonsol.com/api/v1 description: Production security: - BearerAuth: [] tags: - name: Tokens description: Token-level buyer-quality, cap-table, and filtered token directory. paths: /token/{mint}: get: tags: - Tokens summary: Comprehensive per-mint snapshot description: Price (VWAP from our DEX firehose), market cap, 24h volume + trade count, `liquidity_to_mc_ratio` (thinness gauge), `launchpad` of origin (`pumpfun`/`launchlab`/`bags`, null when unknown), `launch_cohort_sol` + `launch_cohort_size` (total SOL the first-20 buyers deployed — a heavily-bought open leans dump-prone), `name`/`symbol`, `image_url` + `socials` {website, twitter, telegram, discord} + `description` (from the token URI JSON, resolved within minutes of first trade; null until resolved), `twitter_reuse` {handle, mints_same_handle, mints_same_url} (how many OTHER mints reuse this X handle / exact URL — null = unknown, not zero), `early_buyer_exit` {cohort, still_holding, sold, still_holding_pct} (first-20 buyers still holding vs sold, from confirmed swaps), deployer reputation, KOL smart-money activity, `first_seen_at` + `age_seconds`, and `is_blacklisted` + `blacklist_category` — all in one call. **ULTRA** adds wallet addresses inside `kol_activity.top_buyers[]`. Available on every tier (a free taster of the snapshot data); the filterable `/tokens` directory and the `/tokens/{mint}/*` analytics are PRO/ULTRA. parameters: - name: include in: query schema: type: string description: 'Comma-separated: buyer_quality (embeds /tokens/{mint}/buyer-quality) and/or deployer (embeds /deployer-hunter/{deployer} as deployer_profile). Runs the sibling handler in-process and embeds its body under its own key; a failing include lands in include_errors and never fails the request; counts as ONE call. Unknown names are reported under include_errors.' - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana base58 mint (32-44 chars). Tickers like `BTC`, `ETH`, `SOL` are rejected — use the wrapped-SOL mint `So11111111111111111111111111111111111111112` for SOL. responses: '200': description: OK content: application/json: schema: type: object properties: token: type: object properties: mint: type: string name: type: string nullable: true symbol: type: string nullable: true image_url: type: string nullable: true description: Off-chain metadata resolved from the token URI JSON; null until resolved socials: type: object properties: website: type: string nullable: true twitter: type: string nullable: true telegram: type: string nullable: true discord: type: string nullable: true twitter_reuse: type: object nullable: true description: null = unknown (no twitter / socials unresolved), not zero properties: handle: type: string nullable: true mints_same_handle: type: integer mints_same_url: type: integer description: type: string nullable: true price_usd: type: number nullable: true price_sol: type: number nullable: true market_cap: type: number nullable: true vwap_price_usd: type: number nullable: true vwap_price_sol: type: number nullable: true fdv_usd: type: number nullable: true liquidity_usd: type: number nullable: true liquidity_to_mc_ratio: type: number nullable: true primary_dex: type: string nullable: true launchpad: type: string nullable: true enum: - pumpfun - launchlab - bags - null primary_pool_address: type: string nullable: true is_token_2022: type: boolean nullable: true description: null = no mc-tracker observation (unknown), never false transfer_fee_bps: type: integer nullable: true description: null = unknown (no observation) — NOT "no fee" mint_authority_revoked: type: boolean nullable: true freeze_authority_revoked: type: boolean nullable: true burn_detected: type: boolean nullable: true description: 'DEPRECATED meaning: `true` when the token MINT''s on-chain supply decreased (a token burn, measured by mc-tracker''s supply-drift monitor). It is NOT LP-burn evidence and never means liquidity cannot be removed. null = no mc-tracker observation (unknown).' token_supply_burn_detected: type: boolean nullable: true description: The mint's supply decreased (token burn). Not LP evidence. lp_burn_status: type: string enum: - verified - not_verified - unknown description: 'LP burn/lock evidence: `verified` only with measured LP-token custody (lp_burnt_pct ≥ 99). No Solana writer populates LP custody yet, so today every token is `unknown`. A token-supply burn is never promoted to an LP claim.' volume_24h_usd: type: number nullable: true volume_24h_sol: type: number nullable: true trades_24h: type: integer nullable: true price_source: type: string nullable: true enum: - mc_tracker - dex_stream - null description: Which pipeline produced price_usd/price_sol; null when there is no price price_observed_at: type: string format: date-time nullable: true description: Last trade observed BY THE SELECTED price source — the price's own age anchor price_age_seconds: type: integer nullable: true description: Seconds since price_observed_at (null when the source carries no timestamp) price_is_stale: type: boolean nullable: true description: true when price_age_seconds > 900; null (unknown) when a price exists but its source has no observation time — never false without an observation; false when there is no price last_trade_at: type: string format: date-time nullable: true description: Newest trade observed by ANY source (mc-tracker or dex-stream). Not a price-age anchor — use price_observed_at first_seen_at: type: string format: date-time nullable: true age_seconds: type: integer nullable: true is_blacklisted: type: boolean nullable: true description: null = the blacklist lookup failed (unknown) — never "not blacklisted" blacklist_category: type: string nullable: true launch_cohort_sol: type: number nullable: true description: Total SOL the first-20 buyers deployed on their first buy launch_cohort_size: type: integer nullable: true description: null = the cohort lookup failed; 0 = no cohort rows early_buyer_exit: type: object nullable: true description: First-20 buyer cohort exit status from confirmed swaps; null when the cohort is unknown properties: cohort: type: integer still_holding: type: integer sold: type: integer still_holding_pct: type: number deployer: type: object nullable: true description: Creator + live reputation. Resolved from `deployer_tokens` (bonded) or `pending_deploys` (not yet bonded — ~98% of fresh launches). null ⇒ read `deployer_identity` for why (unknown creator vs failed lookup). `history_status` other than `established` means the bonding rate is not a track record yet (a first launch is "0 of 1", not a serial dumper). properties: wallet: type: string address: type: string description: Same value as wallet (contract name) tier: type: string bonding_rate: type: number total_deployed: type: integer total_bonded: type: integer recent_bond_rate: type: number identity_status: type: string enum: - resolved identity_source: type: string enum: - deployer_tokens - pending_deploys history_status: type: string enum: - new_in_our_index - limited_history - reputation_pending - established first_seen_at: type: string format: date-time nullable: true description: First deploy OBSERVED in our capture — not the wallet's first launch anywhere observed_launch_count: type: integer resolved_outcome_count: type: integer description: Resolved outcomes (bonds + dumps) we hold for this creator stats_computed_at: type: string format: date-time nullable: true deployer_identity: type: object description: 'Always present. Explains a null `deployer`: `unknown` = both lookups succeeded and found no creator; `lookup_failed` = a lookup errored (retry — NOT "no deployer").' properties: identity_status: type: string enum: - resolved - unknown - lookup_failed history_status: type: string nullable: true enum: - new_in_our_index - limited_history - reputation_pending - established - null address: type: string nullable: true source: type: string nullable: true enum: - deployer_tokens - pending_deploys - null kol_activity: type: object description: 'Complete 7-day aggregate over every tracked-KOL trade (SQL, not a row sample). `status: unavailable` = the aggregate read failed: every figure is null, never zero/neutral.' properties: status: type: string enum: - ok - unavailable buying_kols: type: integer nullable: true description: Distinct KOL wallets with ≥1 buy in the window (exact) selling_kols: type: integer nullable: true net_flow_sol: type: number nullable: true signal: type: string nullable: true enum: - accumulating - distributing - neutral - null top_buyers: type: array description: Largest KOL buyers by SOL bought over the whole window (≤ 10, complete ranking) items: type: object properties: name: type: string wallet: type: string description: ULTRA only sol_amount: type: number window_hours: type: integer window_start: type: string format: date-time nullable: true computed_at: type: string format: date-time nullable: true last_trade_at: type: string format: date-time nullable: true unique_kols: type: integer nullable: true unique_wallets: type: integer nullable: true buys: type: integer nullable: true sells: type: integer nullable: true buy_sol: type: number nullable: true sell_sol: type: number nullable: true counts_basis: type: string nullable: true enum: - complete_window - null data_status: type: object description: Per-block read status. `unavailable` = that lookup failed; its fields are null (unknown), never a neutral default like 0 KOLs or not-blacklisted. properties: market: type: string enum: - ok - unavailable deployer: type: string enum: - ok - unavailable kol_activity: type: string enum: - ok - unavailable first_seen: type: string enum: - ok - unavailable blacklist: type: string enum: - ok - unavailable metadata: type: string enum: - ok - unavailable launch_cohort: type: string enum: - ok - unavailable sniper_footprint: type: string enum: - ok - unavailable volume_candles: type: string enum: - ok - unavailable twitter_reuse: type: string enum: - ok - unavailable mc_change_pct: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number nullable: true volume_usd: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number mev_volume_pct: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number nullable: true history_age_seconds: type: integer nullable: true description: Present only when history exists as_of: type: string format: date-time description: Snapshot assembly time — NOT the observation time of any field (see price_observed_at, kol_activity.computed_at, deployer.stats_computed_at) buyer_quality: type: object description: Present when include=buyer_quality — see /tokens/{mint}/buyer-quality deployer_profile: type: object description: Present when include=deployer — see /deployer-hunter/{wallet} include_errors: type: object nullable: true description: 'Per-key errors for requested includes that failed. include=deployer: 404 when the token has no known creator, 503 (retryable) when the creator lookup failed (deployer_identity.identity_status = lookup_failed)' included: type: array items: type: string nullable: true headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: 'Invalid mint. Body includes `code: invalid_mint`, `reason` (specific cause), `received`, `received_length`, `expected_length`, `example`, and `docs` URL.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: getTokenByMint x-operation-id-source: derived /tokens: get: tags: - Tokens summary: Filtered token directory (PRO+) description: 'Browse tokens by market-cap band, liquidity floor, recent-activity window, primary DEX, authority/safety flags, computed 1h volume, MEV-share ceiling, and MC change. Momentum sorts surface movers (`mc_change_5m_desc`, `mc_change_1h_desc`, `volume_1h_desc`) and a composite `trending` board. Complements `/token/{mint}` (single lookup), `/tokens/almost-bonded` (pre-bond pump.fun), and the KOL-scoped `/kol/tokens/{hot,trending}`. **Default `min_liq` is 2000 USD** to filter the long tail of phantom-MC tokens from low-liquidity pools. Pass `min_liq=0` to opt out. **Default `max_mc` is 100,000,000,000 USD ($100B)** — a sanity ceiling that drops phantom market caps (a genuine ~1e12 token supply trading near $1, or a thin-pool price blow-up). No real tracked memecoin approaches it. Pass a higher explicit `max_mc` to opt out. **Momentum sorts + filters are DB-native** (via the `token_prices_trending` view): `min_volume_1h_usd`, `max_mev_share_pct`, `min_liq_mc_ratio`, `max_liq_mc_ratio`, `mc_change_1h_min_pct`, `mc_change_1h_max_pct` and the momentum sorts paginate correctly with no over-fetch. The `trending` sort = recent-volume × positive-momentum composite, recency-bounded to the last 1h, and echoes `filters.trending_version: "v1"`. **Only `deployer_tier` and `launchpad`** remain post-filters (joined from `deployer_tokens`, not the view). They are served by a bounded SCAN: candidates are walked in pages until `limit` matches, the candidates run out, or the scan budget is spent. `has_more:false` only when the candidates ran out; `pagination.scan_truncated:true` = the budget ran out first and more matches MAY exist — continue with `offset = pagination.next_offset` (a RAW candidate offset, not a match count). Offset pages walk a LIVE order (`pagination.order_is_live: true`): MC and activity move between calls, so rows can shift across pages — treat the screener as a ranking, not a ledger. **LP burn (2026-09-21):** `lp_burned` / `lp_burn_status` come from LP-custody evidence only (`lp_burnt_pct`). No Solana writer populates it yet, so `lp_burn_status` is `unknown` and `lp_burned` null for every token, and `lp_burned=true|false` currently matches nothing. `lp_burned` is DEPRECATED: `true` = verified burned/locked, `false` = verified NOT burned, and a token with unknown LP custody matches neither (false never turns unknown into "unburned"); a request that sends it gets a `deprecations` entry. The old `lp_burned` flag was the token-SUPPLY burn — filter that with `supply_burn` and read it as `token_supply_burn_detected`.' parameters: - name: min_mc in: query schema: type: number minimum: 0 description: Minimum market_cap_usd - name: max_mc in: query schema: type: number minimum: 0 default: 100000000000 description: Maximum market_cap_usd. Default 100000000000 ($100B) — pass a higher value to surface phantom thin-pool caps. - name: min_liq in: query schema: type: number minimum: 0 default: 2000 description: Minimum liquidity_usd. Default 2000 — set 0 to disable. - name: active_h in: query schema: type: number minimum: 0 maximum: 8760 default: 168 description: Only tokens with a trade within the last N hours. Default 168 (7 d) — a screener ranks what is tradeable; 0 = no freshness bound (not applied to sort=trending, which has its own 1 h window) - name: primary_dex in: query schema: type: string enum: - pumpfun - pumpswap - raydium - meteora - orca - raydium_clmm - launchlab description: Filter by primary trading venue (`launchlab` = on-curve bonk/LetsBonk tokens) - name: deployer_tier in: query schema: type: string enum: - elite - good - moderate - rising - cold - unranked description: Filter to pump.fun tokens whose deployer carries this reputation tier (post-filter; non-pump tokens have a null tier and are dropped) - name: launchpad in: query schema: type: string enum: - pumpfun - launchlab - bags description: 'Filter by launchpad of origin (post-filter, from `deployer_tokens.launchpad`). Distinct from `primary_dex`: bags tokens trade on meteora, so only this tag identifies them.' - name: authority_revoked in: query schema: type: boolean description: true = require both mint + freeze authority revoked; false = at least one not revoked - name: exclude_token2022 in: query schema: type: boolean default: false description: Drop Token-2022 mints (commonly used for transfer-fee scams) - name: lp_burned in: query deprecated: true schema: type: boolean description: 'DEPRECATED — use `lp_burn_status` on each token, or `supply_burn` for the token-supply burn flag. Filter on VERIFIED LP-burn evidence only: `true` = lp_burnt_pct ≥ 99; `false` = lp_burnt_pct measured AND below it. A token with unknown LP custody matches NEITHER value (false never means "unknown"). Sending it adds a `deprecations` entry to the response. No writer populates LP evidence yet, so this matches no token today — it never matches a token-supply burn (use `supply_burn`). See `filters.lp_burned_source`.' - name: supply_burn in: query schema: type: boolean description: Filter on the token-SUPPLY burn flag (the mint's on-chain supply decreased). This is what `lp_burned` used to filter on; it is not LP evidence. - name: min_volume_1h_usd in: query schema: type: number minimum: 0 description: Computed organic-volume floor in the last 1 hour (DB-native; tokens with insufficient history are dropped) - name: max_mev_share_pct in: query schema: type: number minimum: 0 maximum: 100 description: Computed MEV/bot volume ceiling as a % of total cumulative volume (DB-native) - name: min_liq_mc_ratio in: query schema: type: number minimum: 0 default: 2.0e-05 description: Minimum liquidity_to_mc_ratio (liquidity ÷ market cap). Default 0.00002 (= MC at most 50,000× pool liquidity) — a plausibility gate against price × nominal-supply blowups off dust pools; pass 0 to opt out. Low values = thin liquidity relative to MC (DB-native) - name: max_liq_mc_ratio in: query schema: type: number minimum: 0 description: Maximum liquidity_to_mc_ratio — drops over-collateralised thin-MC tokens (DB-native) - name: mc_change_1h_min_pct in: query schema: type: number description: Minimum 1h MC change % (DB-native; use for momentum scans) - name: mc_change_1h_max_pct in: query schema: type: number description: Maximum 1h MC change % (DB-native; use for dip scans) - name: sort in: query schema: type: string enum: - mc_desc - mc_asc - last_trade_desc - liquidity_desc - cumulative_volume_desc - mc_change_5m_desc - mc_change_1h_desc - volume_1h_desc - trending default: mc_desc description: '`trending` = recent-volume × positive-momentum composite, recency-bounded to the last 1h; echoes `filters.trending_version: "v1"`.' - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 - name: offset in: query schema: type: integer minimum: 0 maximum: 10000 default: 0 responses: '200': description: 'OK. Returns `tokens[]` (with `liquidity_to_mc_ratio`, `deployer_tier`, `launchpad` (`pumpfun`/`launchlab`/`bags`, null when unknown), `mev_share_pct`, `organic_volume_1h_usd`, `mc_change_5m_pct`, `mc_change_1h_pct`, `is_token_2022`, `authorities_revoked`, etc.), `pagination` (with `post_filtered` flag), and a `filters` echo (with `min_liq_default_applied`; `trending_version: "v1"` when `sort=trending`).' content: application/json: schema: type: object properties: tokens: type: array items: type: object properties: mint: type: string symbol: type: string nullable: true name: type: string nullable: true price_usd: type: number nullable: true market_cap_usd: type: number nullable: true fdv_usd: type: number nullable: true liquidity_usd: type: number nullable: true liquidity_to_mc_ratio: type: number nullable: true primary_dex: type: string nullable: true launchpad: type: string nullable: true enum: - pumpfun - launchlab - bags - null deployer_tier: type: string nullable: true authorities_revoked: type: boolean lp_burned: type: boolean nullable: true description: 'From LP evidence ONLY: true iff lp_burn_status = verified, false iff not_verified, null = unknown (every token today). Never derived from a token-supply burn.' lp_burn_status: type: string enum: - verified - not_verified - unknown description: 'LP burn/lock evidence: `verified` only with measured LP-token custody (lp_burnt_pct ≥ 99). No Solana writer populates LP custody yet, so today every token is `unknown`. A token-supply burn is never promoted to an LP claim.' lp_burnt_pct: type: number nullable: true description: Measured share of LP tokens burned (0–100); null = not measured token_supply_burn_detected: type: boolean nullable: true description: The mint's on-chain supply decreased (token burn) — the flag lp_burned used to expose. Not LP evidence. is_token_2022: type: boolean last_trade_time: type: string format: date-time nullable: true mc_change_5m_pct: type: number nullable: true mc_change_1h_pct: type: number nullable: true organic_volume_1h_usd: type: number nullable: true mev_share_pct: type: number nullable: true pagination: type: object properties: limit: type: integer offset: type: integer returned: type: integer has_more: type: boolean description: false ONLY when the candidates ran out — never inferred from a short post-filtered page next_offset: type: integer nullable: true description: Raw candidate offset to resume from (pass as offset); null when exhausted order_is_live: type: boolean enum: - true description: Offset pages walk a live order (MC/activity move between calls), not a snapshot post_filtered: type: boolean scanned: type: integer description: Candidate rows examined scan_truncated: type: boolean description: The per-request scan budget ran out before `limit` matches — more matches MAY exist past the cursor/offset scan_budget: type: integer scanned_until_offset: type: integer description: offset + scanned (post-filtered pages) filters: type: object description: Echo of applied filters, including defaults properties: min_mc: type: number nullable: true max_mc: type: number max_mc_default_applied: type: boolean min_liq: type: number min_liq_default_applied: type: boolean active_h: type: number nullable: true primary_dex: type: string nullable: true launchpad: type: string nullable: true deployer_tier: type: string nullable: true authority_revoked: type: boolean nullable: true exclude_token2022: type: boolean nullable: true lp_burned: type: boolean nullable: true lp_burned_source: type: string description: What lp_burned=true and lp_burned=false each filter on (verified LP evidence only; unknown matches neither) supply_burn: type: boolean nullable: true min_volume_1h_usd: type: number nullable: true max_mev_share_pct: type: number nullable: true min_liq_mc_ratio: type: number nullable: true max_liq_mc_ratio: type: number nullable: true mc_change_1h_min_pct: type: number nullable: true mc_change_1h_max_pct: type: number nullable: true sort: type: string trending_version: type: string nullable: true description: '"v1" only when sort=trending' deprecations: type: array description: 'Present only when a deprecated parameter was sent (today: `lp_burned`). Each entry names the parameter, exactly what it matches, and its replacement.' items: type: object properties: param: type: string status: type: string enum: - deprecated matches: type: string replacement: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokens x-operation-id-source: derived /tokens/search: get: tags: - Tokens summary: Resolve a ticker or name to candidate mints description: 'Search our token universe by **symbol or name** and get back candidate mints ranked so the right one is first: exact ticker match, then symbol prefix, then name prefix, then fuzzy — and *inside* each tier by liquidity, so `bonk` returns the $2M-liquidity Bonk before ten thousand squats named BONK. Each result carries `market_cap_usd`, `liquidity_usd`, `primary_dex` and `last_trade_at` so you can disambiguate in one hop and pass the `mint` on to `/token/{mint}` and friends. **Why this exists (2026-08-16):** 73% of all API 400s were tickers sent to mint routes (`/token/SOL`, `/token/WORLDCUP`). Those routes now answer a ticker with `code: ticker_not_mint` and a `search_url` pointing here. **Honesty:** this searches the memecoin/launchpad universe our market-cap tracker has seen (pump.fun, PumpSwap, LaunchLab/bonk, Raydium, Meteora, Orca). Native SOL and blue-chips are deliberately not in it — `q=SOL` returns tokens *named* SOL and the response says so in `note`. Any authenticated key, including Free.' parameters: - name: q in: query required: true schema: type: string minLength: 1 maxLength: 64 example: bonk description: Ticker or name fragment. 1 char matches exact symbol only; 2+ enables symbol prefix; 3+ name prefix; 4+ fuzzy. - name: limit in: query schema: type: integer minimum: 1 maximum: 50 default: 10 responses: '200': description: OK content: application/json: schema: type: object properties: q: type: string count: type: integer note: type: string nullable: true description: Present when q is a blue-chip ticker (results are namesakes) or when nothing matched. results: type: array items: type: object properties: mint: type: string symbol: type: string nullable: true name: type: string nullable: true match: type: string enum: - symbol_exact - symbol_prefix - name_prefix - fuzzy market_cap_usd: type: number nullable: true liquidity_usd: type: number nullable: true primary_dex: type: string nullable: true last_trade_at: type: string format: date-time nullable: true image_url: type: string nullable: true description: From token_metadata; null when not known. twitter: type: string nullable: true description: From token_metadata; null when not known. website: type: string nullable: true description: From token_metadata; null when not known. headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Missing/invalid q or limit headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: getTokensSearch x-operation-id-source: derived /tokens/almost-bonded: get: tags: - Tokens summary: Almost-bonded pump.fun tokens (PRO+) description: 'Pre-bond pump.fun tokens approaching graduation, ranked by **velocity** (Δbonding-progress per minute) and enriched with **deployer reputation**. Differentiator: pre-bond + reputation + velocity in one feed — "95% and accelerating" beats "92% stalled". `progress_pct` is derived from on-chain `real_token_reserves` depletion. `velocity_pct_per_min` is null until a 5-minute snapshot exists; `eta_minutes` is a linear projection from current velocity. `stalled` flags tokens whose |velocity| is below 0.05 %/min. Already-bonded tokens are excluded (`is_bonded=false`, `max_progress` defaults to 99.99).' parameters: - name: min_progress in: query schema: type: number minimum: 0 maximum: 100 default: 80 description: Progress band floor (0-100). - name: max_progress in: query schema: type: number minimum: 0 maximum: 100 default: 99.99 description: Progress band ceiling (< 100; excludes bonded). - name: min_velocity_pct_per_min in: query schema: type: number description: Momentum filter — minimum Δprogress per minute. Tokens without a 5m snapshot drop out when set. - name: max_age_minutes in: query schema: type: integer minimum: 1 description: Freshness — max minutes since deploy. - name: deployer_tier in: query schema: type: string enum: - elite - good - moderate - rising - cold - unranked description: Filter by deployer reputation tier. - name: launchpad in: query schema: type: string enum: - pumpfun - launchlab description: 'Restrict to one launchpad venue (default: both pump.fun and bonk/LetsBonk LaunchLab). No `bags` — its DBC curve has no progress decoder.' - name: authority_revoked in: query schema: type: boolean description: true = require both mint + freeze authority revoked. - name: min_liq in: query schema: type: number minimum: 0 description: Minimum liquidity_usd. - name: sort in: query schema: type: string enum: - velocity_desc - progress_desc - eta_asc default: velocity_desc description: '`velocity_desc` / `eta_asc` place unmeasured (null) tokens last.' - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 responses: '200': headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' X-Read-Source: schema: type: string enum: - replica - core - cache description: 'Where this page was served from: the read replica, the primary (core) after a replica fallback, or the 8-second response cache.' description: 'OK. Returns `{ tokens[], filters, returned, note, coverage }`. Each token: `mint`, `symbol`, `name`, `launchpad` (`pumpfun` | `launchlab`), `progress_pct`, `velocity_pct_per_min`, `eta_minutes`, `stalled`, `real_sol_reserves`, `market_cap_usd`, `liquidity_usd`, `authorities_revoked`, `deployer_tier`, `age_minutes`.' content: application/json: schema: type: object properties: tokens: type: array items: type: object properties: mint: type: string symbol: type: string nullable: true name: type: string nullable: true launchpad: type: string enum: - pumpfun - launchlab progress_pct: type: number nullable: true velocity_pct_per_min: type: number nullable: true description: Δprogress/min; null until a 5-minute-ago snapshot exists eta_minutes: type: number nullable: true description: Linear projection to 100% at the current velocity; null when velocity is null/non-positive stalled: type: boolean nullable: true description: true when |velocity| is below 0.05 %/min real_sol_reserves: type: number nullable: true market_cap_usd: type: number nullable: true liquidity_usd: type: number nullable: true authorities_revoked: type: boolean deployer_tier: type: string nullable: true age_minutes: type: integer nullable: true description: Minutes since deploy; null if the token has no pending_deploys row filters: type: object properties: min_progress: type: number max_progress: type: number min_velocity_pct_per_min: type: number nullable: true max_age_minutes: type: integer nullable: true deployer_tier: type: string nullable: true launchpad: type: string nullable: true authority_revoked: type: boolean nullable: true min_liq: type: number nullable: true sort: type: string returned: type: integer scan: type: object description: 'What the ranking covered (audit F09): complete=true means every candidate in the progress band was examined (or, for progress_desc, the top `limit` were found); scan_truncated=true means the budget ran out and velocity/eta ranks cover only the `scanned` most-progressed candidates.' properties: scanned: type: integer matched: type: integer nullable: true complete: type: boolean scan_truncated: type: boolean scan_budget: type: integer note: type: string coverage: $ref: '#/components/schemas/LaunchCoverage' '400': description: Invalid query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensAlmostBonded x-operation-id-source: derived /token/batch: post: tags: - Tokens summary: Batch per-mint snapshot (up to 50 mints) description: Same per-mint shape as `GET /token/{mint}` but in one round-trip. DB queries batched with `IN (...)`; dex-stream + RPC supply fan-outs run in parallel. 10–20× cheaper than N sequential single-mint calls. requestBody: required: true content: application/json: schema: type: object required: - mints properties: mints: anyOf: - type: string - type: array items: type: string minItems: 1 maxItems: 50 example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Up to 50 Solana token mints — a comma-separated string or a JSON array responses: '200': description: OK. Response contains `tokens[]` with the same per-mint shape as `/token/{mint}` (including `launchpad`). content: application/json: schema: type: object properties: tokens: type: array description: One entry per unique requested mint. Same price-freshness (price_source/price_observed_at/price_age_seconds/price_is_stale), creator-identity and KOL-aggregate contract as GET /token/{mint}. Unlike it, does not include name/symbol/image_url/socials/description/twitter_reuse/early_buyer_exit or a per-token data_status (failed blocks are listed once in top-level degraded_fields). items: type: object properties: mint: type: string price_usd: type: number nullable: true price_sol: type: number nullable: true market_cap: type: number nullable: true vwap_price_usd: type: number nullable: true vwap_price_sol: type: number nullable: true fdv_usd: type: number nullable: true liquidity_usd: type: number nullable: true primary_dex: type: string nullable: true primary_pool_address: type: string nullable: true is_token_2022: type: boolean nullable: true description: null = no mc-tracker observation (unknown) transfer_fee_bps: type: integer nullable: true description: null = unknown — NOT "no fee" mint_authority_revoked: type: boolean nullable: true freeze_authority_revoked: type: boolean nullable: true burn_detected: type: boolean nullable: true description: 'DEPRECATED meaning: `true` when the token MINT''s on-chain supply decreased (a token burn, measured by mc-tracker''s supply-drift monitor). It is NOT LP-burn evidence and never means liquidity cannot be removed. null = no mc-tracker observation (unknown).' token_supply_burn_detected: type: boolean nullable: true description: The mint's supply decreased (token burn). Not LP evidence. lp_burn_status: type: string enum: - verified - not_verified - unknown description: 'LP burn/lock evidence: `verified` only with measured LP-token custody (lp_burnt_pct ≥ 99). No Solana writer populates LP custody yet, so today every token is `unknown`. A token-supply burn is never promoted to an LP claim.' launchpad: type: string nullable: true enum: - pumpfun - launchlab - bags - null liquidity_to_mc_ratio: type: number nullable: true volume_24h_usd: type: number nullable: true volume_24h_sol: type: number nullable: true trades_24h: type: integer nullable: true price_source: type: string nullable: true enum: - mc_tracker - dex_stream - null description: Which pipeline produced price_usd/price_sol; null when there is no price price_observed_at: type: string format: date-time nullable: true description: Last trade observed BY THE SELECTED price source — the price's own age anchor price_age_seconds: type: integer nullable: true description: Seconds since price_observed_at (null when the source carries no timestamp) price_is_stale: type: boolean nullable: true description: true when price_age_seconds > 900; null (unknown) when a price exists but its source has no observation time — never false without an observation; false when there is no price last_trade_at: type: string format: date-time nullable: true description: Newest trade observed by ANY source (mc-tracker or dex-stream). Not a price-age anchor — use price_observed_at first_seen_at: type: string format: date-time nullable: true age_seconds: type: integer nullable: true is_blacklisted: type: boolean nullable: true description: null = the blacklist read failed (listed in degraded_fields) — never "not blacklisted" blacklist_category: type: string nullable: true launch_cohort_sol: type: number nullable: true launch_cohort_size: type: integer nullable: true description: null = the cohort read failed; 0 = no cohort rows deployer: type: object nullable: true description: Creator + live reputation. Resolved from `deployer_tokens` (bonded) or `pending_deploys` (not yet bonded — ~98% of fresh launches). null ⇒ read `deployer_identity` for why (unknown creator vs failed lookup). `history_status` other than `established` means the bonding rate is not a track record yet (a first launch is "0 of 1", not a serial dumper). properties: wallet: type: string address: type: string description: Same value as wallet (contract name) tier: type: string bonding_rate: type: number total_deployed: type: integer total_bonded: type: integer recent_bond_rate: type: number identity_status: type: string enum: - resolved identity_source: type: string enum: - deployer_tokens - pending_deploys history_status: type: string enum: - new_in_our_index - limited_history - reputation_pending - established first_seen_at: type: string format: date-time nullable: true description: First deploy OBSERVED in our capture — not the wallet's first launch anywhere observed_launch_count: type: integer resolved_outcome_count: type: integer description: Resolved outcomes (bonds + dumps) we hold for this creator stats_computed_at: type: string format: date-time nullable: true deployer_identity: type: object description: 'Always present. Explains a null `deployer`: `unknown` = both lookups succeeded and found no creator; `lookup_failed` = a lookup errored (retry — NOT "no deployer").' properties: identity_status: type: string enum: - resolved - unknown - lookup_failed history_status: type: string nullable: true enum: - new_in_our_index - limited_history - reputation_pending - established - null address: type: string nullable: true source: type: string nullable: true enum: - deployer_tokens - pending_deploys - null kol_activity: type: object description: 'Per-mint complete 7-day aggregate (no shared row budget across the batch). complete=false = the aggregate read failed (listed in degraded_fields): figures are null, never zero.' properties: buying_kols: type: integer nullable: true selling_kols: type: integer nullable: true net_flow_sol: type: number nullable: true signal: type: string nullable: true enum: - accumulating - distributing - neutral - null top_buyers: type: array description: Largest KOL buyers by SOL bought over the whole window (≤ 10, complete ranking) items: type: object properties: name: type: string wallet: type: string description: ULTRA only sol_amount: type: number unique_kols: type: integer buys: type: integer sells: type: integer buy_sol: type: number sell_sol: type: number last_trade_at: type: string format: date-time nullable: true window_hours: type: integer window_start: type: string format: date-time complete: type: boolean mc_change_pct: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number nullable: true volume_usd: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number mev_volume_pct: type: object nullable: true description: 'Present only when history exists. Keys: 5m, 15m, 1h, 2h, 4h' additionalProperties: type: number nullable: true history_age_seconds: type: integer nullable: true description: Present only when history exists count: type: integer as_of: type: string format: date-time description: Response assembly time — not an observation time degraded_fields: type: array items: type: string enum: - market - deployer - deployer_pending - first_seen - blacklist - launch_cohort - kol_activity description: Blocks whose lookup failed for this call; their per-token fields are null (unknown), never an empty-state default headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid body or mint headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: postTokenBatch x-operation-id-source: derived /tokens/{mint}/buyer-quality: get: tags: - Tokens summary: Token buyer-quality score description: 'Composite 0-100 score for the top 20 early buyers of a token. Base = avg 7d winrate of non-bot buyers with ≥3 tokens; + KOL bonus, − bundle/bot penalty. 5-minute LRU cache per mint. A mint with no captured early buyers is NOT a 404: it returns a neutral placeholder (score 50, confidence insufficient_data, signal neutral, zeroed breakdown) with `note: "Insufficient buyer data"` and a `coverage` block that says whether the mint is outside the launchpad write-gate. `confidence` counts only buyers whose win rate actually fed the score (`breakdown.qualified_win_rate_wallets`); a cohort of wallets with history but no usable win rates is `insufficient_data`, not high confidence. For bulk scoring use `POST /tokens/batch/buyer-quality`.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/BuyerQuality' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: getTokensByMintBuyerQuality x-operation-id-source: derived /tokens/{mint}/flow: get: tags: - Tokens summary: Trade-flow aggregate for one token (organic vs fake volume) description: 'Per-token trade-flow aggregate from the token_trades firehose over a 1h or 24h window: unique wallets/buyers/sellers, buy/sell counts, buy/sell SOL flow, net SOL, and trades_per_wallet — a wash-trading proxy (median ~5; p90 ~19; p99 ~50, so wash IS detectable). Counts and flows only; no holder/balance data. PRO+.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address - name: window in: query schema: type: string enum: - 1h - 24h default: 1h description: Aggregation window responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string window: type: string enum: - 1h - 24h from: type: string format: date-time coverage: $ref: '#/components/schemas/TradeCoverage' unique_wallets: type: integer unique_buyers: type: integer unique_sellers: type: integer buy_count: type: integer sell_count: type: integer total_trades: type: integer buy_sol: type: number sell_sol: type: number net_sol: type: number description: sell_sol − buy_sol; positive means net SOL leaving the pool (net selling) trades_per_wallet: type: number description: 'Wash-trading proxy: total_trades ÷ unique_wallets (median ~5, p90 ~19, p99 ~50)' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid mint or window headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintFlow x-operation-id-source: derived /tokens/{mint}/trades: get: tags: - Tokens summary: Mint-scoped trade tape (cursor-paginated raw trades) description: 'Every captured trade for one token, newest first — the backfill complement to the live firehose (seed history, then keep current via the stream). Per trade: tx_signature, wallet_address, action, sol_amount, token_amount, price_sol/price_usd, market_price_sol/market_price_usd, early_buyer_rank, slot, block_time. **Two different prices, on purpose.** `price_sol` is *this trade''s executed price* — `sol_amount / token_amount`, so it reconciles exactly with the amounts on the same row and with the PnL endpoints. `sol_amount` is the wallet''s net SOL movement, so that price is the trader''s all-in effective rate: it includes the swap fee and any account rent paid in the same transaction, and it is not the pool mid. `market_price_sol` is our market-cap tracker''s canonical pool price sampled near that trade''s slot — one value per token per update, so every trade in the same slot shares it. Use `price_sol` for cost basis, fills and PnL; use `market_price_sol` when you want a per-token price series independent of trade size and direction. *Changed 2026-08-16*: `price_sol`/`price_usd` previously carried the canonical value, which disagreed with the row''s own amounts by a 7.9% median (and far more on fast-moving mints, where one slot can span a 60% price swing). The canonical number did not disappear — it moved to `market_price_*`. **Coverage honesty**: capture starts 2026-04-12 and is pump.fun-pipeline scoped — the `coverage` response object carries both markers. **Size floor**: Buys under 0.05 SOL (under $3.50 when paid in USDC/USDT) are not stored; sells that receive SOL are kept at any size. (`coverage.size_floor`). Default window is full history. PRO+. **Where the rows come from** (*added 2026-08-29*): closed months older than the newest three move from Postgres to a Parquet archive; pages are split at `history.postgres_from` (Unix seconds) — rows at/after it are served from Postgres, older rows from the archive reader — with the same ordering and cursor. `history.archive_used` says whether this page touched the archive (`archive_months` lists which), `history.archive_available` whether the archive could be reached (`null` = not configured), and `history.truncated: true` means older history was requested but the archive did not answer — retry later rather than treating `has_more:false` as the end of the tape. Response header `X-Read-Source`: `core`, `core+archive` or `archive`.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 100 - name: cursor in: query schema: type: string description: Opaque pagination cursor from next_cursor - name: action in: query schema: type: string enum: - buy - sell - name: wallet in: query schema: type: string description: Filter to one wallet's trades on this token - name: since in: query schema: type: integer description: 'Unix seconds lower bound (default: full history)' - name: until in: query schema: type: integer description: 'Unix seconds upper bound (default: now)' responses: '200': headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' X-Read-Source: schema: type: string enum: - core - core+archive - archive description: 'Where this page''s rows came from: Postgres only, Postgres plus the Parquet archive (page straddles `history.postgres_from`), or the archive only.' description: OK content: application/json: schema: type: object properties: mint: type: string trades: type: array items: type: object properties: tx_signature: type: string wallet_address: type: string action: type: string enum: - buy - sell sol_amount: type: number token_amount: type: number price_sol: type: number nullable: true description: This trade's executed price (sol_amount / token_amount) price_usd: type: number nullable: true market_price_sol: type: number nullable: true description: mc-tracker's canonical pool price sampled near this trade's slot — not this trade's price market_price_usd: type: number nullable: true early_buyer_rank: type: integer nullable: true slot: type: integer nullable: true block_time: type: integer description: Unix seconds traded_at: type: string format: date-time next_cursor: type: string nullable: true has_more: type: boolean filters: type: object properties: action: type: string nullable: true enum: - buy - sell - null wallet: type: string nullable: true since: type: integer until: type: integer history: type: object description: Postgres/archive split for this page (added 2026-08-29) properties: postgres_from: type: integer description: Unix seconds — rows at/after this come from Postgres, older from the archive archive_used: type: boolean archive_months: type: array items: type: string archive_available: type: boolean nullable: true description: null = archive reader not configured truncated: type: boolean description: true = older history was requested but the archive did not answer — do not treat has_more:false as the end of the tape note: type: string description: Present when the archive could not be reached or is not configured coverage: $ref: '#/components/schemas/TradeCoverage' '400': description: Invalid mint or params headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintTrades x-operation-id-source: derived /tokens/{mint}/candles: get: tags: - Tokens summary: OHLCV candles for a token description: 'Open/high/low/close + volume candles aggregated from the 1-minute base series. PRO gets all timeframes with the last 30 days of history measured from now (`history_floor`): a window entirely older returns 200 with zero candles and `history_outside_plan: true`, a partly older one is clamped (`history_clamped: true`). ULTRA (and BUSINESS) adds net-flow/liquidity fields and full retained history. This tier split applies to Solana candles only; RHC candles are full-history on PRO+. Windows over 999 buckets are paged newest-first on the timeframe grid; `truncated` reports a page budget that ran out.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address - name: tf in: query schema: type: string enum: - 1m - 5m - 15m - 1h - 4h - 1d default: 1h description: Candle timeframe. Also accepted as `timeframe`; `tf` wins if both are sent. - name: limit in: query schema: type: integer minimum: 1 maximum: 1500 default: 200 description: Max candles to return (1500 = one full day of 1m bars with margin) - name: from in: query schema: type: string format: date-time description: 'ISO8601 window start, inclusive (defaults to limit×tf back). PRO: floored at now minus 30 days' - name: to in: query schema: type: string format: date-time description: ISO8601 window end, exclusive (defaults to now) responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string timeframe: type: string enum: - 1m - 5m - 15m - 1h - 4h - 1d from: type: string format: date-time to: type: string format: date-time count: type: integer net_flow_included: type: boolean description: true on ULTRA — signals whether the net-flow/liquidity fields below are present truncated: type: boolean description: true when the 3-page budget (up to 999 buckets each) ran out before `limit` candles or `from` were reached covered_from: type: string format: date-time description: Oldest instant actually searched; equals `from` when the whole window was scanned history_floor: type: string format: date-time nullable: true description: 'PRO: now minus 30 days, rounded up to the tf grid (oldest bucket served). null on ULTRA/BUSINESS (no floor)' history_clamped: type: boolean description: 'PRO: part of the requested window was older than history_floor and was cut; `from` echoes the clamped start' history_outside_plan: type: boolean description: 'PRO: the whole requested window is older than history_floor, so 200 with zero candles' candles: type: array items: type: object properties: t: type: string description: Bucket start (ISO timestamp as returned by the OHLC rollup) open: type: number nullable: true high: type: number nullable: true low: type: number nullable: true close: type: number nullable: true volume_usd: type: number nullable: true trades: type: integer nullable: true market_cap_usd: type: number nullable: true buy_volume_usd: type: number nullable: true description: ULTRA only sell_volume_usd: type: number nullable: true description: ULTRA only net_volume_usd: type: number nullable: true description: ULTRA only — buy_volume_usd − sell_volume_usd buy_count: type: integer nullable: true description: ULTRA only sell_count: type: integer nullable: true description: ULTRA only volume_mev_usd: type: number nullable: true description: ULTRA only open_liquidity_usd: type: number nullable: true description: ULTRA only close_liquidity_usd: type: number nullable: true description: ULTRA only high_mc_usd: type: number nullable: true description: ULTRA only low_mc_usd: type: number nullable: true description: ULTRA only headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid tf/limit/window headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintCandles x-operation-id-source: derived /tokens/batch/buyer-quality: post: tags: - Tokens summary: Batch buyer-quality scoring (up to 50 mints) description: 'Same per-mint shape as `GET /tokens/{mint}/buyer-quality`. Shares the LRU cache with the single-mint endpoint — already-warm mints return at ~zero cost. Response includes `cache_hits` counter. **Coverage** (additive): each item carries `in_scope` (boolean|null) derived from whether that mint had early-buyer rows in our capture — `false` may simply mean ''no early-buyer rows'', not proof the token never traded (the batch path does not run the per-mint probe; use the single-mint endpoint for a probed verdict). A top-level `coverage` object carries the shared `history_start` + `scope` markers once.' requestBody: required: true content: application/json: schema: type: object required: - mints properties: mints: anyOf: - type: string - type: array items: type: string minItems: 1 maxItems: 50 example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Up to 50 Solana token mints — a comma-separated string or a JSON array responses: '200': description: OK. Response contains `tokens[]`, `count`, and `cache_hits`. content: application/json: schema: type: object properties: tokens: type: array items: allOf: - $ref: '#/components/schemas/BuyerQualityBatchItem' - type: object properties: note: type: string description: Present when the mint had no early-buyer rows to score, e.g. "Insufficient buyer data" in_scope: type: boolean nullable: true description: Whether this mint had early-buyer rows in our capture. false may simply mean no early-buyer rows, not proof the token never traded — the batch path skips the per-mint probe GET /tokens/{mint}/buyer-quality runs. count: type: integer cache_hits: type: integer coverage: type: object properties: history_start: type: string scope: type: string note: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid body or mint headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: postTokensBatchBuyerQuality x-operation-id-source: derived /tokens/{mint}/cap-table: get: tags: - Tokens summary: Token cap table description: 'The token''s early buyers with classification (KOL, human, bundle) AND their outcome on this token — `realized_pnl_sol`, `total_sol_bought`/`total_sol_sold`, `first_sell_at`, and `still_holding` — so you can read whether the smart early money profited and has already exited. PRO returns the top 10, ULTRA the full 20 (mv_early_buyer_pnl excludes rank-1, the deployer''s own buy). Cohort scope: first 20 buyers (deployer excluded) of launches our deployer pipeline recorded at creation and tracked from that launch; mints admitted later (bags/LaunchLab persist-set, revived curves, restart-loaded history) have no early-buyer cohort, and buyers during an ingest gap are missing.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 20 responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string buyers: type: array items: type: object properties: rank: type: integer wallet: type: string first_buy_sol: type: number first_buy_at: type: string format: date-time nullable: true realized_pnl_sol: type: number nullable: true description: Realized SOL PnL for this wallet on this token (FIFO cost-basis from confirmed trades). total_sol_bought: type: number nullable: true total_sol_sold: type: number nullable: true first_sell_at: type: string format: date-time nullable: true description: When this early buyer first sold; null if never sold. still_holding: type: boolean description: True if the wallet has not sold (first_sell_at is null). is_bundle: type: boolean is_kol: type: boolean kol_name: type: string nullable: true bot_confidence: type: string nullable: true historical_win_rate: type: number nullable: true description: Wallet's all-time win rate (0–1), from mv_alpha_wallets. historical_pnl_sol: type: number nullable: true historical_tokens: type: integer nullable: true summary: type: object properties: known_alpha_wallets: type: integer known_kols: type: integer bundle_buyers: type: integer buyer_quality_score: type: number description: 0–100 composite quality of the early-buyer cohort. confidence: type: string enum: - insufficient_data - low - medium - high description: 'Confidence in the PREDICTIVE inputs: counts only buyers whose win rate fed the score (≥3 tokens of history, not bot-flagged, not zero-cost-dump dominated). insufficient_data = none did — the score is the neutral-50 default plus penalties. 1 → low, 5+ → medium, 11+ → high.' signal: type: string cohort_size: type: integer description: Early buyers scored (≤20). still_holding: type: integer description: Cohort members with no confirmed sell on a tracked venue (swap-ledger scoped — a plain transfer out still reads as holding). sold: type: integer still_holding_pct: type: number coverage: $ref: '#/components/schemas/TradeCoverage' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintCapTable x-operation-id-source: derived /tokens/{mint}/holders: get: tags: - Tokens summary: Live holders, holder count + concentration description: 'A live holder census read from the ledger at `confirmed`: every token account of the mint (owner + balance via mint-scoped `getProgramAccounts`), merged per owner. Gives an EXACT `holder_count` (distinct non-zero owners minus excluded pools/curves/burns), `token_accounts_nonzero`, ranks 1–100 retained, and `top1/top10/top20/top50/top100_share` over the circulating denominator. Each disclosed owner is labelled from MadeOnSol wallet intelligence (deployer / kol / early_buyer / bundle / bot / dump_cluster). Program-derived owners (liquidity pools, bonding curves, vaults) and burn addresses are EXCLUDED from the denominator and listed in `excluded[]` — the #1 raw account of a fresh memecoin is its own bonding curve. Disclosure: PRO ranks 1–10, ULTRA 1–50, BUSINESS 1–100; the maths is tier-independent. Measured latency: fresh pump.fun mints <1 s, $0.1–2M memecoins ~0.1–1.2 s, 200k–550k-account tokens 6–11 s (503 `holder_scan_in_progress` then cached). Mega-caps the provider refuses (TRUMP/JUP/BONK class) fall back to the top-20 `getTokenLargestAccounts` view with `holder_count: null` and `source.census_fallback_reason` set — never a trade-derived estimate. The same fallback applies when the census times out, and (since 2026-10-02) when a census''s balances add up to more than the mint''s supply: such a read is not one consistent ledger snapshot, so it is refused rather than served (`census_fallback_reason: census_inconsistent_sum_exceeds_supply`). Distinct from `/cap-table` (who bought first) — this is who holds NOW.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string slot: type: integer description: Ledger slot the holder set was read at. as_of: type: string format: date-time holders: type: array items: type: object properties: rank: type: integer owner: type: string description: Wallet (owner of the token account(s)). token_accounts: type: array items: type: string amount_raw: type: string description: Raw u64 balance as a string — never a float. amount: type: number nullable: true pct_of_supply: type: number nullable: true pct_of_circulating: type: number nullable: true description: Share of supply minus pools/curves/burns. labels: type: array items: type: string enum: - deployer - kol - early_buyer - buyer - bundle - bot - dump_cluster description: Empty = unknown to us, not verified clean. kol_name: type: string nullable: true early_buyer_rank: type: integer nullable: true bot_confidence: type: string nullable: true enum: - none - low - medium - high historical_win_rate: type: number nullable: true count: type: integer disclosed: type: integer description: 'Rank cap by tier: 10 PRO, 50 ULTRA, 100 BUSINESS.' excluded: type: array items: type: object properties: owner: type: string token_accounts: type: array items: type: string amount_raw: type: string pct_of_supply: type: number nullable: true reason: type: string enum: - pool - bonding_curve - burn - program_account description: pool = vault authority of a known pool (dex + pool_address set); bonding_curve = pump.fun / LaunchLab curve; burn = incinerator / system program; program_account = off-curve owner we could not attribute to a known pool (vault, escrow, staking, unknown pool). dex: type: string nullable: true description: pumpfun | pumpswap | raydium | launchlab | meteora | orca | jupiter — when named. pool_address: type: string nullable: true concentration: type: object properties: holder_count: type: integer nullable: true description: Exact distinct non-zero owners minus excluded pools/curves/burns, at slot (census). null only when the census was not served (provider refusal, timeout or an inconsistent census; see source.census_fallback_reason) — never estimated from trades. holder_count_source: type: string nullable: true token_accounts_nonzero: type: integer nullable: true supply_raw: type: string nullable: true circulating_raw: type: string nullable: true decimals: type: integer nullable: true top1_share: type: number nullable: true top10_share: type: number nullable: true top20_share: type: number nullable: true top50_share: type: number nullable: true top100_share: type: number nullable: true pool_and_program_pct: type: number nullable: true description: Share of TOTAL supply sitting in pools/curves/vaults/burns among the excluded owners (= pool_pct + burned_pct + program_pct). pool_pct: type: number nullable: true description: Share of total supply in NAMED pools + bonding curves (excluded[].reason pool | bonding_curve). burned_pct: type: number nullable: true description: Share of total supply at burn addresses. program_pct: type: number nullable: true description: Share of total supply held by off-curve owners we could not attribute (excluded[].reason program_account). deployer_pct: type: number nullable: true kol_pct: type: number nullable: true early_buyer_pct: type: number nullable: true bundle_pct: type: number nullable: true bot_pct: type: number nullable: true dump_cluster_pct: type: number nullable: true distinct_owners_in_top20: type: integer ranked_owners_available: type: integer description: How many ranked owners the scan retained (≤100 census, ≤20 fallback). deployer: type: object nullable: true properties: wallet: type: string tier: type: string bonding_rate: type: number nullable: true source: type: object properties: method: type: string enum: - getProgramAccounts_census - getTokenLargestAccounts token_program: type: string nullable: true rpc_cap: type: integer commitment: type: string scan_ms: type: integer census_fallback_reason: type: string nullable: true description: 'Why the full census was not served (null on a census). `census_exceeds_provider_limit` = the provider refused it (mega-caps); `census_timed_out`; `census_inconsistent_sum_exceeds_supply` = the census balances summed past the mint supply (or the supply could not be read), so it was refused (2026-10-02); `census_failed: ` otherwise. `note` explains it in prose.' note: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: 'Mint not found on-chain (`error_kind: not_a_mint`).' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': description: '`error_kind: holder_scan_in_progress` (Retry-After 20) — large established tokens take 5–30 s to enumerate upstream; the scan continues and is cached, so the retry is instant. Or `holder_rpc_unavailable` (Retry-After 15). Either way we fail closed rather than serve a trade-derived guess.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintHolders x-operation-id-source: derived /tokens/{mint}/top-traders: get: tags: - Tokens summary: Top traders on a token (by realized PnL) description: 'Every wallet that traded this mint, ranked by realized SOL PnL (or ROI), using the SAME average-cost-basis method as `/wallet/{address}/pnl` — so a wallet''s `realized_pnl_sol` here equals its per-token value there. Each trader is enriched with our reputation data (is_kol, is_alpha_tracked + bot_confidence + historical win rate) so you can separate smart money from a bot round-trip. Distinct from `/cap-table` (first ~20 EARLY buyers by entry order): this ranks ALL traders by profit. PRO returns top 25, ULTRA top 100.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 description: Capped at 25 (PRO) / 100 (ULTRA). - name: offset in: query schema: type: integer minimum: 0 default: 0 description: Page start within the ranking. `rank` continues across pages; the visible window stays capped at the tier's row ceiling (offset+limit ≤ 25 PRO / 100 ULTRA — paging past it returns an empty page). - name: sort in: query schema: type: string enum: - pnl - roi default: pnl - name: window_days in: query schema: type: integer minimum: 1 maximum: 180 default: 90 - name: min_bought_sol in: query schema: type: number minimum: 0 default: 0.1 description: Drop dust wallets that bought less than this. responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string sort: type: string enum: - pnl - roi window_days: type: integer traders: type: array items: type: object properties: rank: type: integer wallet: type: string trades: type: integer buys: type: integer sells: type: integer bought_sol: type: number sold_sol: type: number realized_pnl_sol: type: number description: Average-cost-basis realized SOL PnL. Negative for losers. unrealized_pnl_sol: type: number total_pnl_sol: type: number held_value_sol: type: number roi: type: number nullable: true description: Realized ROI as a fraction of cost basis of the sold portion. null if nothing sold. still_holding: type: boolean first_trade_at: type: string format: date-time last_trade_at: type: string format: date-time is_kol: type: boolean kol_name: type: string nullable: true is_alpha_tracked: type: boolean bot_confidence: type: string nullable: true historical_win_rate: type: number nullable: true description: Wallet's all-time win rate (0–1), from mv_alpha_wallets. historical_pnl_sol: type: number nullable: true historical_tokens: type: integer nullable: true summary: type: object properties: returned: type: integer known_kols: type: integer known_alpha_wallets: type: integer net_realized_pnl_sol: type: number coverage: $ref: '#/components/schemas/TradeCoverage' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid wallet address headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintTopTraders x-operation-id-source: derived /tokens/{mint}/risk: get: tags: - Tokens summary: Token risk / safety score description: 'Transparent 0–100 rug-risk score (higher = riskier) with a per-factor breakdown — mint/freeze authority, liquidity depth & thinness, Token-2022 transfer fee, LP burn, bundled/sniped open, deployer track record, KOL distribution, supply inflation (on-chain supply grown above expected = stealth-mint/dilution), and blacklist. Composed from the same data layer as `GET /token/{mint}` (no extra latency). Returns `band` (safe/caution/danger), `factors[]` (each with `status` + `points` + `detail`), and the raw `inputs` (including `supply_inflation_pct` and `sniper_footprint`). The top-level `dev` block (nullable) is the deployer self-activity read: create-tx self-buy (`buy_sol`/`buy_tokens`/`buy_supply_pct`), post-create own-mint trading (`bought_tokens_after`, `sold_tokens`, `sold_sol`, `first_sell_at`/`last_sell_at`), and live on-chain holdings (`holdings_tokens`, `holdings_supply_pct`, `wallet_empty`), each with its own observation time (`holdings_observed_at`, `activity_rollup_through`), plus `transfer_status` (`none_detected` | `suspected` | `unknown`): a balance below what the dev''s own recorded trades explain is only ever SUSPECTED (a sell on an uncaptured venue looks identical), and misaligned or missing observations are `unknown`; `transferred_out` is its deprecated boolean view. Carries `score_version` (now `v2`) and `as_of` (response time — not an observation time). **Score v2 (2026-09-21):** the LP factor no longer credits a token-SUPPLY burn (mc-tracker''s burn flag) — LP custody is not observed for Solana pools, so `lp_burn` is warn/8 with `inputs.lp_burn_status: unknown` unless verified LP evidence exists; an unknown transfer fee is warn/4, not "No transfer fee"; deployer reputation scores only an established history. `assessment` says whether every input was observed: `incomplete` lists `unknown_inputs` and the band is never `safe`; a failed lookup of a score-critical input is a retryable 503 (`code: risk_inputs_unavailable`), never a score computed without it. **Same loader on every transport:** `POST /tokens/batch/risk` and x402 return identical scores for identical evidence; batch skips only the live dev-holdings RPC (`dev.holdings_*` null, `transfer_status: unknown`). The `coverage` object (shared TradeCoverage shape) discloses whether the mint sits inside the launchpad trade capture, with a note naming which sub-fields are trade-derived (cohort/sniper/bundle factors, dev sell rollup) vs pure on-chain reads (authorities, liquidity, live holdings) that are unaffected. PRO+.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump responses: '200': description: OK content: application/json: schema: type: object properties: resolved_from: type: object description: Present only when the path address was a liquidity pool that we resolved to its token mint (2026-09-05). Absent otherwise. properties: address: type: string description: The pool address you sent. kind: type: string enum: - pool dex: type: string example: pumpswap source: type: string enum: - token_pools - chain description: 'How the pool was resolved: from our pool index, or decoded live on-chain (PumpSwap).' mint: type: string description: The mint scored. Equals the path value unless the path was a PumpSwap pool address, in which case this is the pool's resolved mint and `resolved_from` is set. risk_score: type: integer minimum: 0 maximum: 100 band: type: string enum: - safe - caution - danger description: Never `safe` while assessment.status is `incomplete`, or with an un-revoked mint/freeze authority score_version: type: string enum: - v2 description: 'v2 (2026-09-21): a token-supply burn no longer earns the LP-burn credit (v1: burn_detected=true → 0, every other token → 8; v2: every token without verified LP evidence → 8, burned or not — no burn penalty); unknown transfer fee is warn(4) not "No transfer fee"; deployer reputation scores only an established history; unknown inputs keep the band off `safe`.' factors: type: array items: type: object properties: key: type: string label: type: string status: type: string enum: - ok - warn - danger - unknown - not_assessed description: ok/warn/danger = evidence read and scored. unknown = the input should exist but could not be read or is insufficient (0 points; listed in assessment.unknown_inputs; band never safe). not_assessed = no evidence source applies to this token (0 points; listed in assessment.not_assessed). Neither is ever positive evidence. points: type: integer detail: type: string inputs: type: object description: Every scorer input. Enrichment fields are null when unobserved — unknown, not false/0. properties: mint_authority_revoked: type: boolean nullable: true freeze_authority_revoked: type: boolean nullable: true liquidity_usd: type: number nullable: true liquidity_to_mc_ratio: type: number nullable: true transfer_fee_bps: type: integer nullable: true is_token_2022: type: boolean nullable: true token_supply_burn_detected: type: boolean nullable: true description: The mint's supply decreased (token burn). Not LP evidence. burn_detected: type: boolean nullable: true description: DEPRECATED alias of token_supply_burn_detected lp_burn_status: type: string enum: - verified - not_verified - unknown description: 'LP burn/lock evidence: `verified` only with measured LP-token custody (lp_burnt_pct ≥ 99). No Solana writer populates LP custody yet, so today every token is `unknown`. A token-supply burn is never promoted to an LP claim.' launch_cohort_sol: type: number nullable: true launch_cohort_size: type: integer nullable: true sniper_footprint: type: object nullable: true deployer_bonding_rate: type: number nullable: true deployer_total_deployed: type: integer nullable: true deployer_history_status: type: string nullable: true enum: - new_in_our_index - limited_history - reputation_pending - established - null description: null when there is no deployer deployer_reputation_scored: type: boolean description: Whether the bonding rate moved the score (established history only) liquidity_observed_at: type: string format: date-time nullable: true description: When the liquidity figure was last observed (mc-tracker last trade). With the creator not assessed, band safe requires this within the last 6 h (null or more than 5 min in the future = not fresh). 6 h is a policy threshold, not proof that the liquidity is still there. kol_signal: type: string nullable: true is_blacklisted: type: boolean nullable: true supply_inflation_pct: type: number nullable: true assessment: type: object properties: status: type: string enum: - complete - incomplete description: incomplete = at least one score input is unknown; the score is a lower bound unknown_inputs: type: array items: type: string description: Inputs not observed for this token (e.g. transfer_fee, mint_authority, liquidity, kol_activity, deployer_reputation, blacklist, supply_inflation, launch_cohort). deployer_reputation is unknown when a creator lookup failed, when a LAUNCHPAD mint's creator was not recorded (a coverage gap), or when a known creator has no established history. explanations: type: object additionalProperties: type: string description: Why each unknown / not-assessed input is listed, keyed by input name, plus band_cap when the band was capped (creator not assessed and liquidity / liquidity-to-MC not both ok, or the liquidity figure older than 6 h — a policy threshold, not proof of safety). Missing required creator information never yields band safe, for any token. not_assessed: type: array items: type: string description: 'Dimensions not measured for this token that are NOT a failed lookup: lp_custody (never observed for Solana pools) and deployer_reputation only when the token has no launchpad origin in our capture (no deploy row, no bonding-curve pool, not PumpSwap-primary, no pump/bonk/BAGS vanity suffix). A launchpad mint with an unrecorded creator, or a failed origin lookup, is an unknown input instead.' dev_status: type: string enum: - ok - not_found - unavailable description: ok = dev block present · not_found = no pending_deploys row (not a launchpad deploy we saw) · unavailable = the lookup failed dev: type: object nullable: true description: Dev-wallet activity (migration 225). Trade-derived fields are accurate THROUGH activity_rollup_through; holdings are observed at holdings_observed_at — never at as_of. properties: wallet: type: string nullable: true launchpad: type: string nullable: true deployed_at: type: string format: date-time nullable: true buy_sol: type: number nullable: true buy_tokens: type: number nullable: true buy_supply_pct: type: number nullable: true bought_tokens_after: type: number nullable: true sold_tokens: type: number nullable: true sold_sol: type: number nullable: true first_sell_at: type: string format: date-time nullable: true last_sell_at: type: string format: date-time nullable: true holdings_tokens: type: number nullable: true description: Live on-chain balance (60 s cache); null = RPC unavailable or not checked (batch) holdings_supply_pct: type: number nullable: true wallet_empty: type: boolean nullable: true holdings_observed_at: type: string format: date-time nullable: true description: When the RPC balance was observed (cache fetch time) activity_rollup_through: type: string format: date-time nullable: true description: Dev trades are rolled up through this time (dev_activity cursor) activity_rollup_ran_at: type: string format: date-time nullable: true expected_tokens_from_trades: type: number nullable: true description: Balance the dev's own recorded trades explain, aligned to holdings_observed_at transfer_status: type: string enum: - none_detected - suspected - unknown description: suspected = balance below what recorded trades explain (a sell on an uncaptured venue looks identical) — never a verified transfer; unknown = observations misaligned, rollup stale, no coverage, or holdings not checked transfer_reason: type: string transferred_out: type: boolean nullable: true description: 'DEPRECATED boolean view of transfer_status: true = suspected, false = none_detected, null = unknown' coverage: $ref: '#/components/schemas/TradeCoverage' as_of: type: string format: date-time description: Response time — not the observation time of any input headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: 'The address is valid base58 but is not a token mint. `code` says what it is: `wallet_not_mint` (a system-owned account — wallet intel lives at /wallet/{address}, KOL routes at /kol/{wallet}; `hint` carries both), `token_account_not_mint` (a holder''s token account; `mint` + `risk_url` point at the right mint when it can be read), `pool_not_mint` (a Raydium / Meteora / Orca pool we could not resolve; `dex` names it — pass the pool''s base mint). Also `evm_address_on_solana_route` for a 0x address (`rhc_endpoint` names the Robinhood Chain twin) and `mint_lowercased` for a mint that lost its case.' content: application/json: schema: type: object properties: error: type: string code: type: string enum: - wallet_not_mint - token_account_not_mint - pool_not_mint - evm_address_on_solana_route - mint_lowercased received: type: string hint: type: string mint: type: string risk_url: type: string dex: type: string docs: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: '`code: not_tracked` — a real mint that has never traded in our capture (carries `resolved_from` + `mint` when the path was a pool we resolved), or `code: not_on_chain` — no account with this address exists on Solana mainnet (checked live), i.e. not a coverage gap on our side.' content: application/json: schema: type: object properties: error: type: string code: type: string enum: - not_tracked - not_on_chain received: type: string hint: type: string mint: type: string resolved_from: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': description: 'A score-critical input (token_prices, blacklist, supply drift, snapshot) could not be read. Retry after `Retry-After`; no score is returned rather than one computed without it. Body: `{ error, code: "risk_inputs_unavailable", unavailable_inputs: string[], retryable: true, retry_after_seconds }`. Distinct from a missing row, which is scored as an unknown input.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintRisk x-operation-id-source: derived /tokens/{mint}/bundle: get: tags: - Tokens summary: Token bundle intelligence description: 'The token''s **bundle cohort** — the same-slot wallets (≥3 buying in one slot, `is_bundle`) — and how much of supply they STILL hold, computed from confirmed on-chain trades (not a pre-execution shred feed). `held_pct_of_supply` (net held ÷ circulating supply) is the headline rug/insider signal; `bundle_kind` distinguishes a definitively-atomic single-tx bundle (`atomic_tx`) from a possibly-coincidental `same_slot` cohort. Field-gated on ALL tiers: BASIC gets the scalar `bundle` block; PRO adds the top-10 wallets (flags only); ULTRA adds the full cohort enriched with KOL/alpha identity. Caveats: `buy_volume` is cumulative buy volume (can exceed supply); `tokens_held` is a swap-derived net position, not a holder census. The `coverage` object (additive) separates ''no bundle cohort'' from ''mint outside our launchpad trade capture''.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/TokenBundle' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: Token not found or not tracked headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: FREE x-badges: - name: FREE position: after color: '#64748b' operationId: getTokensByMintBundle x-operation-id-source: derived /tokens/{mint}/pools: get: tags: - Tokens summary: Token liquidity map (per-venue pools) description: Per-venue **liquidity map** — every DEX pool the token trades in (pump.fun / PumpSwap / Raydium / Meteora / Orca), which pools are live vs parked, and how fragmented its liquidity is across venues. Each pool carries its `dex`, `quote_mint`, `liquidity_usd`, `last_price_sol`, `last_swap_at`, `amm_id`, and an `is_active` flag; the `summary` rolls up `pool_count` / `active_pool_count` / `dex_count`, the `primary_pool` + `primary_dex`, and `top_pool_share_pct` (how concentrated liquidity is in the single deepest pool). Complements `GET /token/{mint}`, which returns only a single aggregate liquidity number. PRO+. parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string pools: type: array items: type: object properties: pool_address: type: string dex: type: string description: Venue (e.g. pumpfun, pumpswap, raydium, meteora, orca) quote_mint: type: string liquidity_usd: type: number nullable: true last_price_sol: type: number nullable: true last_swap_at: type: string format: date-time nullable: true amm_id: type: string nullable: true is_active: type: boolean description: Live (recently swapped) vs parked summary: type: object properties: pool_count: type: integer active_pool_count: type: integer dex_count: type: integer dexes: type: array items: type: string total_liquidity_usd: type: number nullable: true primary_pool: type: string nullable: true primary_dex: type: string nullable: true top_pool_share_pct: type: number nullable: true description: Share of total liquidity held by the single deepest pool (0–100) universe: type: object description: The inventory is the deepest max_pools pools by liquidity (audit F19); summary totals cover the returned pools. truncated=true = more pools exist past the cut. properties: kind: type: string enum: - top_by_liquidity max_pools: type: integer returned: type: integer truncated: type: boolean headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: Token not found or not tracked headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintPools x-operation-id-source: derived /tokens/{mint}/locks: get: tags: - Tokens summary: Token locks & vesting on a mint (Streamflow, Jupiter Lock, Bonfida, Smithii… description: 'Every **on-chain token lock / vesting contract** on a mint — Streamflow streams, Jupiter Lock vesting escrows and Bonfida token-vesting accounts, plus Smithii vesting and Sablier Lockup streams while they are listed in ''meta.programs'' (rows are admitted only after per-account verification; Sablier''s ''recipient'' is the current proven stream-NFT holder or null, see ''holder_status'') — decoded from the locker programs'' account state (Yellowstone account subscription + daily getProgramAccounts reconciliation, mig 326). Each contract carries the schedule (start / cliff / period / end, cliff and per-period amounts), the terms (cancelable by sender/recipient, transferable, top-up) and a **live-derived** view computed at request time: what is still locked right now, what has unlocked, what is claimable, and the ''next_unlock'' event. The ''summary'' rolls up locked / deposited totals (raw, ui, usd, % of supply), the **7d / 30d forward unlock schedule**, the nearest next unlock across all contracts, distinct lockers and how many active contracts the sender can still cancel. Answers *did the team lock, how much, until when, and can they pull it*. **LP locks are not included** (this is token/vesting locks). Amounts are base-unit strings; ui/usd/pct are null when decimals/price are unknown (''token.facts_resolved''). PRO+.' parameters: - name: mint in: path required: true schema: type: string example: NUGye8S6CV82ZNrauf5YfXL2xJxvSvfiMAvy2U1sAVk - name: status in: query schema: type: string enum: - active - completed - cancelled - closed description: Filter the list (summary always covers all rows) - name: program in: query schema: type: string enum: - streamflow - jupiter_lock - bonfida_vesting - smithii_vesting - sablier_lockup - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 200 responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string token: type: object properties: symbol: type: string nullable: true name: type: string nullable: true decimals: type: integer nullable: true price_usd: type: number nullable: true supply: type: number nullable: true market_cap_usd: type: number nullable: true facts_resolved: type: boolean summary: type: object properties: lock_count: type: integer description: Exact count of contracts on the mint complete: type: boolean description: false when the mint holds more than 5000 contracts — totals then cover the newest 5000 (rows_considered) rows_considered: type: integer active_count: type: integer by_program: type: object additionalProperties: type: integer by_kind: type: object additionalProperties: type: integer distinct_lockers: type: integer locked_raw: type: string locked: type: number nullable: true locked_usd: type: number nullable: true locked_pct_of_supply: type: number nullable: true deposited_raw: type: string deposited: type: number nullable: true deposited_usd: type: number nullable: true unlocking_7d_raw: type: string unlocking_7d: type: number nullable: true unlocking_7d_usd: type: number nullable: true unlocking_7d_pct_of_supply: type: number nullable: true unlocking_30d_raw: type: string unlocking_30d: type: number nullable: true unlocking_30d_usd: type: number nullable: true unlocking_30d_pct_of_supply: type: number nullable: true next_unlock: type: object nullable: true properties: at: type: string format: date-time kind: type: string amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true lock_account: type: string active_cancelable_by_sender: type: integer locks: type: array items: type: object description: One on-chain lock/vesting contract with a live-derived view. raw = base units (digit string); ui/usd/pct fields are null when decimals/price are unknown. properties: lock_account: type: string description: The contract account (Streamflow stream / Jupiter VestingEscrow / Bonfida vesting account) program: type: string enum: - streamflow - jupiter_lock - bonfida_vesting - smithii_vesting - sablier_lockup provider: type: object description: 'Who runs the lock contract, and how sure we are. identity: verified = a known provider deployment (by contract address); compatible = only the event shape (ABI) matches a known provider, the operator is NOT identified; unverified = unknown. id/website_url are null unless verified. lock_url is a per-lock page on the provider''s site, emitted ONLY where the URL format is proven (none is proven for Streamflow / Jupiter Lock / Bonfida today, so it is always null); otherwise null (never guessed).' properties: id: type: string nullable: true name: type: string nullable: true identity: type: string enum: - verified - compatible - unverified compatible_with: type: string nullable: true website_url: type: string nullable: true lock_url: type: string nullable: true explorer: type: object description: Independent evidence on Solana Explorer. properties: lock_account_url: type: string nullable: true creation_tx_url: type: string nullable: true price_usd: type: number nullable: true description: Token price used for the *_usd fields (null when unknown or guarded as phantom). seconds_until_end: type: integer nullable: true description: Seconds until fully unlocked (>= 0). 0 once completed; null when there is no end date (perpetual) or the lock was cancelled/closed. seconds_until_next_unlock: type: integer nullable: true description: Seconds until next_unlock.at (>= 0); null when there is no next unlock. kind: type: string enum: - lock - vesting description: lock = whole amount at one date; vesting = cliff and/or periodic release status: type: string enum: - active - completed - cancelled - closed description: Derived at request time mint: type: string sender: type: string nullable: true description: Creator / locker (Bonfida has none on-chain) recipient: type: string nullable: true description: 'Beneficiary. sablier_lockup: the CURRENT proven holder of the stream NFT only; null when the holder is not currently proven (see holder_status), never the last proven holder. smithii_vesting: the authority for single-receiver locks, null for merkle receivers (off-chain).' holder_status: type: string nullable: true enum: - current - lost_proof - never_proven - burned - anomaly - null description: 'sablier_lockup only (null for other programs): current = recipient proven by the latest read; lost_proof = recipient unknown, last_proven_holder is evidence; never_proven; burned = stream NFT burned, no recipient; anomaly = fail closed.' last_proven_holder: type: string nullable: true description: 'sablier_lockup only: the last holder a verifying read proved (provenance, NOT the current recipient unless holder_status is current).' holder_proven_at_slot: type: integer nullable: true description: 'sablier_lockup only: slot of the read that last proved last_proven_holder.' name: type: string nullable: true amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true amount_pct_of_supply: type: number nullable: true locked_raw: type: string description: Still locked right now (amount − unlocked-so-far); 0 unless active locked: type: number nullable: true locked_usd: type: number nullable: true locked_pct_of_supply: type: number nullable: true unlocked_raw: type: string unlocked: type: number nullable: true withdrawn_raw: type: string nullable: true description: 'Claimed so far; null when the program does not expose it (withdrawn_tracked false: smithii_vesting)' withdrawn: type: number nullable: true withdrawn_tracked: type: boolean description: false = withdrawn/claimable are unknown (null), not zero claimable_raw: type: string nullable: true description: Unlocked but not yet withdrawn; null when withdrawn is not tracked claimable: type: number nullable: true start_at: type: string format: date-time nullable: true cliff_at: type: string format: date-time nullable: true end_at: type: string format: date-time nullable: true description: Fully unlocked at; null = perpetual/no schedule period_seconds: type: integer nullable: true continuous: type: boolean description: period < 1h (per-second stream) amount_per_period_raw: type: string nullable: true amount_per_period: type: number nullable: true cliff_amount_raw: type: string nullable: true cliff_amount: type: number nullable: true perpetual: type: boolean schedule: type: array description: 'Bonfida token-vesting only (absent on Streamflow / Jupiter Lock): the tranche list, each { release_at, amount_raw } with amount_raw a base-unit string.' items: type: object properties: release_at: type: string format: date-time nullable: true amount_raw: type: string next_unlock: type: object nullable: true properties: at: type: string format: date-time kind: type: string enum: - cliff - period - final - tranche amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true cancelable_by_sender: type: boolean nullable: true description: The locker can cancel — funds are locked against the recipient, not the locker cancelable_by_recipient: type: boolean nullable: true transferable: type: boolean nullable: true can_topup: type: boolean nullable: true cancelled_at: type: string format: date-time nullable: true created_at: type: string format: date-time nullable: true created_at_estimated: type: boolean description: Backfilled row with no on-chain creation time (Jupiter Lock) tx_signature: type: string nullable: true meta: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid mint or query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintLocks x-operation-id-source: derived /tokens/surges: get: tags: - Tokens summary: Token momentum fires — new-token surges + dormant-token revivals description: 'Newest **momentum fires**, newest first. Two kinds: **surge** — a token < 30 min old whose market cap runs hard vs its launch MC (tier ''early'' ≤10 min / ≥$12k / ≥3× launch MC; ''strong'' ≤30 min / ≥$30k / ≥6× and ≥2× the lowest sample of the last 3 min; ''breakout'' ≤2 min / ≥$45k / ≥8×; each tier fires once per mint, and only when SUSTAINED — the level must hold on the current tick and on a sample ≥10 s older, nothing before 20 s of age, so a one-tick same-slot bundle mark is not a surge) — and **revival** — a token with no 1-minute trade candle for ≥24 h that started trading again, CONFIRMED by real buys + buy volume from the tape (≥5 buys, ≥$500 buy volume, ≥1.5× the pre-dormancy MC), never by the price mark alone. Hard gates on both kinds: liquidity ≥ $1.5k and ≥ 2% of MC when known (a thin-pool ''MC'' is an artefact), MC ≤ $100B, and the MC gained must be PAID FOR — buy volume on the tape ≥ 3% × (MC − launch / pre-dormancy MC); a price mark moves MC on ~$0 of volume. Every fire carries the burst tape (buys/sells/volume with ''tape.source'' = candles | wallet_trades | null — the open minute candle lands only at rollover, so a < 90 s-old surge is measured on the live wallet tape where covered; unique buyers where the mint is in token_trades coverage), tracked-KOL involvement, the first-20 early-buyer cohort (bundled / sold / sniper wallets), deployer reputation and ''risk_flags[]'' (bundled_launch, few_buyers, wash_pattern, thin_liquidity, cold_deployer, sniper_heavy, early_buyers_exiting, sell_pressure, no_tape_trades, no_prior_price, mint_authority_active, transfer_fee). Fires ≥65 min old carry ''outcome'' (+1 h MC / peak / low) and ''?stats=1'' prints per-tier hit-rates — out-of-sample by construction. Filters are DB-native: ''kind'', ''tier'', ''mint'', ''launchpad'', ''deployer_tier'', ''min_mc_usd''/''max_mc_usd'', ''min_buys'', ''exclude_flags'' (comma list), ''only_clean=1''. Cursors ''since''/''before'' on fired_at. Pushed live on the **''token:surges''** WebSocket channel and the webhook registry as ''token:surge'' / ''token:revival''. The thresholds are echoed in ''definitions'' so the docs can never drift from what fires. PRO+.' parameters: - name: kind in: query schema: type: string enum: - surge - revival - name: tier in: query schema: type: string enum: - early - strong - breakout description: Surges only (400 with kind=revival) - name: mint in: query schema: type: string - name: since in: query schema: type: string format: date-time description: Only fires after this instant (use pagination.next_since) - name: before in: query schema: type: string format: date-time description: 'Page back: only fires before this instant' - name: min_mc_usd in: query schema: type: number minimum: 0 - name: max_mc_usd in: query schema: type: number minimum: 0 - name: min_buys in: query schema: type: integer minimum: 0 description: Buys on the tape at fire time - name: launchpad in: query schema: type: string description: e.g. pumpfun, launchlab, pumpswap, raydium - name: deployer_tier in: query schema: type: string enum: - elite - good - moderate - rising - cold - unranked - name: exclude_flags in: query schema: type: string description: Comma list of risk flags; rows carrying ANY are dropped. Unknown flag → 400 with known_flags[] - name: only_clean in: query schema: type: string enum: - '1' - '0' - 'true' - 'false' description: Only rows with no risk flags - name: stats in: query schema: type: string enum: - '1' - '0' - 'true' - 'false' description: Add per-(kind,tier) +1h hit-rates - name: days in: query schema: type: integer minimum: 1 maximum: 30 default: 7 description: Stats window - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 responses: '200': description: OK content: application/json: schema: type: object properties: events: type: array items: type: object description: One momentum fire. The same object is the WS/webhook event payload (token:surge / token:revival) plus 'outcome'. properties: id: type: integer kind: type: string enum: - surge - revival tier: type: string nullable: true enum: - early - strong - breakout description: null on revivals mint: type: string symbol: type: string nullable: true name: type: string nullable: true launchpad: type: string nullable: true primary_dex: type: string nullable: true fired_at: type: string format: date-time birth_at: type: string format: date-time nullable: true birth_source: type: string nullable: true enum: - sniper - deployer - first_seen age_seconds: type: integer nullable: true market_cap_usd: type: number liquidity_usd: type: number nullable: true liquidity_to_mc_ratio: type: number nullable: true price_usd: type: number nullable: true baseline_mc_usd: type: number nullable: true description: 'surge: first MC sample after birth' baseline_source: type: string nullable: true enum: - launch - late mc_multiple: type: number nullable: true description: market_cap_usd ÷ baseline (launch baselines only) mc_change_3m_pct: type: number nullable: true dormant_hours: type: number nullable: true description: 'revival: hours without a trade candle' prev_mc_usd: type: number nullable: true description: 'revival: close MC of the last pre-dormancy candle' mc_vs_prev_multiple: type: number nullable: true peak_mc_usd: type: number nullable: true pct_of_peak: type: number nullable: true bonding_progress_pct: type: number nullable: true is_bonded: type: boolean nullable: true tape: type: object properties: since: type: string format: date-time nullable: true available: type: boolean description: false = no tape covers the window yet (candle lag); every count is then null source: type: string nullable: true enum: - candles - wallet_trades - null description: What the burst numbers are measured on; null when available=false buy_sol: type: number nullable: true sell_sol: type: number nullable: true buys: type: integer nullable: true sells: type: integer nullable: true trades: type: integer nullable: true buy_volume_usd: type: number nullable: true sell_volume_usd: type: number nullable: true volume_usd: type: number nullable: true mev_volume_usd: type: number nullable: true unique_buyers: type: integer nullable: true description: null when wallet_data_available=false unique_wallets: type: integer nullable: true trades_per_wallet: type: number nullable: true wallet_data_available: type: boolean kol: type: object nullable: true properties: buyers: type: integer buys: type: integer sells: type: integer names: type: array items: type: string early_buyers: type: object nullable: true properties: count: type: integer bundled: type: integer cohort_sol: type: number sold: type: integer sniper_wallets: type: integer deployer: type: object nullable: true properties: wallet: type: string tier: type: string bonding_rate: type: number nullable: true total_bonded: type: integer nullable: true total_deployed: type: integer nullable: true runner_rate: type: number nullable: true labeled_tokens: type: integer nullable: true recent: type: string nullable: true deployer_wallet: type: string nullable: true deployer_tier: type: string nullable: true mint_authority_revoked: type: boolean nullable: true freeze_authority_revoked: type: boolean nullable: true is_token_2022: type: boolean nullable: true risk_flags: type: array items: type: string enrichment_available: type: boolean detail_url: type: string outcome: type: object nullable: true description: null until the fire is ≥65 min old properties: computed_at: type: string format: date-time mc_usd_1h_after: type: number nullable: true peak_mc_usd_1h_after: type: number nullable: true low_mc_usd_1h_after: type: number nullable: true mc_1h_multiple: type: number nullable: true peak_1h_multiple: type: number nullable: true priced_after_1h: type: boolean description: false = no candle within the hour (token stopped being priced) — not zero pagination: type: object properties: limit: type: integer count: type: integer has_more: type: boolean description: true when the page is full (count === limit); page back with before= next_since: type: string format: date-time nullable: true next_before: type: string format: date-time nullable: true filters: type: object stats: type: object description: 'Only with ?stats=1: { days, note, rows[] { kind, tier, fires, with_outcome, up_1h, up_1h_pct, median_peak_multiple, p75_peak_multiple, median_mc_1h_multiple, doubled_1h, doubled_1h_pct } }' stream: type: object description: WebSocket pointer for the token:surges channel definitions: type: object description: The live thresholds behind every tier / the revival confirmation / each risk flag note: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: invalid_params (unknown exclude_flags → known_flags[]; tier with kind=revival) headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensSurges x-operation-id-source: derived /tokens/locks: get: tags: - Tokens summary: Newest token locks & vesting contracts (all mints) description: 'Cross-token feed of **new lock / vesting contracts** — who just locked tokens, of what mint, how much, until when — newest first, from Streamflow, Jupiter Lock and Bonfida vesting. Poll with ''since='' (cursor = ''pagination.next_since'') or subscribe to the **''token:locks''** WebSocket channel for a push the moment the contract lands on-chain. Filters: ''mint'', ''sender'', ''recipient'', ''program'', ''kind'', ''status'', ''min_usd'', ''min_pct_of_supply'' (the last three are post-filters served by a bounded scan — see ''pagination.scan_truncated''). Backfilled Jupiter Lock rows have no on-chain creation time and are excluded by default (''include_estimated=1'' to include). PRO+.' parameters: - name: since in: query schema: type: string format: date-time description: Only contracts created after this instant (use pagination.next_since) - name: before in: query schema: type: string format: date-time description: 'LEGACY page back: only contracts created before this instant (strict — skips same-timestamp siblings; prefer cursor)' - name: cursor in: query schema: type: string description: 'PREFERRED page back: pagination.next_cursor from the previous page — a strict (created_at, id) keyset that never skips or repeats a row' - name: mint in: query schema: type: string - name: sender in: query schema: type: string - name: recipient in: query schema: type: string - name: program in: query schema: type: string enum: - streamflow - jupiter_lock - bonfida_vesting - smithii_vesting - sablier_lockup - name: kind in: query schema: type: string enum: - lock - vesting - name: status in: query schema: type: string enum: - active - completed - cancelled - closed - name: min_usd in: query schema: type: number minimum: 0 description: Deposited amount ≥ (needs a known price) - name: min_pct_of_supply in: query schema: type: number minimum: 0 maximum: 100 - name: include_estimated in: query schema: type: string enum: - '1' - '0' - 'true' - 'false' - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 responses: '200': description: OK content: application/json: schema: type: object properties: locks: type: array items: allOf: - type: object description: One on-chain lock/vesting contract with a live-derived view. raw = base units (digit string); ui/usd/pct fields are null when decimals/price are unknown. properties: lock_account: type: string description: The contract account (Streamflow stream / Jupiter VestingEscrow / Bonfida vesting account) program: type: string enum: - streamflow - jupiter_lock - bonfida_vesting - smithii_vesting - sablier_lockup provider: type: object description: 'Who runs the lock contract, and how sure we are. identity: verified = a known provider deployment (by contract address); compatible = only the event shape (ABI) matches a known provider, the operator is NOT identified; unverified = unknown. id/website_url are null unless verified. lock_url is a per-lock page on the provider''s site, emitted ONLY where the URL format is proven (none is proven for Streamflow / Jupiter Lock / Bonfida today, so it is always null); otherwise null (never guessed).' properties: id: type: string nullable: true name: type: string nullable: true identity: type: string enum: - verified - compatible - unverified compatible_with: type: string nullable: true website_url: type: string nullable: true lock_url: type: string nullable: true explorer: type: object description: Independent evidence on Solana Explorer. properties: lock_account_url: type: string nullable: true creation_tx_url: type: string nullable: true price_usd: type: number nullable: true description: Token price used for the *_usd fields (null when unknown or guarded as phantom). seconds_until_end: type: integer nullable: true description: Seconds until fully unlocked (>= 0). 0 once completed; null when there is no end date (perpetual) or the lock was cancelled/closed. seconds_until_next_unlock: type: integer nullable: true description: Seconds until next_unlock.at (>= 0); null when there is no next unlock. kind: type: string enum: - lock - vesting description: lock = whole amount at one date; vesting = cliff and/or periodic release status: type: string enum: - active - completed - cancelled - closed description: Derived at request time mint: type: string sender: type: string nullable: true description: Creator / locker (Bonfida has none on-chain) recipient: type: string nullable: true description: 'Beneficiary. sablier_lockup: the CURRENT proven holder of the stream NFT only; null when the holder is not currently proven (see holder_status), never the last proven holder. smithii_vesting: the authority for single-receiver locks, null for merkle receivers (off-chain).' holder_status: type: string nullable: true enum: - current - lost_proof - never_proven - burned - anomaly - null description: 'sablier_lockup only (null for other programs): current = recipient proven by the latest read; lost_proof = recipient unknown, last_proven_holder is evidence; never_proven; burned = stream NFT burned, no recipient; anomaly = fail closed.' last_proven_holder: type: string nullable: true description: 'sablier_lockup only: the last holder a verifying read proved (provenance, NOT the current recipient unless holder_status is current).' holder_proven_at_slot: type: integer nullable: true description: 'sablier_lockup only: slot of the read that last proved last_proven_holder.' name: type: string nullable: true amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true amount_pct_of_supply: type: number nullable: true locked_raw: type: string description: Still locked right now (amount − unlocked-so-far); 0 unless active locked: type: number nullable: true locked_usd: type: number nullable: true locked_pct_of_supply: type: number nullable: true unlocked_raw: type: string unlocked: type: number nullable: true withdrawn_raw: type: string nullable: true description: 'Claimed so far; null when the program does not expose it (withdrawn_tracked false: smithii_vesting)' withdrawn: type: number nullable: true withdrawn_tracked: type: boolean description: false = withdrawn/claimable are unknown (null), not zero claimable_raw: type: string nullable: true description: Unlocked but not yet withdrawn; null when withdrawn is not tracked claimable: type: number nullable: true start_at: type: string format: date-time nullable: true cliff_at: type: string format: date-time nullable: true end_at: type: string format: date-time nullable: true description: Fully unlocked at; null = perpetual/no schedule period_seconds: type: integer nullable: true continuous: type: boolean description: period < 1h (per-second stream) amount_per_period_raw: type: string nullable: true amount_per_period: type: number nullable: true cliff_amount_raw: type: string nullable: true cliff_amount: type: number nullable: true perpetual: type: boolean schedule: type: array description: 'Bonfida token-vesting only (absent on Streamflow / Jupiter Lock): the tranche list, each { release_at, amount_raw } with amount_raw a base-unit string.' items: type: object properties: release_at: type: string format: date-time nullable: true amount_raw: type: string next_unlock: type: object nullable: true properties: at: type: string format: date-time kind: type: string enum: - cliff - period - final - tranche amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true cancelable_by_sender: type: boolean nullable: true description: The locker can cancel — funds are locked against the recipient, not the locker cancelable_by_recipient: type: boolean nullable: true transferable: type: boolean nullable: true can_topup: type: boolean nullable: true cancelled_at: type: string format: date-time nullable: true created_at: type: string format: date-time nullable: true created_at_estimated: type: boolean description: Backfilled row with no on-chain creation time (Jupiter Lock) tx_signature: type: string nullable: true - type: object properties: token: type: object properties: symbol: type: string nullable: true name: type: string nullable: true decimals: type: integer nullable: true price_usd: type: number nullable: true market_cap_usd: type: number nullable: true pagination: type: object properties: limit: type: integer count: type: integer has_more: type: boolean description: false ONLY when the feed ran out — never inferred from a short post-filtered page next_since: type: string format: date-time nullable: true next_before: type: string format: date-time nullable: true description: LEGACY — prefer next_cursor next_cursor: type: string nullable: true description: Pass as cursor= to page back; points past the last row CONSUMED, so a filtered page that matched nothing still advances. null = end of feed post_filtered: type: boolean scanned: type: integer description: Candidate rows examined scan_truncated: type: boolean description: The per-request scan budget ran out before `limit` matches — more matches MAY exist past the cursor/offset scan_budget: type: integer degraded_fields: type: array items: type: string description: mint_facts: — a per-mint enrichment read (decimals/price/supply) failed; the affected usd/ui/pct values are null (unknown) and the min_usd / min_pct_of_supply post-filters could not be applied to those rows stream: type: object description: WebSocket pointer for the token:locks channel meta: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensLocks x-operation-id-source: derived /tokens/unlocks: get: tags: - Tokens summary: Upcoming token unlocks (cliffs, periodic releases, final unlocks) description: Upcoming **unlock events** across all active lock / vesting contracts inside a window — cliffs, periodic releases (hourly or coarser) and final unlocks — i.e. *which tokens have locked supply hitting the market this week, how much, from whose lock*. One entry per active contract = its **next** unlock event in the window ('event' = cliff | period | final | tranche), plus 'window_amount_*' = that contract's total release over the whole window. Continuous per-second streams (Streamflow payroll) contribute only their cliff/final events. Sort by soonest (default), largest_usd or largest_pct; filter by mint / program / kind / min_usd / min_pct_of_supply. PRO+. parameters: - name: within in: query schema: type: string enum: - 1h - 6h - 24h - 3d - 7d - 14d - 30d - 90d default: 7d - name: mint in: query schema: type: string - name: program in: query schema: type: string enum: - streamflow - jupiter_lock - bonfida_vesting - smithii_vesting - sablier_lockup - name: kind in: query schema: type: string enum: - lock - vesting - name: min_usd in: query schema: type: number minimum: 0 description: Next-event amount ≥ (needs a known price) - name: min_pct_of_supply in: query schema: type: number minimum: 0 maximum: 100 - name: sort in: query schema: type: string enum: - soonest - largest_usd - largest_pct default: soonest - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 responses: '200': description: OK content: application/json: schema: type: object properties: window: type: object properties: within: type: string from: type: string format: date-time to: type: string format: date-time unlocks: type: array items: type: object properties: unlock_at: type: string format: date-time in_seconds: type: integer event: type: string enum: - cliff - period - final - tranche amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true amount_pct_of_supply: type: number nullable: true window_amount_raw: type: string window_amount: type: number nullable: true window_amount_usd: type: number nullable: true window_amount_pct_of_supply: type: number nullable: true mint: type: string token: type: object properties: symbol: type: string nullable: true name: type: string nullable: true decimals: type: integer nullable: true price_usd: type: number nullable: true market_cap_usd: type: number nullable: true lock: type: object description: The contract this event belongs to (subset of the /tokens/{mint}/locks row) pagination: type: object properties: limit: type: integer count: type: integer total_in_window: type: integer has_more: type: boolean meta: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensUnlocks x-operation-id-source: derived /tokens/{mint}/fee-shares: get: tags: - Tokens summary: pump.fun creator-fee sharing on a mint (who the fees are redirected to) description: 'The on-chain **SharingConfig** of a pump.fun coin — who receives what share (bps) of its creator fees — decoded from the pump_fees program account (PDA [''sharing-config'', mint]): admin, status, each shareholder with is_admin / is_social_pda (fees earmarked for a platform identity such as an X account, with the identity''s lifetime claimed), redirected_bps (share going to non-admin addresses) and social_bps. Plus the **distribution history** (every distribute_creator_fees payout, pro-rata per shareholder), what each recipient has received, past recipients no longer in the split, and the config change log (created / updated / reset / creator transferred). Config comes from our stream table when the mint has ever had a non-default split, otherwise it is read live from chain — so is_default:true (100% to the creator) is a real answer. Event history starts 2026-08-17. PRO+.' parameters: - name: mint in: path required: true schema: type: string example: E2rQLGJxb1pq4u4AoXSAmqTbspupMXfgfbJsXU5npump responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string config: type: object nullable: true properties: sharing_config: type: string admin: type: string nullable: true admin_revoked: type: boolean nullable: true status: type: string nullable: true version: type: integer nullable: true is_default: type: boolean nullable: true description: 100% to the admin — no redirect redirected_bps: type: integer redirected_pct: type: number social_bps: type: integer social_pct: type: number shareholders: type: array items: type: object properties: address: type: string share_bps: type: integer nullable: true share_pct: type: number nullable: true is_admin: type: boolean description: The config admin (normally the coin creator) is_social_pda: type: boolean description: Address is a pump_fees SocialFeePda — fees earmarked for a platform identity social: type: object nullable: true properties: platform: type: integer platform_label: type: string nullable: true description: 2 = x; others platform_ until observed user_id: type: string description: Platform-native numeric id, not the handle lifetime_claimed_raw: type: string lifetime_claimed: type: number nullable: true lifetime_claimed_usd: type: number nullable: true last_claimed_at: type: string format: date-time nullable: true received_raw: type: string received: type: number nullable: true received_usd: type: number nullable: true payout_count: type: integer last_payout_at: type: string format: date-time nullable: true source: type: string enum: - stream - chain updated_at: type: string format: date-time nullable: true config_pda: type: string config_error: type: string nullable: true description: Set when the live read failed on every RPC endpoint (config is then null) quote: type: object properties: symbol: type: string decimals: type: integer sol_usd: type: number nullable: true distributions: type: object properties: count: type: integer total_raw: type: string total: type: number nullable: true total_usd: type: number nullable: true last_at: type: string format: date-time nullable: true recipients: type: array items: type: object properties: address: type: string share_bps: type: integer nullable: true share_pct: type: number nullable: true is_admin: type: boolean description: The config admin (normally the coin creator) is_social_pda: type: boolean description: Address is a pump_fees SocialFeePda — fees earmarked for a platform identity social: type: object nullable: true properties: platform: type: integer platform_label: type: string nullable: true description: 2 = x; others platform_ until observed user_id: type: string description: Platform-native numeric id, not the handle lifetime_claimed_raw: type: string lifetime_claimed: type: number nullable: true lifetime_claimed_usd: type: number nullable: true last_claimed_at: type: string format: date-time nullable: true received_raw: type: string received: type: number nullable: true received_usd: type: number nullable: true payout_count: type: integer last_payout_at: type: string format: date-time nullable: true past_recipients: type: array items: type: object properties: address: type: string share_bps: type: integer nullable: true share_pct: type: number nullable: true is_admin: type: boolean description: The config admin (normally the coin creator) is_social_pda: type: boolean description: Address is a pump_fees SocialFeePda — fees earmarked for a platform identity social: type: object nullable: true properties: platform: type: integer platform_label: type: string nullable: true description: 2 = x; others platform_ until observed user_id: type: string description: Platform-native numeric id, not the handle lifetime_claimed_raw: type: string lifetime_claimed: type: number nullable: true lifetime_claimed_usd: type: number nullable: true last_claimed_at: type: string format: date-time nullable: true received_raw: type: string received: type: number nullable: true received_usd: type: number nullable: true payout_count: type: integer last_payout_at: type: string format: date-time nullable: true payouts_considered: type: integer payouts_truncated: type: boolean history: type: array description: Config changes + creator transfers, newest first items: type: object recent_distributions: type: array items: type: object properties: at: type: string format: date-time tx_signature: type: string amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true shareholders: type: array items: type: object actor: type: string nullable: true meta: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid mint headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintFeeShares x-operation-id-source: derived /tokens/fee-claims: get: tags: - Tokens summary: pump.fun fee-claim feed (distributions to shareholders, social-handle claims… description: 'Every decoded pump.fun fee event, newest first: **distribution** (creator fees paid out pro-rata to the SharingConfig shareholders — fees redirected to others — with payouts[] per address), **social_claim** (fees earmarked for a platform identity — platform 2 = X, user_id = the platform-native numeric id — claimed to a recipient wallet), **shares_created / shares_updated / shares_reset** (config changes), **creator_transferred** (creator role moved), **creator_claim** (the plain creator vault claim — per creator, no mint; excluded unless requested), and **holder_distribution** (pump.fun DistributeFeeToHolders — creator fees airdropped pro-rata to the coin''s token holders; amount = the total, payload.recipients = holder count; excluded unless requested, not pushed on WS; history from 2026-09-13 is retained, archive policy pending). Default 100%-to-creator configs and zero-amount distributions are not stored. Poll with since= (cursor = pagination.next_since) or subscribe to WS channel token:fee_claims. History starts 2026-08-17. PRO+.' parameters: - name: type in: query schema: type: string description: 'Comma list of event types (default: all except creator_claim and holder_distribution)' - name: mint in: query schema: type: string - name: recipient in: query schema: type: string description: Payout / claim recipient wallet, or new creator - name: actor in: query schema: type: string description: Transaction signer - name: social_platform in: query schema: type: integer description: Raw platform id (2 = X) - name: social_user_id in: query schema: type: string description: Platform-native numeric user id - name: min_sol in: query schema: type: number minimum: 0 description: Amount floor in SOL - name: since in: query schema: type: string format: date-time - name: before in: query schema: type: string format: date-time - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 50 responses: '200': description: OK content: application/json: schema: type: object properties: events: type: array items: type: object properties: id: type: integer type: type: string enum: - shares_created - shares_updated - shares_reset - distribution - social_pda_created - social_claim - creator_transferred - creator_claim - holder_distribution at: type: string format: date-time tx_signature: type: string slot: type: integer nullable: true mint: type: string nullable: true description: NULL for social claims and creator vault claims (per identity / per creator) admin: type: string nullable: true actor: type: string nullable: true description: Transaction signer recipient: type: string nullable: true amount_raw: type: string nullable: true description: Quote base units (SOL lamports unless a stable-quoted coin) amount: type: number nullable: true amount_usd: type: number nullable: true quote: type: string social: type: object nullable: true properties: platform: type: integer platform_label: type: string nullable: true user_id: type: string pda: type: string nullable: true shareholders: type: array nullable: true items: type: object properties: address: type: string share_bps: type: integer payouts: type: array nullable: true description: 'distribution only: pro-rata amount per shareholder' items: type: object properties: address: type: string share_bps: type: integer amount_raw: type: string amount: type: number nullable: true amount_usd: type: number nullable: true payload: type: object nullable: true description: Full decoded Anchor event pagination: type: object properties: limit: type: integer count: type: integer has_more: type: boolean next_since: type: string format: date-time nullable: true next_before: type: string format: date-time nullable: true stream: type: object description: WebSocket pointer for token:fee_claims meta: type: object headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid query parameters headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensFeeClaims x-operation-id-source: derived /tokens/{mint}/depth: get: tags: - Tokens summary: Token depth / price impact (per pool) description: 'Per-pool **price-impact / slippage** — "how much SOL to move price N%" and the impact + tokens received for each buy size. For every pool: `spot_price_sol`, `quotes[]` per requested SOL size (`tokens_out`, `avg_price_sol`, `price_impact_pct`), and `to_move_price` (SOL to move price 1%/5%/10%). Exact for constant-product AMMs (PumpSwap, Raydium v4/CPMM, Meteora DAMM — served from streamed reserves, `source: "stream"`); pump.fun / bonk bonding curves priced from one live read of the curve''s virtual reserves (`source: "live_rpc"`). Impact is per-pool, not router-optimal; concentrated-liquidity pools (Raydium CLMM, Orca, Meteora DLMM) and unclassified pools return under `unsupported_pools[]` with a `reason` instead of a wrong number. PRO+.' parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump - name: sizes in: query required: false schema: type: string example: 0.5,1,5,10 description: Comma-separated SOL buy sizes to quote (max 8, each >0 and ≤10000). Default 0.5,1,5,10. responses: '200': description: OK content: application/json: schema: type: object properties: mint: type: string found: type: boolean description: false = no depth-computable pool (pools may still appear in unsupported_pools) sol_usd: type: number nullable: true sizes_sol: type: array items: type: number primary_pool: type: string nullable: true description: Deepest depth-available pool pools: type: array items: type: object properties: pool_address: type: string dex: type: string quote_mint: type: string pool_model: type: string description: constant_product | curve liquidity_usd: type: number nullable: true is_active: type: boolean depth_available: type: boolean model: type: string fee_pct: type: number source: type: string description: stream (DB reserves) | live_rpc (curve virtual reserves) reserves_age_ms: type: number spot_price_sol: type: number quotes: type: array items: type: object properties: size_sol: type: number tokens_out: type: number avg_price_sol: type: number price_impact_pct: type: number to_move_price: type: object properties: 1pct: type: number description: SOL to move price +1% 5pct: type: number 10pct: type: number universe: type: object description: Only the deepest max_pools pools by stored liquidity are evaluated (audit F19); pools past that cut are not listed. truncated=true = the cut dropped pools. properties: kind: type: string enum: - top_by_liquidity max_pools: type: integer evaluated: type: integer truncated: type: boolean unsupported_pools: type: array items: type: object properties: pool_address: type: string dex: type: string quote_mint: type: string pool_model: type: string nullable: true liquidity_usd: type: number nullable: true is_active: type: boolean reason: type: string description: e.g. concentrated_liquidity_depth_not_supported, pool_model_unknown, curve_graduated_use_amm_pool, reserves_unavailable note: type: string headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid sizes parameter headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintDepth x-operation-id-source: derived /tokens/batch/risk: post: tags: - Tokens summary: Batch token risk scoring (up to 50 mints) description: 'Same loader and per-mint shape as `GET /tokens/{mint}/risk` (score v2, assessment, dev block, coverage) for up to 50 mints — identical evidence gives identical scores. Documented difference: no live dev-holdings RPC per mint, so `dev.holdings_*` are null and `dev.transfer_status` is `unknown` (the dev block never moves the score). Untracked mints come back as `{ mint, error: "not_tracked" }`, and mints whose score-critical inputs could not be read as `{ mint, error: "unavailable", retryable: true }` — never a partial score. PRO+.' requestBody: required: true content: application/json: schema: type: object required: - mints properties: mints: type: array items: type: string minItems: 1 maxItems: 50 responses: '200': description: OK content: application/json: schema: type: object properties: coverage: $ref: '#/components/schemas/LaunchCoverage' tokens: type: array items: type: object properties: mint: type: string risk_score: type: integer minimum: 0 maximum: 100 band: type: string enum: - safe - caution - danger description: Never `safe` while assessment.status is `incomplete`, or with an un-revoked mint/freeze authority score_version: type: string enum: - v2 description: 'v2 (2026-09-21): a token-supply burn no longer earns the LP-burn credit (v1: burn_detected=true → 0, every other token → 8; v2: every token without verified LP evidence → 8, burned or not — no burn penalty); unknown transfer fee is warn(4) not "No transfer fee"; deployer reputation scores only an established history; unknown inputs keep the band off `safe`.' factors: type: array items: type: object properties: key: type: string label: type: string status: type: string enum: - ok - warn - danger - unknown - not_assessed description: ok/warn/danger = evidence read and scored. unknown = the input should exist but could not be read or is insufficient (0 points; listed in assessment.unknown_inputs; band never safe). not_assessed = no evidence source applies to this token (0 points; listed in assessment.not_assessed). Neither is ever positive evidence. points: type: integer detail: type: string inputs: type: object description: Every scorer input. Enrichment fields are null when unobserved — unknown, not false/0. properties: mint_authority_revoked: type: boolean nullable: true freeze_authority_revoked: type: boolean nullable: true liquidity_usd: type: number nullable: true liquidity_to_mc_ratio: type: number nullable: true transfer_fee_bps: type: integer nullable: true is_token_2022: type: boolean nullable: true token_supply_burn_detected: type: boolean nullable: true description: The mint's supply decreased (token burn). Not LP evidence. burn_detected: type: boolean nullable: true description: DEPRECATED alias of token_supply_burn_detected lp_burn_status: type: string enum: - verified - not_verified - unknown description: 'LP burn/lock evidence: `verified` only with measured LP-token custody (lp_burnt_pct ≥ 99). No Solana writer populates LP custody yet, so today every token is `unknown`. A token-supply burn is never promoted to an LP claim.' launch_cohort_sol: type: number nullable: true launch_cohort_size: type: integer nullable: true sniper_footprint: type: object nullable: true deployer_bonding_rate: type: number nullable: true deployer_total_deployed: type: integer nullable: true deployer_history_status: type: string nullable: true enum: - new_in_our_index - limited_history - reputation_pending - established - null description: null when there is no deployer deployer_reputation_scored: type: boolean description: Whether the bonding rate moved the score (established history only) liquidity_observed_at: type: string format: date-time nullable: true description: When the liquidity figure was last observed (mc-tracker last trade). With the creator not assessed, band safe requires this within the last 6 h (null or more than 5 min in the future = not fresh). 6 h is a policy threshold, not proof that the liquidity is still there. kol_signal: type: string nullable: true is_blacklisted: type: boolean nullable: true supply_inflation_pct: type: number nullable: true assessment: type: object properties: status: type: string enum: - complete - incomplete description: incomplete = at least one score input is unknown; the score is a lower bound unknown_inputs: type: array items: type: string description: Inputs not observed for this token (e.g. transfer_fee, mint_authority, liquidity, kol_activity, deployer_reputation, blacklist, supply_inflation, launch_cohort). deployer_reputation is unknown when a creator lookup failed, when a LAUNCHPAD mint's creator was not recorded (a coverage gap), or when a known creator has no established history. explanations: type: object additionalProperties: type: string description: Why each unknown / not-assessed input is listed, keyed by input name, plus band_cap when the band was capped (creator not assessed and liquidity / liquidity-to-MC not both ok, or the liquidity figure older than 6 h — a policy threshold, not proof of safety). Missing required creator information never yields band safe, for any token. not_assessed: type: array items: type: string description: 'Dimensions not measured for this token that are NOT a failed lookup: lp_custody (never observed for Solana pools) and deployer_reputation only when the token has no launchpad origin in our capture (no deploy row, no bonding-curve pool, not PumpSwap-primary, no pump/bonk/BAGS vanity suffix). A launchpad mint with an unrecorded creator, or a failed origin lookup, is an unknown input instead.' dev_status: type: string enum: - ok - not_found - unavailable description: ok = dev block present · not_found = no pending_deploys row (not a launchpad deploy we saw) · unavailable = the lookup failed dev: type: object nullable: true description: Dev-wallet activity (migration 225). Trade-derived fields are accurate THROUGH activity_rollup_through; holdings are observed at holdings_observed_at — never at as_of. properties: wallet: type: string nullable: true launchpad: type: string nullable: true deployed_at: type: string format: date-time nullable: true buy_sol: type: number nullable: true buy_tokens: type: number nullable: true buy_supply_pct: type: number nullable: true bought_tokens_after: type: number nullable: true sold_tokens: type: number nullable: true sold_sol: type: number nullable: true first_sell_at: type: string format: date-time nullable: true last_sell_at: type: string format: date-time nullable: true holdings_tokens: type: number nullable: true description: Live on-chain balance (60 s cache); null = RPC unavailable or not checked (batch) holdings_supply_pct: type: number nullable: true wallet_empty: type: boolean nullable: true holdings_observed_at: type: string format: date-time nullable: true description: When the RPC balance was observed (cache fetch time) activity_rollup_through: type: string format: date-time nullable: true description: Dev trades are rolled up through this time (dev_activity cursor) activity_rollup_ran_at: type: string format: date-time nullable: true expected_tokens_from_trades: type: number nullable: true description: Balance the dev's own recorded trades explain, aligned to holdings_observed_at transfer_status: type: string enum: - none_detected - suspected - unknown description: suspected = balance below what recorded trades explain (a sell on an uncaptured venue looks identical) — never a verified transfer; unknown = observations misaligned, rollup stale, no coverage, or holdings not checked transfer_reason: type: string transferred_out: type: boolean nullable: true description: 'DEPRECATED boolean view of transfer_status: true = suspected, false = none_detected, null = unknown' coverage: $ref: '#/components/schemas/TradeCoverage' as_of: type: string format: date-time description: Response time — not the observation time of any input error: type: string enum: - not_tracked - unavailable - error description: 'Present instead of a score: not_tracked = no snapshot data; unavailable = a score-critical input could not be read (retryable: true, unavailable_inputs lists them) — never a partial score; error = the per-mint compute failed (retryable when it was a statement timeout)' code: type: string enum: - risk_inputs_unavailable unavailable_inputs: type: array items: type: string retryable: type: boolean count: type: integer headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid body headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: postTokensBatchRisk x-operation-id-source: derived /tokens/{mint}/kol-consensus: get: tags: - Tokens summary: KOL consensus snapshot for one token description: Aggregated KOL consensus view for a token mint — how many KOLs are accumulating vs distributing, net SOL flow, conviction score, and per-KOL stance breakdown. parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address responses: '200': description: OK content: application/json: schema: type: object description: 'When the mint has no kol_trades rows, the response is instead `{ mint, total_kol_buyers: 0, total_kol_sellers: 0, consensus: null }` (no current_mc_usd/current_price_usd).' properties: mint: type: string current_mc_usd: type: number nullable: true current_price_usd: type: number nullable: true total_kol_buyers: type: integer description: Only present on the no-activity shape; otherwise nested under consensus.total_kol_buyers total_kol_sellers: type: integer description: Only present on the no-activity shape; otherwise nested under consensus.total_kol_sellers complete: type: boolean description: Only present (true) on the no-activity shape; otherwise nested under consensus.complete consensus: type: object nullable: true properties: total_kol_buyers: type: integer total_kol_sellers: type: integer kol_exit_rate: type: number description: Fraction (0-1) of KOL buyers with ≥1 recorded SELL of this token (any size, any order) — NOT a full position exit; kept under its historical name. Same value as kol_any_sell_rate kol_any_sell_rate: type: number description: Accurately named copy of kol_exit_rate kol_exit_rate_definition: type: string complete: type: boolean description: false when the trade tape behind every number hit the 50,000-row paging ceiling (the OLDEST rows_scanned trades are included, later ones are not) truncated: type: boolean rows_scanned: type: integer net_flow_sol: type: number total_buy_sol: type: number total_sell_sol: type: number first_kol_buy_at: type: string format: date-time nullable: true last_kol_buy_at: type: string format: date-time nullable: true first_touch_wallet: type: string nullable: true first_touch_at: type: string format: date-time nullable: true median_entry_mc_usd: type: number nullable: true total_trades: type: integer buyers: type: array items: type: string description: ULTRA only — every distinct KOL buyer wallet exited: type: array items: type: string description: ULTRA only — KOL buyer wallets that also sold headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid mint headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: Token not seen by KOL roster headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintKolConsensus x-operation-id-source: derived /tokens/{mint}/peak-history: get: tags: - Tokens summary: Peak market cap history for one token description: Historical peak MC snapshots for a token — ATH market cap, timestamps of each peak, drawdown from ATH, and recovery windows. Useful for gauging whether a token has already peaked or is re-approaching highs. parameters: - name: mint in: path required: true schema: type: string example: FfuWjhMm5LP3anZ5zq8SG4iqzYVvhM4LoyNMM957pump description: Solana token mint address responses: '200': description: OK content: application/json: schema: type: object description: 'found: false shape is `{ mint, found: false, peak_history: null }` (no token block).' properties: mint: type: string found: type: boolean token: type: object nullable: true properties: name: type: string nullable: true symbol: type: string nullable: true image_url: type: string nullable: true peak_history: type: object nullable: true properties: peak_mc_usd: type: number nullable: true peak_mc_updated_at: type: string format: date-time nullable: true current_mc_usd: type: number nullable: true current_price_usd: type: number nullable: true decline_from_peak_pct: type: number nullable: true description: (current − peak) / peak × 100, negative when below peak mc_at_bond: type: number nullable: true mc_1h_after_bond: type: number nullable: true mc_6h_after_bond: type: number nullable: true mc_24h_after_bond: type: number nullable: true mc_7d_after_bond: type: number nullable: true still_alive_1h: type: boolean nullable: true mc_tracking_complete: type: boolean nullable: true deployed_at: type: string format: date-time nullable: true bonded_at: type: string format: date-time nullable: true time_to_bond_minutes: type: number nullable: true headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' X-Data-Delay: $ref: '#/components/headers/XDataDelay' X-MadeOnSol-Quota-Warning: $ref: '#/components/headers/XMadeOnSolQuotaWarning' '400': description: Invalid mint headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/TierForbidden' '404': description: Token not tracked headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/AuthBackendUnavailable' x-madeonsol-tier: PRO x-badges: - name: PRO position: after color: '#1d4ed8' operationId: getTokensByMintPeakHistory x-operation-id-source: derived components: headers: XRateLimitReset: description: When the window resets, as a Unix epoch in SECONDS (next 00:00 UTC for the daily quota; about 60 s ahead for a per-minute bucket). schema: type: integer example: 1791158400 XRateLimitRemaining: description: Calls left in that window. `0` on a quota 429. schema: type: integer minimum: 0 example: 9412 XDataDelay: description: 'Free-tier only: the feed in this body is delayed by this many seconds (same value as the body''s `delay_seconds`). Absent on real-time responses.' schema: type: integer minimum: 1 example: 300 XRateLimitLimit: description: 'Size of the rate-limit window this call was counted against: the daily quota for your tier on data endpoints, the per-minute bucket on `/stream/token` (10) and `/stream/sessions` (30), the hourly allowance for the public demo key. Sent once the key has been authenticated and rate-limited, so it is absent on authentication-stage errors.' schema: type: integer minimum: 0 example: 10000 XMadeOnSolQuotaWarning: description: 'Free-tier only, past about 70 % of the daily quota: a human-readable usage line with an upgrade link.' schema: type: string example: '150/200 used - upgrade for 50x higher limits: https://madeonsol.com/checkout?tier=PRO' XRequestId: description: Unique id of this response. On responses built after authentication it is also the `request_id` of the audit-log row for the call, so quote it in a support request. Present on every response of the keyed API. required: true schema: type: string format: uuid example: 3f6c1d2e-8a4b-4f7e-9c21-5b0d7e6a9f13 XRateLimitUsed: description: Calls used in that window (`limit − remaining`). schema: type: integer minimum: 0 example: 588 RetryAfter: description: Seconds to wait before retrying (RFC 9110 delay-seconds, at least 1). Sent on every 429 (quota, concurrency, authentication-failure) and every 503; when the body carries `retry_after_seconds` it is the same value. required: true schema: type: integer minimum: 1 example: 10 schemas: DemoKeyNotAllowedError: type: object description: 403 for the public demo key (`msk_demo_…`) outside its three-endpoint allow-list. `endpoint_tier` is the endpoint's real minimum plan (null when unknown). required: - error - message - allowed_endpoints properties: error: type: string enum: - demo_key_endpoint_not_allowed message: type: string endpoint_tier: type: string nullable: true enum: - FREE - PRO - ULTRA - BUSINESS - ENTERPRISE - null allowed_endpoints: type: array items: type: string signup_url: type: string format: uri upgrade_url: type: string format: uri description: Only when the endpoint's tier can be bought at /checkout. TokenBundle: type: object properties: mint: type: string bundle: type: object properties: wallet_count: type: integer description: 'Number of same-slot bundle wallets (is_bundle: ≥3 buying in one slot)' bundle_kind: type: string enum: - atomic_tx - same_slot - none description: atomic_tx = ≥3 wallets share one buy tx (definitively atomic); same_slot = the ≥3-same-slot heuristic (possible coincidence); none = no cohort held_pct_of_supply: type: number nullable: true description: HEADLINE. Cohort net held ÷ circulating supply, 0–1 (clamped). null when supply is unknown held_ratio: type: number nullable: true description: Cohort net held ÷ buy_volume, 0–1. Churn-sensitive — secondary to held_pct_of_supply fully_exited: type: boolean buy_volume: type: number description: Cumulative buy-side token volume — NOT distinct tokens; can exceed supply on round-tripping tokens_held: type: number description: Swap-derived net position (Σbuys−Σsells) — a proxy for on-chain balance, not a holder census wallets: type: array description: 'BASIC: empty. PRO: top-10, flags only. ULTRA: full cohort + identity fields.' items: type: object properties: rank: type: integer wallet: type: string held_ratio: type: number nullable: true has_sold: type: boolean atomic: type: boolean description: This wallet is part of an atomic single-tx bundle is_kol: type: boolean kol_name: type: string nullable: true description: ULTRA only win_rate: type: number nullable: true description: ULTRA only, 0–1 bot_confidence: type: string nullable: true description: ULTRA only tokens_held: type: number description: ULTRA only coverage: $ref: '#/components/schemas/TradeCoverage' ConcurrencyLimitError: type: object description: '429 from the heavy-query limiter: too many heavy requests in flight for this key (`concurrency`) or for the whole server (`capacity`). Clears as soon as in-flight requests finish.' required: - error - error_kind - retry_after_seconds properties: error: type: string error_kind: type: string enum: - concurrency - capacity retry_after_seconds: type: number max_concurrent: type: integer support: type: string _rid: type: string TierRequiredError: type: object description: 403 when the key's tier is below the endpoint's (or a parameter's) gate. `required_tier` matches the operation's `x-madeonsol-tier` or `x-madeonsol-feature-tiers`. ENTERPRISE gates return `contact` instead of `upgrade`. required: - error - message - your_tier - required_tier properties: error: type: string enum: - tier_required message: type: string feature: type: string your_tier: type: string enum: - BASIC - PRO - ULTRA - BUSINESS - ENTERPRISE required_tier: type: string enum: - PRO - ULTRA - BUSINESS - ENTERPRISE upgrade: type: object description: The plan that lifts this limit, with price and a checkout link. properties: tier: type: string enum: - PRO - ULTRA - BUSINESS - ENTERPRISE price_eur_per_month: type: number price_usdc_per_month: type: number daily_limit: type: integer burst_limit: type: integer pitch: type: string url: type: string upgrade_url: type: string format: uri reactivate: type: boolean description: Present (true) when the key's paid subscription has just lapsed and `tier` is the plan it held. prior_tier: type: string enum: - PRO - ULTRA - BUSINESS contact: type: object properties: url: type: string contact_url: type: string pitch: type: string support: type: string _rid: type: string BuyerQualityBatchItem: type: object description: 'Per-mint item of POST /tokens/batch/buyer-quality: the GET /tokens/{mint}/buyer-quality shape WITHOUT `signal_stats` (the batch path does not attach the signal-performance snapshot).' properties: mint: type: string score: type: number description: 0-100 composite buyer-quality score confidence: type: string enum: - insufficient_data - low - medium - high description: 'Confidence in the PREDICTIVE inputs: counts only buyers whose win rate fed the score (≥3 tokens of history, not bot-flagged, not zero-cost-dump dominated). insufficient_data = none did — the score is the neutral-50 default plus penalties. 1 → low, 5+ → medium, 11+ → high.' signal: type: string enum: - positive - neutral - negative breakdown: type: object properties: alpha_wallet_count: type: integer kol_count: type: integer bundle_buyer_count: type: integer avg_historical_win_rate: type: number nullable: true description: null when no buyer's win rate qualified (confidence insufficient_data) bot_dominated: type: boolean wallets_with_history: type: integer description: Buyers with ≥3 tokens of history (cohort identification — not predictive) qualified_win_rate_wallets: type: integer description: Buyers whose win rate fed the score — the basis of confidence dump_cluster_count: type: integer description: 'First-20 buyers on the rolling dump-cluster list (wallets whose 5+ recent first-20 appearances are exclusively on tokens that peaked <15 min after deploy; trailing 42d, refreshed daily). Out-of-sample: 3+ such wallets predicted a sub-15-min peak 94% of the time vs 61% base. Informational — does not move the score.' recycled_early_buyer_count: type: integer description: First-20 buyers with 5+ recent first-20 cohort appearances of any kind. The count alone predicts nothing; a heavily recycled cohort with dump_cluster_count 0 historically leans runner. cached_at: type: string format: date-time coverage: $ref: '#/components/schemas/TradeCoverage' note: type: string description: Present only when no early-buyer data exists for the mint — the score is then a neutral 50 placeholder ("Insufficient buyer data"); check coverage to tell 'no early buyers captured' from 'mint outside the write-gate'. TradeCoverage: type: object description: 'Coverage disclosure for endpoints backed by our persisted trade capture (`token_trades` / early-buyer data). Capture starts 2026-04-12 and persistence is gated to the launchpad pipeline (pump.fun, LaunchLab/bonk, bags) — it is NOT a full Solana trade history. Three separate answers (2026-09-21): `data_observed`/`in_scope` = at least one persisted row (presence, not completeness — a brand-new in-scope mint with no trades yet also reads false), `eligibility` = whether the CURRENT gate admits the subject, `completeness` = always `not_verified`. When no rows exist, zero/empty values mean ''not captured'' (see eligibility for why), never ''no activity''.' properties: history_start: type: integer example: 1775984757 description: Epoch seconds of the first persisted trade (2026-04-12). scope: type: string example: pump.fun pipeline (pump.fun, LaunchLab/bonk, bags) in_scope: type: boolean nullable: true description: 'Kept for compatibility — same value as data_observed: true = we hold ≥1 persisted trade for this subject · false = none persisted (see eligibility for why — a brand-new in-scope mint also reads false) · null = probe unavailable' data_observed: type: boolean nullable: true eligibility: type: string nullable: true enum: - eligible - lapsed - excluded - unknown - admitted_previously - not_applicable - null description: Resolved against the EFFECTIVE production capture settings (refreshed every few minutes; if they cannot be confirmed the answer narrows, never widens). eligible = admitted by the current gate · lapsed = launched via our pipeline, capture window closed under the current settings · excluded = blacklisted · unknown = no evidence the gate admits it (most likely outside the launchpad pipeline; basis capture_state_unavailable = settings could not be confirmed, no claim either way) · admitted_previously = persisted rows prove past admission, current status not established · not_applicable = wallet subjects · null = lookup failed eligibility_basis: type: string nullable: true description: Evidence behind eligibility (e.g. unresolved_launch, recently_bonded, revived_curve, pipeline_known, launchlab_pool, bags_vanity_mint, launchpad_proven, resolved_launch, bonded_over_7d, revival_expired, blacklisted, persisted_rows, capture_state_unavailable, lookup_failed) completeness: type: string enum: - not_verified size_floor: type: object description: Persistence size floor of the same write path (added 2026-09-30). Buys under 0.05 SOL (under $3.50 when paid in USDC/USDT) are not stored; sells that receive SOL are kept at any size. Live streams, prices and candles are not subject to it. properties: min_sol: type: number example: 0.05 min_stable_usd: type: number example: 3.5 applies_to: type: string example: buys and SOL-spending legs; SOL-receiving sells of any size are kept note: type: string description: 'How to read the values (always present since 2026-09-21: eligibility-specific when no rows exist, a completeness caveat when they do).' ServiceUnavailableError: type: object description: '503: a temporary capacity or maintenance condition (e.g. a Postgres statement timeout during a partition move, or the API-key lookup itself being unavailable). Retry after `Retry-After` seconds.' required: - error properties: error: type: string code: type: string description: Machine-readable code where one exists, e.g. `auth_backend_unavailable`. error_kind: type: string description: e.g. `statement_timeout`, `auth_backend` retry_after_seconds: type: number retryable: type: boolean support: type: string _rid: type: string RateLimitError: type: object description: '429 when a quota is exhausted: the daily quota, the per-minute burst, or a free-tier network/key-rotation cap. `datasets` is added on `daily` only.' required: - error - error_kind - message - tier - limit - resets_at properties: error: type: string enum: - rate_limit_exceeded error_kind: type: string enum: - daily - burst - ip_cap - ip_rotation message: type: string tier: type: string enum: - BASIC - PRO - ULTRA - BUSINESS - ENTERPRISE limit: type: integer resets_at: type: string format: date-time upgrade: type: object description: The plan that lifts this limit, with price and a checkout link. properties: tier: type: string enum: - PRO - ULTRA - BUSINESS - ENTERPRISE price_eur_per_month: type: number price_usdc_per_month: type: number daily_limit: type: integer burst_limit: type: integer pitch: type: string url: type: string upgrade_url: type: string format: uri reactivate: type: boolean description: Present (true) when the key's paid subscription has just lapsed and `tier` is the plan it held. prior_tier: type: string enum: - PRO - ULTRA - BUSINESS datasets: type: object properties: pitch: type: string url: type: string format: uri BuyerQuality: type: object properties: mint: type: string score: type: number description: 0-100 composite buyer-quality score confidence: type: string enum: - insufficient_data - low - medium - high description: 'Confidence in the PREDICTIVE inputs: counts only buyers whose win rate fed the score (≥3 tokens of history, not bot-flagged, not zero-cost-dump dominated). insufficient_data = none did — the score is the neutral-50 default plus penalties. 1 → low, 5+ → medium, 11+ → high.' signal: type: string enum: - positive - neutral - negative breakdown: type: object properties: alpha_wallet_count: type: integer kol_count: type: integer bundle_buyer_count: type: integer avg_historical_win_rate: type: number nullable: true description: null when no buyer's win rate qualified (confidence insufficient_data) bot_dominated: type: boolean wallets_with_history: type: integer description: Buyers with ≥3 tokens of history (cohort identification — not predictive) qualified_win_rate_wallets: type: integer description: Buyers whose win rate fed the score — the basis of confidence dump_cluster_count: type: integer description: 'First-20 buyers on the rolling dump-cluster list (wallets whose 5+ recent first-20 appearances are exclusively on tokens that peaked <15 min after deploy; trailing 42d, refreshed daily). Out-of-sample: 3+ such wallets predicted a sub-15-min peak 94% of the time vs 61% base. Informational — does not move the score.' recycled_early_buyer_count: type: integer description: First-20 buyers with 5+ recent first-20 cohort appearances of any kind. The count alone predicts nothing; a heavily recycled cohort with dump_cluster_count 0 historically leans runner. cached_at: type: string format: date-time coverage: $ref: '#/components/schemas/TradeCoverage' note: type: string description: Present only when no early-buyer data exists for the mint — the score is then a neutral 50 placeholder ("Insufficient buyer data"); check coverage to tell 'no early buyers captured' from 'mint outside the write-gate'. signal_stats: type: object description: Present only when breakdown.dump_cluster_count ≥ 1 and the daily signal-performance snapshot has a bucket for it. The live, out-of-sample efficacy of that value, so the raw count is readable. properties: dump_cluster_count: type: object properties: value: type: integer description: The token's own dump_cluster_count. bucket: type: string enum: - k>=1 - k>=3 - k>=5 description: Most specific cumulative-threshold bucket the value qualifies for. outcome: type: string enum: - dump - runner description: Outcome the hit rate is measured against. hit_rate: type: number description: P(outcome | bucket), 0-1. base_rate: type: number description: P(outcome | all tokens), 0-1. lift: type: number description: hit_rate / base_rate. sample_n: type: integer window_days: type: integer as_of: type: string format: date-time description: Snapshot time of the signal_performance row. summary: type: string example: 'dump_cluster_count=3 (k>=3): 95% of such tokens dumped vs 75% base (1.27× lift), n=349, 14d OOS' Error: type: object description: Generic error envelope. `error` is always present (SDKs match on it). Errors returned after authentication also carry `_rid` and, on paid tiers, `support`; authentication-stage errors (missing/invalid key, origin) carry neither. properties: error: type: string description: Error message or machine code (e.g. `tier_required`, `rate_limit_exceeded`). Never changed once shipped. message: type: string description: Human-readable explanation, when `error` is a machine code. code: type: string description: Machine-readable sub-code on some errors (e.g. `invalid_query`, `invalid_wallet`, `origin_not_allowed`). error_kind: type: string description: Machine-readable category on some errors (e.g. `statement_timeout`, `concurrency`, `daily`). detail: type: string description: Extra human-readable context. hint: type: string retry_after_seconds: type: number description: When present on a 429/503, the same value is sent as the `Retry-After` header. signup_url: type: string format: uri description: 'On the missing-key / bad-format 401: where to get a free key.' docs_url: type: string format: uri support: type: string description: 'Paid tiers, errors after authentication: priority support contact.' _rid: type: string description: Request ID — include when reporting issues required: - error LaunchCoverage: type: object description: 'Dataset-scope disclosure injected on every 200 from the launch-pipeline feeds (`/deployer-hunter/*`, `/sniper/*`, `/alpha/*`, `/tokens/almost-bonded`, `/tokens/batch/risk`, `/wallet/batch/classify`) unless the route already carries a more precise per-subject `coverage`. Static: these datasets are built from launchpad ingest (in practice ~99% pump.fun), so an absent wallet/token, an empty profile or a `false` reputation flag means ''not observed by this pipeline'', never ''clean'' or ''no activity''.' properties: scope: type: string example: launchpad pipeline (pump.fun and LaunchLab/bonk deploys; bags.fm tokens are covered in trade data but their deploys are not attributed here; no direct-to-DEX launches) note: type: string description: How to read absences and zeros in this response. responses: ServiceUnavailable: description: 'Temporarily unavailable — a Postgres statement timeout while the dataset is under load or maintenance (`statement_timeout`, with `X-RateLimit-*` and `_rid`), or the API-key lookup itself unavailable (`auth_backend_unavailable`, authentication stage: no `X-RateLimit-*`, no `_rid`, not counted as a failed authentication). Always carries `Retry-After`; honour it.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/ServiceUnavailableError' examples: statement_timeout: summary: Statement timeout value: error: Query timed out — this dataset is under load or being maintained. Retry shortly. error_kind: statement_timeout retry_after_seconds: 10 _rid: eyJ1IjoiZXhhbXBsZSIsInQiOjE3OTEwMDAwMDAwMDAsImgiOiIwMDAwMDAwMCJ9 auth_backend: summary: API-key lookup unavailable value: error: Authentication temporarily unavailable — please retry. code: auth_backend_unavailable error_kind: auth_backend retryable: true retry_after_seconds: 5 TierForbidden: description: 'Forbidden. One of: the key''s tier is below this endpoint''s or parameter''s gate (`tier_required` with an `upgrade` block); the public demo key used outside its allow-list (`demo_key_endpoint_not_allowed`); an invalid or revoked key; an origin-restricted key called from a disallowed origin (`code` `origin_not_allowed` / `origin_required`). `X-RateLimit-*` headers are present only on `tier_required`.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: anyOf: - $ref: '#/components/schemas/TierRequiredError' - $ref: '#/components/schemas/DemoKeyNotAllowedError' - $ref: '#/components/schemas/Error' examples: tier_required: summary: Tier below the gate value: error: tier_required message: This endpoint requires ULTRA tier — you're on PRO. feature: This endpoint your_tier: PRO required_tier: ULTRA upgrade: tier: ULTRA price_eur_per_month: 131 price_usdc_per_month: 149 daily_limit: 100000 burst_limit: 600 pitch: … url: https://madeonsol.com/pricing upgrade_url: https://madeonsol.com/checkout?tier=ULTRA support: 'Priority support: info@madeonsol.com' _rid: eyJ1IjoiZXhhbXBsZSIsInQiOjE3OTEwMDAwMDAwMDAsImgiOiIwMDAwMDAwMCJ9 demo_key: summary: Demo key outside its allow-list value: error: demo_key_endpoint_not_allowed message: The public demo key works on /kol/feed, /deployer-hunter/alerts, and /alpha/leaderboard only. This endpoint requires the PRO plan or higher; a free API key will not unlock it. See https://madeonsol.com/pricing. endpoint_tier: PRO allowed_endpoints: - /api/v1/kol/feed - /api/v1/deployer-hunter/alerts - /api/v1/alpha/leaderboard signup_url: https://madeonsol.com/developer upgrade_url: https://madeonsol.com/checkout?tier=PRO revoked_key: summary: Invalid or revoked key value: error: Invalid or revoked API key InternalError: description: Unexpected server error from the route (`Internal server error`, with `_rid`). Safe to retry with backoff. A failed API-key lookup is a 503 `auth_backend_unavailable`, never a 500. headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' content: application/json: schema: $ref: '#/components/schemas/Error' examples: route: summary: Route error value: error: Internal server error _rid: eyJ1IjoiZXhhbXBsZSIsInQiOjE3OTEwMDAwMDAwMDAsImgiOiIwMDAwMDAwMCJ9 RateLimited: description: Too many requests. Every 429 carries `Retry-After`. A quota 429 (`rate_limit_exceeded`, with `X-RateLimit-*`), a heavy-query concurrency 429 (`error_kind` `concurrency`/`capacity`), or the per-IP authentication-failure block (30 failed auths/min; plain `error` + `retry_after_seconds`, no `X-RateLimit-*`). headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-RateLimit-Limit: $ref: '#/components/headers/XRateLimitLimit' X-RateLimit-Remaining: $ref: '#/components/headers/XRateLimitRemaining' X-RateLimit-Used: $ref: '#/components/headers/XRateLimitUsed' X-RateLimit-Reset: $ref: '#/components/headers/XRateLimitReset' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: anyOf: - $ref: '#/components/schemas/RateLimitError' - $ref: '#/components/schemas/ConcurrencyLimitError' - $ref: '#/components/schemas/Error' examples: daily: summary: Daily quota exhausted value: error: rate_limit_exceeded error_kind: daily message: Daily rate limit exceeded — 200/day for BASIC tier. Resets at midnight UTC. tier: BASIC limit: 200 resets_at: '2026-10-05T00:00:00.000Z' upgrade: tier: PRO price_eur_per_month: 43 price_usdc_per_month: 49 daily_limit: 10000 burst_limit: 300 pitch: … url: https://madeonsol.com/pricing upgrade_url: https://madeonsol.com/checkout?tier=PRO datasets: pitch: … url: https://madeonsol.com/datasets concurrency: summary: Too many heavy queries in flight value: error: Too many concurrent heavy queries. Up to 8 of these endpoints may be in flight at once per key — retry in a moment, or lower your request concurrency. error_kind: concurrency retry_after_seconds: 2 max_concurrent: 8 _rid: eyJ1IjoiZXhhbXBsZSIsInQiOjE3OTEwMDAwMDAwMDAsImgiOiIwMDAwMDAwMCJ9 auth_failures: summary: Per-IP authentication-failure block value: error: Too many authentication failures from this IP. Retry in 60s. retry_after_seconds: 42 AuthBackendUnavailable: description: 'The API-key or subscription lookup could not be answered (authentication stage). Retryable and not your fault: it is never counted as a failed authentication. Honour `Retry-After`; no `X-RateLimit-*`, no `_rid`.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/ServiceUnavailableError' examples: auth_backend: summary: API-key lookup unavailable value: error: Authentication temporarily unavailable — please retry. code: auth_backend_unavailable error_kind: auth_backend retryable: true retry_after_seconds: 5 Unauthorized: description: 'Missing or invalid API key, or an expired free-tier key. Authentication-stage error: no `X-RateLimit-*` headers, no `_rid`.' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' content: application/json: schema: $ref: '#/components/schemas/Error' examples: missing_key: summary: No Bearer key value: error: 'Missing authentication — use API key (Authorization: Bearer msk_...)' hint: Get a free API key — 200 calls/day, no payment signup_url: https://madeonsol.com/developer docs_url: https://madeonsol.com/api-docs bad_format: summary: Malformed key value: error: Invalid API key format hint: Get a free API key — 200 calls/day, no payment signup_url: https://madeonsol.com/developer docs_url: https://madeonsol.com/api-docs securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: msk_... description: Get a free API key at https://madeonsol.com/developer x-madeonsol-tiers: - tier: FREE plan: BASIC name: Free price_eur_monthly: 0 price_eur_annual: 0 price_usd_monthly: 0 daily_limit: 200 burst_limit: 60 url: https://madeonsol.com/developer - tier: PRO plan: PRO name: Pro price_eur_monthly: 43 price_eur_annual: 430 price_usd_monthly: 49 daily_limit: 10000 burst_limit: 300 url: https://madeonsol.com/checkout?tier=PRO - tier: ULTRA plan: ULTRA name: Ultra price_eur_monthly: 131 price_eur_annual: 1310 price_usd_monthly: 149 daily_limit: 100000 burst_limit: 600 url: https://madeonsol.com/checkout?tier=ULTRA - tier: BUSINESS plan: BUSINESS name: Business price_eur_monthly: 400 price_eur_annual: 4000 price_usd_monthly: 456 daily_limit: 500000 burst_limit: 3000 url: https://madeonsol.com/checkout?tier=BUSINESS - tier: ENTERPRISE plan: ENTERPRISE name: Enterprise price_eur_monthly: null price_eur_annual: null price_usd_monthly: null daily_limit: null burst_limit: null url: https://madeonsol.com/pricing#enterprise x-provenance: method: harvested publisher: MadeOnSol source: https://madeonsol.com/api/v1/openapi.json note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json); stamped by stamp-openapi-provenance.py (roadmap#793)