---
title: "Token momentum fires — new-token surges + dormant-token revivals"
method: GET
path: "/tokens/surges"
tags: ["Tokens"]
---

# Token momentum fires — new-token surges + dormant-token revivals

`GET /tokens/surges`

## Request

- Base URL: `https://madeonsol.com/api/v1`
- URL: `https://madeonsol.com/api/v1/tokens/surges`
- Auth: HTTP bearer

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+.

## Query parameters

- `kind` 'surge' | 'revival'
- `tier` 'early' | 'strong' | 'breakout'
- `mint` string
- `since` string, date-time
- `before` string, date-time
- `min_mc_usd` number
- `max_mc_usd` number
- `min_buys` integer
- `launchpad` string
- `deployer_tier` 'elite' | 'good' | 'moderate' | 'rising' | 'cold' | 'unranked'
- `exclude_flags` string
- `only_clean` '1' | '0' | 'true' | 'false'
- `stats` '1' | '0' | 'true' | 'false'
- `days` integer
- `limit` integer

## Response `200`

OK

- object
  - `events` object[]
    - `id` integer
    - `kind` 'surge' | 'revival'
    - `tier` 'early' | 'strong' | 'breakout', nullable: null on revivals
    - `mint` string
    - `symbol` string, nullable
    - `name` string, nullable
    - `launchpad` string, nullable
    - `primary_dex` string, nullable
    - `fired_at` string, date-time
    - `birth_at` string, date-time, nullable
    - `birth_source` 'sniper' | 'deployer' | 'first_seen', nullable
    - `age_seconds` integer, nullable
    - `market_cap_usd` number
    - `liquidity_usd` number, nullable
    - `liquidity_to_mc_ratio` number, nullable
    - `price_usd` number, nullable
    - `baseline_mc_usd` number, nullable: surge: first MC sample after birth
    - `baseline_source` 'launch' | 'late', nullable
    - `mc_multiple` number, nullable: market_cap_usd ÷ baseline (launch baselines only)
    - `mc_change_3m_pct` number, nullable
    - `dormant_hours` number, nullable: revival: hours without a trade candle
    - `prev_mc_usd` number, nullable: revival: close MC of the last pre-dormancy candle
    - `mc_vs_prev_multiple` number, nullable
    - `peak_mc_usd` number, nullable
    - `pct_of_peak` number, nullable
    - `bonding_progress_pct` number, nullable
    - `is_bonded` boolean, nullable
    - `tape` object
      - `since` string, date-time, nullable
      - `available` boolean: false = no tape covers the window yet (candle lag); every count is then null
      - `source` 'candles' | 'wallet_trades' | 'null', nullable: What the burst numbers are measured on; null when available=false
      - `buy_sol` number, nullable
      - `sell_sol` number, nullable
      - `buys` integer, nullable
      - `sells` integer, nullable
      - `trades` integer, nullable
      - `buy_volume_usd` number, nullable
      - `sell_volume_usd` number, nullable
      - `volume_usd` number, nullable
      - `mev_volume_usd` number, nullable
      - `unique_buyers` integer, nullable: null when wallet_data_available=false
      - `unique_wallets` integer, nullable
      - `trades_per_wallet` number, nullable
      - `wallet_data_available` boolean
    - `kol` object, nullable
      - `buyers` integer
      - `buys` integer
      - `sells` integer
      - `names` string[]
    - `early_buyers` object, nullable
      - `count` integer
      - `bundled` integer
      - `cohort_sol` number
      - `sold` integer
      - `sniper_wallets` integer
    - `deployer` object, nullable
      - `wallet` string
      - `tier` string
      - `bonding_rate` number, nullable
      - `total_bonded` integer, nullable
      - `total_deployed` integer, nullable
      - `runner_rate` number, nullable
      - `labeled_tokens` integer, nullable
      - `recent` string, nullable
    - `deployer_wallet` string, nullable
    - `deployer_tier` string, nullable
    - `mint_authority_revoked` boolean, nullable
    - `freeze_authority_revoked` boolean, nullable
    - `is_token_2022` boolean, nullable
    - `risk_flags` string[]
    - `enrichment_available` boolean
    - `detail_url` string
    - `outcome` object, nullable: null until the fire is ≥65 min old
      - `computed_at` string, date-time
      - `mc_usd_1h_after` number, nullable
      - `peak_mc_usd_1h_after` number, nullable
      - `low_mc_usd_1h_after` number, nullable
      - `mc_1h_multiple` number, nullable
      - `peak_1h_multiple` number, nullable
      - `priced_after_1h` boolean: false = no candle within the hour (token stopped being priced) — not zero
  - `pagination` object
    - `limit` integer
    - `count` integer
    - `has_more` boolean: true when the page is full (count === limit); page back with before=
    - `next_since` string, date-time, nullable
    - `next_before` string, date-time, nullable
  - `filters` object
  - `stats` object: 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` object: WebSocket pointer for the token:surges channel
  - `definitions` object: The live thresholds behind every tier / the revival confirmation / each risk flag
  - `note` string

## Other responses

- `400`: invalid_params (unknown exclude_flags → known_flags[]; tier with kind=revival)
- `401`: Missing or invalid API key, or an expired free-tier key. Authentication-stage error: no `X-RateLimit-*` headers, no `_rid`.
- `403`: 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`.
- `429`: 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-*`).
- `500`: 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.
- `503`: 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.

---

[API](https://skmtc.dev/madeonsol/apis/madeonsol-api.md) · [All operations](https://skmtc.dev/madeonsol/apis/madeonsol-api/llms.txt) · [OpenAPI document](https://skmtc.dev/madeonsol/apis/madeonsol-api/revisions/d9531a28d83b?raw)
