API reference · v1 · updated 28 Sep 2026

Feyn API

Everything the website shows, as JSON: the verdict, every scam (S) and malicious (M) finding, and the wallets and transactions behind each one. Request a report over HTTPS, or subscribe over a WebSocket and get it live.

Base URL
https://api.feyn.fun
Auth
API key (Authorization: Bearer) or an x402 payment
Format
JSON, schema feyn.report/1
Live
wss://api.feyn.fun/v1/ws, free

Payments open at the $FEYN launch.

Until then /v1/token/{mint} and its summary are free, within the per-IP rate limits, and need no key or payment. The endpoints marked Available at launch answer 404 until then. Everything below is the API as it runs from the launch.

Getting started

Quickstart

  1. Get a key. Buy a credit pack on the pricing page with one payment from your Solana wallet. The starter pack is $25 for 6,000 credits. The key is shown once; keep it in an environment variable.
  2. Ask for a token. One report costs 1 credit. Every response says how many are left in X-Feyn-Credits-Remaining.
  3. Read the verdict. token.verdict.level is scam (an S-class finding fired), high_risk or caution (M-class findings only; the site shows these as “Warning · high risk” and “Warning”, since an M class firing does not make a token malicious), clean (nothing fired: no flags, which is not a promise) or insufficient_data.
curl https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump \
  -H "Authorization: Bearer $FEYN_API_KEY"

Until the $FEYN launch, leave the key out: the same request is free, within the per-IP rate limits. From the launch it answers 402 without one.

Authentication

Send your API key as a bearer token. Keys start with feyn_sk_. Feyn stores only a hash of each key, so keep yours out of client-side code and logs.

API key header
GET /v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump HTTP/1.1
Host: api.feyn.fun
Authorization: Bearer feyn_sk_…

Every keyed response carries two headers:

HeaderMeaning
X-Feyn-Credits-RemainingCredits left on the key after this call; unlimited on an Enterprise key.
X-Feyn-Key-ExpiresWhen the key expires, in unix seconds.
X-Request-IdOn every response, keyed or not. Quote it when something goes wrong.

What is free: a bad address (400) or an unknown token (404) is answered before any charge. A 304 revalidation is refunded. If the server fails after charging (5xx), the credits come back.

The website, the Telegram bot, the WebSocket, /v1/resolve, /v1/blob and /healthz need no key.

Paying with x402

Available at launch Payments open at the $FEYN launch.

Payments use x402 v2 with the Solana exact scheme: a 402 answer carries a quote, you pay it with one token transfer, and repeat the request with the payment attached. Feyn runs its own facilitator and pays the network fee. Pay in USDC at list price, or in $FEYN at 15% off. SOL is not accepted.

Single call

Without a key, GET /v1/token/{mint} costs $0.01 per report, paid on the request itself. Paid attempts are limited to 2 per second per IP.

  1. Client to Feyn API: GET /v1/token/{mint}, no key
  2. Feyn API to Client: 402 with PAYMENT-REQUIRED: one quote per asset, each with extra.memo
  3. Client: Build the transfer, sign as payer only
  4. Client to Feyn API: Same request + PAYMENT-SIGNATURE
  5. Feyn API to Solana: Co-sign as fee payer, simulate, broadcast
  6. Solana to Feyn API: Confirmed
  7. Feyn API to Client: 200 with the report + PAYMENT-RESPONSE

The transaction must have exactly this shape, or it is refused before broadcast:

#InstructionRule
0ComputeBudget SetComputeUnitLimitNo accounts, at most 100,000 units.
1ComputeBudget SetComputeUnitPriceNo accounts, at most 50,000 micro-lamports per unit.
2TransferChecked (SPL Token or Token-2022)The exact amount of the quoted asset to the associated token account of payTo, signed by the payer.
3MemoExactly extra.memo: the quote, signed by the server.

Two signers only (the fee payer extra.feePayer at index 0, then you), no address lookup tables, no extra instructions. Wallets that inject Lighthouse assertions are refused.

402 Payment Required (body; the header holds the same JSON, base64)
{
  "x402Version": 2,
  "error": "payment required",
  "resource": {
    "url": "https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump",
    "description": "Feyn token report",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "10000",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "payTo": "<vault owner>",
      "maxTimeoutSeconds": 300,
      "extra": {
        "feePayer": "<facilitator>",
        "memo": "feyn1:c:usdc:10000:<exp>:<nonce>:<mac>",
        "name": "USDC",
        "decimals": 6
      }
    },
    {
      "…": "the same for $FEYN at 15% off (maxTimeoutSeconds ≤ 60), while a TWAP quote exists"
    }
  ]
}
# 1. The quote
curl -si https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump | grep -i payment-required

# 2. Sign the transfer with an x402 v2 client, then:
curl https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump \
  -H "PAYMENT-SIGNATURE: $PAYMENT_B64"

Credit packs

A pack is bought the same way, from POST /v1/keys?pack=…: the paid request answers 201 with the key. A payment buys exactly one key; a replayed payment is refused. The key becomes active once its payment is final (usually within seconds; otherwise the answer is 202 and the key activates on first use).

  1. Client to Feyn API: POST /v1/keys?pack=starter
  2. Feyn API to Client: 402 with the pack's quotes
  3. Client: Build the transfer, sign as payer only
  4. Client to Feyn API: Same request + PAYMENT-SIGNATURE
  5. Feyn API to Solana: Co-sign, broadcast, wait until final
  6. Feyn API to Client: 201 with the key, shown once
  7. Client to Feyn API: Every later call: Authorization: Bearer feyn_sk_…

USDC quotes hold for 300 seconds and $FEYN quotes for 60. The $FEYN price is a 10-minute average of its own trades; no $FEYN quote is offered while that price is unreliable, and USDC is always offered. On the website, the pricing page does all of this with your wallet.

Plans and pricing

Every call with a pack's key spends credits; Enterprise calls spend none. Prices and costs below come from the same configuration the API server loads. $FEYN prices are 15% below the USDC price.

CallCredits
GET /v1/token/{mint}, the full report1
GET /v1/token/{mint}/summary1
Evidence or wallet-graph export2
Batch lookup, per token1
/v1/keys/self, /v1/pricing, /v1/resolve, /v1/blob, /healthz0
PlanUSDCIn $FEYNCreditsKey lastsRequests/s
Starter$25$21.256,00030 days10
Builder$100$8530,00060 days20
Pro$500$425200,00090 days200
Enterprise, with the firehose$1,500/month$1,275/monthUnlimited lookups30 days, renewable2,000
Single call (x402, no key)$0.01$0.00851 report—2 per IP

A key's lifetime counts from its payment. Credits left at expiry expire with it. Every plan gets the same reports; Enterprise adds the firehose, 24/7 support and monthly renewal of the same key.

Rate limits

Each key has its own limit, set by its pack. Over it, the answer is 429 rate_limited with Retry-After (seconds) and retry_after_s in the body.

WhoRequests/sBurst
Starter key1020
Builder key2040
Pro key200400
Enterprise key20004000
x402 paid attempts, per IP24
Free lookups (no key, paid mode off), per IP30/min15

Payments have two more limits that protect the fee payer. A network (IPv4 /24, IPv6 /48) whose payments failed on chain and used its share of the fee budget gets 429 network_throttled for 5 minutes. Under a drain attempt new payers settle one at a time, and a payment that waits over 10 s gets 503 settlement_busy. API keys are never affected by either.

Errors

Every error has the same JSON body. Switch on code, never on msg. retry_after_s (and the Retry-After header) appear when waiting helps.

Error body
{
  "error": {
    "code": "key_expired",
    "msg": "key expired",
    "request_id": "01J9…",
    "retry_after_s": 5
  }
}
HTTP error codes
StatusCodeWhenWhat to do
400bad_requestA bad query parameter, blob hash or resolve query.Fix the request. Nothing was charged.
400invalid_addressThe path address is not base58 or not 32 bytes (64 bytes is a signature).Fix the address. Nothing was charged.
400invalid_paymentPAYMENT-SIGNATURE is not base64 JSON of a PaymentPayload.Fix the encoding. Nothing was charged.
401invalid_keyThe key is malformed, unknown or revoked, or its payment failed or never landed.Check the Authorization header, or buy a new pack.
401key_pendingThe key's payment is not final yet.Retry after Retry-After seconds.
401key_expiredThe key is past expires_at.Buy a new pack. Unused credits expire with the key.
401invalid_signatureKey recovery: the message was not signed by the wallet that paid.Sign with the paying wallet.
402credits_exhaustedThe key has no credits left.Buy a new pack.
403plan_requiredThe firehose (GET /v1/firehose) with a key whose plan has no firehose.Use an Enterprise key.
403bot_blockedA free request (no key, no payment) from a client detected as automated: sustained, machine-like traffic that does not come from feyn.fun.Use an API key or pay per call with x402. The block lifts after Retry-After seconds once the traffic stops.
403payer_blockedThis payer, source account or IP had 3 payments fail on chain within 24 h.Wait 24 h, or use an API key.
404not_a_mintThe address is a curve, pool, wallet or other account; the body's resolved names its mint.Follow resolved.mint. Nothing was charged.
404not_indexedA pump.fun token created before Feyn started indexing, with no activity since (no backfill).Nothing to fetch until it trades again. Nothing was charged.
404not_foundUnknown resource, or the token is not one Feyn tracks.Check the path. Nothing was charged.
404unknown_refA blob hash that is not stored.Re-read the report for current refs. Nothing was charged.
405method_not_allowedA method the endpoint does not take (GET, HEAD and OPTIONS everywhere; POST only on /v1/keys).Use the documented method.
409payment_in_flightAnother payment from this payer or source account is not settled yet.Retry after Retry-After (5 s).
409already_usedKey recovery: this signed message was already used.Sign a new message with a fresh timestamp.
429too_many_socketsThe key already has 5 firehose sockets open.Close one, then reconnect.
429rate_limitedOver the key's tier, the per-IP paid-attempt limit or the free lookup budget.Back off for Retry-After seconds.
429network_throttledPayments from your client network (IPv4 /24, IPv6 /48) failed on chain and used its share of the fee budget.Retry after Retry-After (300 s), or use an API key.
500internalAn unexpected failure. Credits taken for the call are refunded.Retry once, then report the X-Request-Id.
503settlement_pendingA single-call payment was broadcast but has not confirmed in time.Check PAYMENT-RESPONSE.transaction, then re-send the same PAYMENT-SIGNATURE rather than paying again.
503settlement_pausedThe fee-payer spend budget for your payer class (or overall) is used up.Pay later (Retry-After, 60 s) or use an API key.
503settlement_busyNew payers settle one at a time during a drain attempt, and your turn did not come within 10 s.Retry shortly (Retry-After, 2 s).
503unavailableOverload, warm-up, a standby server, or the key store, payment store or RPC is down.Retry after Retry-After seconds.

A refused payment answers 402 with fresh quotes and a PAYMENT-RESPONSE whose errorReason says why:

invalid_payment_requirements
The quote is unknown, altered or expired. Ask for a fresh 402 and pay again.
duplicate_settlement
This payment was already used. One payment buys exactly one key or call.
insufficient_funds
The paying wallet does not hold enough of the asset.
invalid_transaction_state
The payment transaction failed on chain.
invalid_exact_svm_payload_transaction_*
The transaction is not exactly the accepted shape (compute budget, one TransferChecked, the memo).

Reference

Tokens

GET/v1/token/{mint}

The full report (feyn.report/1) for an indexed pump.fun token: the verdict, every finding with its evidence tables and graphs, holders and context. 1 credit, or $0.01 with x402.

Parameters

NameInTypeDescription
mintrequiredpathbase58The token's mint address (32 bytes). Case-sensitive.
sectionsquerylistComma list of summary, scam, malicious, context, tables, graphs. Default: all. Fields not selected are left out; schema, mint, rev, slot, updated_at, coverage, addrs, labels, hashes are always there.
inlinequerylistComma list of sigs, graphs, tables: include those lazy chunks in a top-level chunks map keyed by ref (up to 8 MB; chunks_truncated when capped). Otherwise fetch them from /v1/blob.
AuthorizationheaderstringBearer API key. Or send PAYMENT-SIGNATURE instead.
If-None-MatchheaderetagThe ETag you hold (the report's rev). A match answers 304, not charged.

Responses

200
The report. ETag and X-Feyn-Rev hold its revision.
304
Unchanged since If-None-Match.
400
invalid_address or bad_request. Free.
402
Payment required (x402 quotes), or credits_exhausted.
404
not_a_mint (the body's resolved names the token), not_indexed or not_found. Free.
429
rate_limited or network_throttled.
503
unavailable, or a payment state: settlement_pending, settlement_paused, settlement_busy.
curl https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump?sections=summary,malicious \
  -H "Authorization: Bearer $FEYN_API_KEY"
200 · sections=summary,malicious
{
  "schema": "feyn.report/1",
  "mint": "B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump",
  "rev": 450641306003,
  "slot": 450641306,
  "updated_at": 1790416804,
  "coverage": {
    "indexed_since_slot": 450300000,
    "complete": true
  },
  "addrs": [
    "D9gQ6RhKEpnobPBUdWY5bPQt2p3zGk3iVz6ChpUi2ArA",
    "tExRzb1XFwHP9A5jnZyJTfDTpFtRvNSn3FhGoVY1GuB"
  ],
  "labels": [
    {
      "a": 0,
      "kind": "creator"
    },
    {
      "a": 1,
      "kind": "curve"
    }
  ],
  "token": {
    "name": "Example Small Curve",
    "symbol": "SMOL",
    "creator": 0,
    "created_slot": 450636806,
    "created_at": 1790415600,
    "venue": "curve",
    "quote_mint": "So11111111111111111111111111111111111111112",
    "curve": {
      "address": 1,
      "progress_bps": 1661,
      "real_quote_reserves": 4200000000,
      "complete": false,
      "mayhem_mode": false
    },
    "price_quote": 3.6335507921714823e-8,
    "price_usd": 0.00000545,
    "mcap_usd": 5450,
    "liquidity_usd": 630,
    "supply_raw": 1000000000000000,
    "decimals": 6,
    "holders": {
      "total": 14,
      "fake_dust": 0,
      "top10_bps": 1873,
      "top10_true_bps": 1873
    },
    "launch_block_bps": 346,
    "trades_24h": {
      "buys": 21,
      "sells": 7,
      "volume_quote": 5900000000,
      "organic_volume_bps": 10000
    },
    "traders": {
      "total": 16,
      "fresh_bps": 0,
      "flagged_bps": 0
    },
    "verdict": {
      "scam": false,
      "level": "caution",
      "scam_ids": [],
      "malicious_ids": [
        "M004"
      ],
      "headline": "No scam detected. Malicious behaviour found: Block-0 buyers."
    },
    "score": {
      "value": 0,
      "band": "no_red_flags",
      "version": "598826f044206d43",
      "why": []
    },
    "insiders": {
      "wallets": 1,
      "at_least": false,
      "bought_bps": 346,
      "bought_market_bps": 346,
      "received_bps": 0,
      "sold_bps": 0,
      "sent_bps": 0,
      "held_bps": 346,
      "held_of_bought_bps": 10000,
      "launch_block_of_bought_bps": 10000,
      "net_bought_bps": 346,
      "net_sold_bps": 0,
      "round_trip_bps": 0,
      "held_of_net_bought_bps": 10000,
      "bought_raw": 34612903225806,
      "sold_raw": 0,
      "held_raw": 34612903225806,
      "buy_quote": 1000000000,
      "sell_quote": 0,
      "realised_quote": 0,
      "realised_usd": 0,
      "held_value_quote": 1257677419,
      "held_value_usd": 188.65,
      "wash_netted": false,
      "groups": [
        {
          "group": "creator",
          "wallets": 1,
          "bought_bps": 346,
          "sold_bps": 0,
          "held_bps": 346,
          "net_bought_bps": 346,
          "net_sold_bps": 0,
          "received_bps": 0,
          "sent_bps": 0,
          "realised_quote": 0
        }
      ]
    }
  },
  "malicious_behaviour": [
    {
      "name": "Block-0 buyers",
      "headline": "3.46% of the supply was bought in the same block the token launched, by the creator alone.",
      "explanation": "The creator bought in the same block the token was created, before anyone else could see it. The creator spent 1 SOL. The creator still holds all of it. The table shows exactly who bought, how much and in what order.\n\nWhy it matters: A creator's launch-block buy is visible to everyone and is common; it matters if the creator sells into later buyers.",
      "severity": 2,
      "confidence": "standard",
      "first_detected_slot": 450636806,
      "updated_slot": 450636806,
      "wallets": [
        0
      ],
      "examples": [],
      "tables": [
        "block0_buyers"
      ],
      "id": "M004",
      "metrics": {
        "create_slot": 450636806,
        "create_ix": 212,
        "buyers": 1,
        "supply_bps": 346,
        "tokens_raw": 34612903225806,
        "quote_spent": 1000000000,
        "tips_lamports": 0,
        "creator_included": true,
        "creator_supply_bps": 346,
        "largest_non_creator_bps": 0,
        "linked_to_creator": 0,
        "held_now_bps": 346,
        "true_held_bps": 346
      }
    }
  ],
  "hashes": {
    "addrs": "9bc01ae70874bc54",
    "malicious/M004": "d9b2b17dca920c51",
    "summary": "12ef3c252b3985d2"
  }
}
A real report: the schema fixture “small curve”, one M-class finding (M004).

GET/v1/token/{mint}/summary

The verdict, the headline of each finding and the key figures: exactly the WebSocket's summary frame. Small and fast; the Telegram bot renders from it. 1 credit.

Parameters

NameInTypeDescription
mintrequiredpathbase58The token's mint address.

Same statuses, ETag and payment rules as the full report. No query parameters.

curl https://api.feyn.fun/v1/token/B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump/summary \
  -H "Authorization: Bearer $FEYN_API_KEY"
200 · Summary
{
  "schema": "feyn.report/1",
  "mint": "B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump",
  "rev": 450641306003,
  "slot": 450641306,
  "updated_at": 1790416804,
  "coverage": {
    "indexed_since_slot": 450300000,
    "complete": true
  },
  "addrs": [
    "D9gQ6RhKEpnobPBUdWY5bPQt2p3zGk3iVz6ChpUi2ArA",
    "tExRzb1XFwHP9A5jnZyJTfDTpFtRvNSn3FhGoVY1GuB"
  ],
  "token": {
    "name": "Example Small Curve",
    "symbol": "SMOL",
    "creator": 0,
    "created_slot": 450636806,
    "created_at": 1790415600,
    "venue": "curve",
    "quote_mint": "So11111111111111111111111111111111111111112",
    "curve": {
      "address": 1,
      "progress_bps": 1661,
      "real_quote_reserves": 4200000000,
      "complete": false,
      "mayhem_mode": false
    },
    "price_quote": 3.6335507921714823e-8,
    "price_usd": 0.00000545,
    "mcap_usd": 5450,
    "liquidity_usd": 630,
    "supply_raw": 1000000000000000,
    "decimals": 6,
    "holders": {
      "total": 14,
      "fake_dust": 0,
      "top10_bps": 1873,
      "top10_true_bps": 1873
    },
    "launch_block_bps": 346,
    "trades_24h": {
      "buys": 21,
      "sells": 7,
      "volume_quote": 5900000000,
      "organic_volume_bps": 10000
    },
    "traders": {
      "total": 16,
      "fresh_bps": 0,
      "flagged_bps": 0
    },
    "verdict": {
      "scam": false,
      "level": "caution",
      "scam_ids": [],
      "malicious_ids": [
        "M004"
      ],
      "headline": "No scam detected. Malicious behaviour found: Block-0 buyers."
    },
    "score": {
      "value": 0,
      "band": "no_red_flags",
      "version": "598826f044206d43",
      "why": []
    },
    "insiders": {
      "wallets": 1,
      "at_least": false,
      "bought_bps": 346,
      "bought_market_bps": 346,
      "received_bps": 0,
      "sold_bps": 0,
      "sent_bps": 0,
      "held_bps": 346,
      "held_of_bought_bps": 10000,
      "launch_block_of_bought_bps": 10000,
      "net_bought_bps": 346,
      "net_sold_bps": 0,
      "round_trip_bps": 0,
      "held_of_net_bought_bps": 10000,
      "bought_raw": 34612903225806,
      "sold_raw": 0,
      "held_raw": 34612903225806,
      "buy_quote": 1000000000,
      "sell_quote": 0,
      "realised_quote": 0,
      "realised_usd": 0,
      "held_value_quote": 1257677419,
      "held_value_usd": 188.65,
      "wash_netted": false,
      "groups": [
        {
          "group": "creator",
          "wallets": 1,
          "bought_bps": 346,
          "sold_bps": 0,
          "held_bps": 346,
          "net_bought_bps": 346,
          "net_sold_bps": 0,
          "received_bps": 0,
          "sent_bps": 0,
          "realised_quote": 0
        }
      ]
    }
  },
  "findings": [
    {
      "id": "M004",
      "name": "Block-0 buyers",
      "headline": "3.46% of the supply was bought in the same block the token launched, by the creator alone.",
      "severity": 2
    }
  ]
}

The insiders object

token.insiders, in the report and the summary: every wallet a shown scam or malicious finding names (the creator, launch-block buyers, linked clusters and hubs, snipers, wash traders and bots), each counted once, and what that set did with the token. Shares are basis points oftoken.supply_raw. The object is absent when no finding names a wallet. It updates live, with every trade of the token.

  • Tokens moving between insiders cancel out. What reached the set from outside by transfer counts as received; what left it without a sale (sent to another wallet, or burned) counts as sent, not as sold.
  • Transfers are followed: a wallet that received the token from an insider outside a trade (and, a few hops on, its own recipients) counts as an insider of the sender's group. What it holds is in held_bps, what it sells in sold_bps and the profit, and the transfer is not sent. followed_wallets says how many such wallets are counted. Exchanges, routers, programs and dust recipients are never followed: tokens sent there stay sent.
  • Wash traders (M001 to M003 wash wallets, M010 bots) are counted net: tokens they bought and sold back add to neither bought nor sold. Their SOL stays gross in buy_quote and sell_quote, since a round trip costs real fees.
  • Flagged traders (M011) are not insiders: their record is on other tokens.
FieldTypeMeaning
walletsintegerInsider wallets counted (programs, pools, vaults and protocol actors are never counted).
at_leastbooleanThe set may be incomplete (an operator cluster truncated by its wallet cap, a finding that counts more wallets than it lists, history from before indexing): every figure is a lower bound.
unknown_walletsinteger?Insider wallets (counted in wallets) whose trades on the token Feyn no longer holds: an idle token's trade history is dropped after 2 hours and restarts on its next trade. Their figures are left out and at_least is set.
figures_unavailableboolean?No figure is known (every insider's history is gone). The shares and amounts are then not measurements: show “unavailable”, never 0%.
bought_bpsbpsSupply the insiders acquired, gross: bought_market_bps + received_bps. Counts every buy, so a wallet trading in and out counts each time; capped at 100% (see net_bought_bps).
bought_market_bpsbpsSupply they bought on the bonding curve and PumpSwap pools, the creator's launch buy included.
received_bpsbpsSupply that reached them by transfer from wallets outside the set (net).
sold_bpsbpsSupply they sold on the curve and pools, gross: every sell counts, capped at 100% (see net_sold_bps).
sent_bpsbpsSupply that left them other than by a sale: sent to wallets outside the set, or burned (net).
held_bpsbpsSupply they hold now. bought − sold − sent = held.
held_of_bought_bpsbpsShare of what they acquired that they still hold.
launch_block_of_bought_bpsbps?Share of their market buys made in the launch block. Absent when the launch predates indexing.
net_bought_bpsbpsSupply they took from the market and did not sell back: each wallet's buys minus its own round trips, plus received_bps. At most 100%. Use this, not bought_bps, for “how much they bought”.
net_sold_bpsbpsSupply they sold beyond what the same wallet bought (tokens from the launch or from transfers, e.g. a consolidating hub). net_bought − net_sold − sent = held.
round_trip_bpsintegerSupply bought and sold back by the same wallet. Not capped: 25000 means they traded the supply 2.5 times over.
held_of_net_bought_bpsbpsShare of net_bought they still hold.
bought_raw, sold_raw, held_rawintegerThe same amounts in raw token units.
buy_quote, sell_quoteintegerQuote paid for every buy and received for every sell, in base units of token.quote_mint (lamports for SOL; for another quote mint divide by 10^token.quote_decimals and label with token.quote_symbol, and token.quote_usd is the USD value of one whole quote token when Feyn has a reliable price). Curve trades are counted before pump.fun's fee, PumpSwap trades with it.
realised_quoteintegerRealised profit on what they sold: per role group, its sells minus the cost of the tokens it sold at its average cost (its buys over every token it acquired; tokens that reached it by transfer count at no cost), summed. Tokens still held or sent away keep their cost. Negative when they sold below cost.
realised_usdnumber?realised_quote in USD at the current rate. Absent without a USD rate for the quote unit.
held_value_quote, held_value_usdnumber?What they still hold at the current price. Unrealised: selling it would move the price.
followed_wallets, followed_held_bps, followed_sold_bpsnumber?Wallets counted because an insider sent them the token, and the supply they hold and sold (parts of held_bps and sold_bps). Absent when there are none.
wash_nettedbooleanWash trading was counted net (see below).
groups[]arrayBy role, each wallet in one group only: group (creator, dev_team, launch_bundle, cluster, sniper, wash_bot, other), wallets, bought_bps, sold_bps (gross), net_bought_bps, net_sold_bps, received_bps, sent_bps (transfers with any wallet outside the group, other insiders included; so each group adds up: net_bought + received − net_sold − sent = held), held_bps, realised_quote.

The outcomes object

token.outcomes, in the report and the summary: what happened to earlier launches that looked like this token at the same age. Historical, not a prediction and not advice. It is computed from a versioned table (table) of Feyn's own history, for pump.fun launches Feyn saw from creation, on their curve and not in Mayhem mode; other tokens have no outcomes.

  • at_secs is the comparison point (120, 300, 900 or 3600 seconds after launch): the latest one the token had reached. Its signals and trading activity are compared with other launches' at that age. After 90 minutes the last object stays as it was.
  • activity: 0 = no trade in the preceding minute; 1–5 = quintile of the SOL bought so far among launches still trading (5 = most). Every rate is within this bucket.
  • rows: one per qualifying signal the token has (signal: a class id such as S006, or M021_copy, launch_block_30, top10_25, creator_5, creator_serial, creator_sold), with label, and the rates of comparable launches with and without it. similar is every comparable launch.
  • Rates are in basis points of the group: crashed_80_1h_bps (price fell to 20% or less within the next hour), doubled_6h_bps (reached 2× within 6 hours), graduated_24h_bps (curve completed within 24 hours of launch). n / n_24h are the group sizes; a rate is absent when its group is under min_n.

The clusters object

token.clusters, in the report and the summary: wallets on this token (its insiders and its creator) that belong to a wallet cluster, a group of wallets that trade as one across many launches. Absent when none does. At most 3 clusters, highest confidence first.

  • Clusters are found from trading behaviour over the last few days of pump.fun launches: the same launches bought within a few blocks of each other, correlated activity, identical buy sizes. Not from funding or signer links: a cluster says the wallets act as one group (one operator or a coordinated group), not who controls them. Recomputed every hour; only clusters with confidence of 60 or more and same-block evidence are listed.
  • confidence (0–100) is calibrated: of clusters rated 70, about 70% were confirmed by an independent on-chain link check.
  • members[]: the cluster's wallets on this token (at most 12; members_more counts the rest), each a (an index into addrs; the summary's own addrs covers only the creator, curve and pool, so resolve it in the report's or the addrs section's table) and its insider role (as in insiders.groups; creator for the creator). creator is set when the creator is a member; insiders counts the token's insiders it covers; size is the whole cluster.
  • Cluster-wide, over the computation's window (window_hours, generation): together (launches two of its wallets bought within a few blocks of each other), same_block_bps and identical_bps (of those co-buys, the share in the same block / with identical sizes), early_bps (first buys made right after launch), overlap_bps (overlap of the launches they bought), activity_corr_bps (correlation of their activity over time), onchain_link (an on-chain link also joins two of them), staggered_bps (optional: of the launches two of its wallets bought, the share bought one after another, more than a minute and at most ten minutes apart).

GET/v1/resolve/{q}

What an address or link is: a mint, its bonding curve or pool, a creator wallet, or not a token. Use it to turn a curve, pool or pump.fun link into the mint to ask for. Free.

Parameters

NameInTypeDescription
qrequiredpathstringAn address, or a percent-encoded pump.fun/coin/…, solscan.io/token/… or dexscreener.com/solana/… link. Up to 256 characters.

kind is one of mint, curve, pool, creator (with up to 20 tokens), wallet, unknown_token, unsupported_launchpad (a Raydium LaunchLab token, with platform and launchpad), not_a_token, invalid (with a reason) or not_indexed. Every well-formed request answers 200; the answer is the kind, not the status.

curl https://api.feyn.fun/v1/resolve/tExRzb1XFwHP9A5jnZyJTfDTpFtRvNSn3FhGoVY1GuB
200 · resolved
{
  "q": "tExRzb1XFwHP9A5jnZyJTfDTpFtRvNSn3FhGoVY1GuB",
  "kind": "curve",
  "mint": "B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump",
  "name": "Example Small Curve",
  "symbol": "SMOL"
}

GET/v1/blob/{hash}

A lazy chunk of a report: a page of signatures, a table page or a subgraph, by the ref a report points at. Immutable and cached for a year. Free.

Parameters

NameInTypeDescription
hashrequiredpathhex(16)A Ref.ref from the report (Finding.txs, Table.next, GraphManifest.expand, …).

X-Feyn-Chunk says what it is: sigs, table or graph. A ref is kept for at least 24 hours after the last report that uses it.

curl https://api.feyn.fun/v1/blob/d550411a3d83bf4a

Reference

Keys and payments

POST/v1/keys?pack={name}Available at launch

Buy a credit pack. Without payment the answer is a 402 quote for the pack; with PAYMENT-SIGNATURE it is the key.

Parameters

NameInTypeDescription
packrequiredquerystringstarter, builder, pro, enterprise.
PAYMENT-SIGNATUREheaderbase64base64 JSON of the x402 PaymentPayload for one of the quotes.

Responses

201
The key, shown once. Feyn stores only its SHA-256.
202
status: "pending": the payment was broadcast but is not final yet; the key activates on first use once it is.
402
The quote (starter: 25000000 USDC atoms), or a refused payment with errorReason.
403 · 409 · 429 · 503
payer_blocked, payment_in_flight, network_throttled, settlement_paused / settlement_busy.
# The quote for the starter pack
curl -si -X POST "https://api.feyn.fun/v1/keys?pack=starter"

# Paid (see Paying with x402)
curl -X POST "https://api.feyn.fun/v1/keys?pack=starter" \
  -H "PAYMENT-SIGNATURE: $PAYMENT_B64"
201 · key issued
{
  "key": "feyn_sk_…",
  "status": "active",
  "pack": "starter",
  "credits": 6000,
  "rate_limit": {
    "per_s": 10,
    "burst": 20
  },
  "lifetime_days": 30,
  "expires_at": 1793008800,
  "transaction": "<signature>"
}

POST/v1/keys/recoverAvailable at launch

Lost the key? The wallet that paid for it signs a message and gets a new secret for the same key (same credits and expiry). The old secret stops working.

Parameters

NameInTypeDescription
transactionrequiredbodybase58The signature of the transaction that paid for the key.
timestamprequiredbodyunix sNow; must be within 5 minutes of the server's clock.
signaturerequiredbodybase58The paying wallet's signMessage over feyn-key-recovery:v1:<network>:<transaction>:<timestamp> (UTF-8).

Responses

201
A new secret for the same key.
401
invalid_signature: signed by another wallet.
404
The transaction paid for a single call, or failed.
409
already_used: each signed message works once.
curl -X POST https://api.feyn.fun/v1/keys/recover \
  -H "Content-Type: application/json" \
  -d '{"transaction":"<base58>","timestamp":1790416804,"signature":"<base58>"}'

POST/v1/keys?pack=enterpriseAvailable at launch

Renew an Enterprise key: the same request as buying, with the key in Authorization. It adds 30 days to the same key; paid with x402 like a purchase.

Parameters

NameInTypeDescription
packrequiredquerystringenterprise.
AuthorizationrequiredheaderstringBearer key to renew. Without it, the request buys a new key.
PAYMENT-SIGNATUREheaderbase64base64 JSON of the x402 PaymentPayload for one of the quotes.

Responses

200
status: "renewed": added_days were added; expires_at is the new expiry.
202
status: "pending": paid, but not final yet. The days are added once it is; do not pay again.
402
"pay to renew this key": the quote ($1,500 USDC, or $FEYN at 15% off), or a refused payment with errorReason.
400
bad_request: the key is not Enterprise. Credit packs are not renewed; buy a new one.
401
invalid_key: unknown or revoked key.
# The renewal quote
curl -si -X POST "https://api.feyn.fun/v1/keys?pack=enterprise" \
  -H "Authorization: Bearer $FEYN_API_KEY"

# Paid (see Paying with x402)
curl -X POST "https://api.feyn.fun/v1/keys?pack=enterprise" \
  -H "Authorization: Bearer $FEYN_API_KEY" \
  -H "PAYMENT-SIGNATURE: $PAYMENT_B64"
200 · renewed
{
  "status": "renewed",
  "pack": "enterprise",
  "added_days": 30,
  "expires_at": 1795600800,
  "transaction": "<signature>",
  "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
  "asset": "usdc",
  "note": "…",
  "support_telegram": "@reaperofrugs"
}

GET/v1/keys/selfAvailable at launch

Your key's credits, expiry, tier, renewal and paying transaction. Costs no credits (it does count against the tier's rate limit). Also at /v1/keys/me. The key page uses it.

Parameters

NameInTypeDescription
AuthorizationrequiredheaderstringBearer API key.

expires_at is null while the payment is not final (the lifetime starts from the payment). status is active or pending. On Enterprise, credits_total and credits_left are null (unlimited), and renewal.due turns true 3 days before expiry.

curl https://api.feyn.fun/v1/keys/self \
  -H "Authorization: Bearer $FEYN_API_KEY"
200 · key
{
  "pack": "starter",
  "status": "active",
  "credits_total": 6000,
  "credits_left": 4980,
  "expires_at": 1793008800,
  "rate_limit": {
    "per_s": 10,
    "burst": 20
  },
  "transaction": "<signature>",
  "plan": "starter",
  "expires_in_s": 2592000,
  "unlimited": false,
  "firehose": false,
  "renewal": {
    "renewable": false,
    "period_days": null,
    "due": false,
    "notice_days": 3,
    "how": "…"
  },
  "support_telegram": "@reaperofrugs"
}

GET/v1/pricingAvailable at launch

Packs, credit costs, payment assets and the current $FEYN rate. Free; cached for 10 seconds.

No parameters.

curl https://api.feyn.fun/v1/pricing
200 · pricing
{
  "x402Version": 2,
  "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
  "fee_payer": "<facilitator>",
  "single_call": {
    "price_usdc": "0.01",
    "price_feyn_atoms": "85000000",
    "rate_limit_per_ip": {
      "per_s": 2,
      "burst": 4
    }
  },
  "credit_costs": {
    "report": 1,
    "summary": 1,
    "export": 2,
    "batch_per_token": 1
  },
  "packs": [
    {
      "name": "starter",
      "price_usdc": "25",
      "price_feyn_atoms": "212500000000",
      "credits": 6000,
      "lifetime_days": 30,
      "rate_limit": {
        "per_s": 10,
        "burst": 20
      },
      "unlimited": false,
      "firehose": false,
      "renew_days": null
    },
    {
      "name": "builder",
      "price_usdc": "100",
      "price_feyn_atoms": "850000000000",
      "credits": 30000,
      "lifetime_days": 60,
      "rate_limit": {
        "per_s": 20,
        "burst": 40
      },
      "unlimited": false,
      "firehose": false,
      "renew_days": null
    },
    {
      "name": "pro",
      "price_usdc": "500",
      "price_feyn_atoms": "4250000000000",
      "credits": 200000,
      "lifetime_days": 90,
      "rate_limit": {
        "per_s": 200,
        "burst": 400
      },
      "unlimited": false,
      "firehose": false,
      "renew_days": null
    },
    {
      "name": "enterprise",
      "price_usdc": "1500",
      "price_feyn_atoms": "12750000000000",
      "credits": null,
      "lifetime_days": 30,
      "rate_limit": {
        "per_s": 2000,
        "burst": 4000
      },
      "unlimited": true,
      "firehose": true,
      "renew_days": 30
    }
  ],
  "support_telegram": "@reaperofrugs",
  "assets": {
    "usdc": {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "decimals": 6
    },
    "feyn": {
      "mint": "FEYNnky1Jp7xfrTnR5U93qBM79RxYkQibzTaR31fdxqS",
      "decimals": 6,
      "discount_bps": 1500,
      "usdc_per_feyn_twap": 0.0001,
      "quote_ttl_s": 60
    }
  }
}
From the live configuration, at an example rate of 0.0001 USDC per $FEYN. price_feyn_atoms is null while $FEYN has no quote.

GET/v1/x402/supportedAvailable at launch

The facilitator's supported schemes and networks (x402 v2 SupportedResponse), for x402 clients that check before paying. Free.

Lists the exact scheme on solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp with the fee payer in extra.feePayer.

curl https://api.feyn.fun/v1/x402/supported

GET/healthz

Liveness and ingest lag, for uptime checks. Not versioned, not rate-limited, never cached.

Responses

200
"ok": serving, ingest lag within target. "degraded": serving, but reports are behind.
503
"starting" or "down": not ready.
curl https://api.feyn.fun/healthz
200 · health
{
  "status": "ok",
  "slot": 450641306,
  "lag_s": 1.2
}

Reference

WebSocket

WSwss://api.feyn.fun/v1/ws

The live feed the website uses: subscribe to a token and get its summary, then each section, then patches as trades land. Free, no auth, rate-limited per IP. The protocol is feyn.ws/1.

Send hello first, then one sub per token (up to 4 per socket; ids strictly increasing). Binary frames carry the data, text frames the control messages. Receivers must ignore unknown fields, message types and section keys: additive changes do not bump the version.

# websocat (github.com/vi/websocat); text frames only
websocat wss://api.feyn.fun/v1/ws
{"type":"hello","v":1,"codecs":[]}
{"type":"sub","id":1,"q":"B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump"}

Messages

DirectiontypeWhat it does
client to serverhelloMust be first: {v: 1, codecs: ["deflate-raw"]}. You may send sub right after without waiting.
client to serversubSubscribe: id, q (a mint, curve, pool or link), optional rev to resume and want to pick sections.
client to serverunsubStop a sub by id. Frames already in flight are dropped by you.
client to servergetA lazy chunk by ref; the answer is one binary chunk frame.
client to serverpingEvery 30 s while visible, with t; no reply within 15 s means the socket is dead.
server to clienthello_ackServer time, max_subs, the codecs it will use, ping_interval_s.
server to clientresolvedFirst reply to every sub: what q is. Only mint, curve and pool go on to stream.
server to clientdoneThe first view (or resume) is complete at rev, with the hash of every section.
server to clientpongEchoes t.
server to clienterrorA code (below), with the sub's id when it concerns one.
First view of a token, frame by frame
→ {"type":"hello","v":1,"codecs":["deflate-raw"]}
→ {"type":"sub","id":1,"q":"B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump"}
← {"type":"hello_ack","v":1,"server_time":1790416804123,"max_subs":4,"codecs":["deflate-raw"],"ping_interval_s":30}
← {"type":"resolved","id":1,"q":"B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump","kind":"mint","mint":"B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump","name":"Example Small Curve","symbol":"SMOL"}
← [01 01 01 00 00 00] summary summary · deflate-raw · 1022 B
← [00 02 01 00 00 00] section addrs · identity · 227 B
← [01 02 01 00 00 00] section malicious/M004 · deflate-raw · 541 B
← [00 02 01 00 00 00] section context · identity · 348 B
← [01 02 01 00 00 00] section tables/block0_buyers · deflate-raw · 422 B
← [01 02 01 00 00 00] section tables/holders · deflate-raw · 414 B
← {"type":"done","id":1,"rev":450641306003,"mode":"snapshot","hashes":{"addrs":"9bc01ae70874bc54","context":"ff7ccdae6732ed75","malicious/M004":"d9b2b17dca920c51","summary":"12ef3c252b3985d2","tables/block0_buyers":"15880e004c033b06","tables/holders":"0fab2cce775fb660"}}
A real transcript: the fixture token's first view (docs/api/examples). Binary frames are shown as header bytes, kind, key, codec and size.

Binary frames

Every binary frame starts with a 6-byte header, then the payload: JSON, deflated with raw DEFLATE when it is 512 bytes or more and you listed the codec.

OffsetSizeFieldValues
01codec0 = identity (UTF-8 JSON), 1 = deflate-raw
11kind1 = summary, 2 = section, 3 = patch, 4 = chunk
24subu32 little-endian: the subscription id
6npayloadencoded per codec

Process frames in arrival order (decompression is asynchronous). Drop frames with an unknown codec or kind, and frames for subs you no longer hold.

Limits and errors

Per socket: 4 subs, 10 sub/s, 20 get/s, 50 messages/s, frames up to 2,048 bytes. Per IP: 60 new sockets a minute, 30 lookups a minute (burst 15; a token you looked up in the last 10 minutes is free), 32 live subs. After 20 refused lookups in 10 minutes an IP is challenged (Turnstile). Reconnect with backoff 0.25 → 0.5 → 1 → 2 → 4 → 8 s, then resume each sub with its rev.

WebSocket error codes
error.codeMeaningWhat to do
bad_requestMalformed JSON, an unknown type, a missing field or a sub id that did not increase. The socket stays open.Fix the client; don't retry blindly.
bot_blockedThe client was detected as automated; close 1008 follows. The free WebSocket is for feyn.fun.Use an API key or x402 (feyn.fun/pricing); don't reconnect until retry_after_ms.
rate_limitedA sub or get was refused; retry_after_ms says when. msg "verification required" means the IP is challenged.Retry after the delay; if challenged, solve Turnstile and reconnect with ?turnstile=.
too_many_subsThe sub with this id was ended to make room (4 per socket).Stop rendering it.
unknown_refA get for a ref the server does not have.Fall back to GET /v1/blob/{ref} once.
overloadedThe server is shedding load; the sub did not start.Back off and retry the sub.
internalAn unexpected failure; the sub (if any) ended.Retry the sub once after 1 s.
hello_requiredThe first frame was not hello (sent just before close 4001).Send hello first.
unsupported_versionhello.v is not supported (sent just before close 4002).Don't reconnect; update the client.
WebSocket close codes
CloseMeaningReconnect
1000Normal close (tab hidden over 5 min, navigation).On demand
1001Going away: restart or deploy.Yes, back off from step 0
1003The client sent a binary frame.No (client bug)
1006Abnormal drop (network, Cloudflare restart).Yes, back off
1008Policy: inbound message flood, or bot_blocked (automated client).Yes, start at 8 s; after bot_blocked, not before retry_after_ms
1009Inbound frame over 2,048 bytes.Yes, back off
1011Internal error.Yes, back off
4001First frame was not hello.Yes, after fixing the order
4002hello.v unsupported.No: reload the page
4008Slow consumer.Yes, back off
4029Too many sockets from this IP.Yes, start at 8 s

Reference

Enterprise

Enterprise plan

Enterprise is $1,500 a month ($1,275 in $FEYN). It is bought like a pack, with POST /v1/keys?pack=enterprise or on the pricing page, and its key includes:

  • The firehose (below): every finding as it happens, new, updated or retracted, across all pump.fun and PumpSwap tokens, on one WebSocket.
  • Unlimited report lookups. Keyed calls spend no credits; X-Feyn-Credits-Remaining reads unlimited.
  • 2,000 requests/s, bursts of 4,000.
  • 24/7 support in a private Telegram channel. Message @reaperofrugs on Telegram to set up your private support channel, before you buy or once you have the key. /v1/pricing and /v1/keys/self carry the handle as support_telegram.

Renewal. The key lasts 30 days from its payment. Paying again with the same key (renewal, or Renew on the key page) adds 30 days to it. The key, its firehose cursor and your setup stay the same, and nothing renews by itself. From 3 days before expiry, /v1/keys/self has renewal.due: true and the firehose sends a key_expiring notice.

WSwss://api.feyn.fun/v1/firehoseAvailable at launch

The Enterprise stream: one message per change to any token's findings or verdict, in order, each with a cursor to resume from. JSON text frames only, no subscriptions.

Parameters

NameInTypeDescription
AuthorizationheaderstringBearer key of an Enterprise plan that has not expired. Or put the key in hello (clients that cannot set headers).
hello.vrequiredbody1Protocol version.
hello.keybodystringThe key, when it is not in the header.
hello.cursorbodystringThe last cursor you processed, to resume. Leave it out to start from now.

Responses

101
Upgraded. Send hello within 10 s; the server answers hello_ack, then streams.
401
invalid_key, key_pending or key_expired (a key in the header is checked before upgrading).
403
plan_required: the key is not Enterprise.
429
too_many_sockets: 5 are open with this key.

A key sent in hello instead is checked there: an error, then a close code (below). The server re-reads the key every minute, so a renewal applies without reconnecting.

websocat -H "Authorization: Bearer $FEYN_API_KEY" wss://api.feyn.fun/v1/firehose
{"type":"hello","v":1,"cursor":"<last cursor>"}
hello_ack
{
  "type": "hello_ack",
  "v": 1,
  "start": "replay",
  "cursor": "3f9a01c2.1834.1767225600123",
  "server_time": 1767225600456,
  "ping_interval_s": 30,
  "max_lag": 32768,
  "plan": "enterprise",
  "expires_at": 1793008800,
  "expires_in_s": 2591999,
  "unlimited": true,
  "firehose": true,
  "renewal": {
    "renewable": true,
    "period_days": 30,
    "due": false,
    "notice_days": 3,
    "how": "…"
  },
  "support_telegram": "@reaperofrugs"
}
finding
{
  "type": "finding",
  "event": "new",
  "mint": "B4HpTokrHXwYGQUYCN1axBH54pYf483Bpw5sCft2pump",
  "class": "S003",
  "key": "scam/S003",
  "scam": true,
  "name": "Consolidated dump",
  "headline": "…",
  "severity": 5,
  "confidence": "high",
  "tier": null,
  "first_detected_slot": 367000000,
  "updated_slot": 367000123,
  "token": {
    "name": "…",
    "symbol": "…"
  },
  "verdict": {
    "level": "scam",
    "scam": true,
    "headline": "…"
  },
  "rev": 42,
  "slot": 367000123,
  "seq": 1835,
  "cursor": "3f9a01c2.1835.1767225600789",
  "t": 1767225600789
}

Firehose messages

Every message is one JSON text frame with a type. Events carry seq, cursor (store the last one you processed) and t (unix ms). They describe state, not edits: treat each as an upsert keyed by (mint, key), so applying one twice is harmless. Ignore unknown fields and types.

DirectiontypeWhat it does
You → serverhelloFirst frame, within 10 s: protocol version, the key (unless it was in the Authorization header) and, to resume, the last cursor you processed.
Server → youhello_ackWhere your stream starts (start), the head cursor, the ping interval, max_lag, and the key's plan, expiry, renewal and support contact.
Server → youfindingA finding on a token changed: event is new, updated or retracted. Carries the finding's current fields and the token's verdict after the change.
Server → youverdictA token's verdict level changed with no finding change (from is the previous level).
Server → yougapReport builds were dropped before they could be diffed (lost_builds). Some changes may be missing: re-read the tokens you track.
Server → youresyncEvents were missed: reason cursor_expired or slow_consumer. Re-read the tokens you track.
Server → younoticekey_expiring: the key expires within 3 days, with how to renew and the support contact.
Server → youerrorBefore a close, or for a bad client message: code and msg.
You → serverpingAnswered with pong. The server pings every 30 s and closes a socket silent for 75 s.

A finding names the token (mint, token), the finding (class, an S or M code, with name and headline), what happened to it (event) and the token's verdict after the change. For the evidence, fetch the report (GET /v1/token/{mint}, unlimited on this plan) or subscribe to the token on the WebSocket. A retracted finding no longer holds.

Resume and back-pressure

Resume. Reconnect, and send the last cursor you processed in hello.cursor. hello_ack.start says what happens:

live
No cursor: the stream starts from the next event.
replay
Your cursor is from this server process and still in its ring (65,536 events): exactly the events after it, in order.
approx
Your cursor is from the other app server or a previous process: replay by time from 2 s before it. Events may repeat; they are upserts.
resync
Your cursor is older than the ring or the process: a resync (cursor_expired) follows, then the live stream. Re-read the tokens you track.

Back-pressure. Every socket reads one shared, bounded ring at its own position, so a slow socket never delays the stream or anyone else. Fall more than max_lag (32,768) events behind, or leave a write blocked for 10 s, and you get resync (reason slow_consumer), then close 4008. Reconnect with your last processed cursor. Read in one task and process in another, so a slow handler does not stall the socket.

Firehose close codes
CloseMeaning
1000Normal, or idle.
1003You sent a binary frame.
1008Message flood (over 5/s sustained), or a malformed cursor.
1009Frame over 4 KiB.
1013The key store was unavailable at hello. Retry.
4001No hello within 10 s, or the first frame was not hello.
4002Unsupported protocol version. Use v: 1.
4008Slow consumer. Reconnect with your last processed cursor.
4029More than 5 sockets for this key.
4401Unknown, malformed, revoked or pending key.
4402Key expired: renew it, then reconnect with your cursor.
4403The key's plan has no firehose (not Enterprise).

Reconnect after any other close with a short random delay, and resume with your last cursor.

More

Changelog

  1. 28 Sep 2026

    API v1, first public version. Reports (feyn.report/1), the WebSocket (feyn.ws/1), API keys and x402 payments in USDC or in $FEYN at 15% off.

  2. 29 Sep 2026

    New plan lineup and Enterprise. Packs from $25; Enterprise at $1,500 a month with the firehose (/v1/firehose), unlimited lookups and renewal of the same key. Additive: /v1/pricing, POST /v1/keys and /v1/keys/self (now also /v1/keys/me) gain fields.

  3. 23 Oct 2026

    Wallet clusters. Reports and summaries gain token.clusters (the clusters object): the token's insiders and creator that belong to a group of wallets trading as one across many launches, with the group's confidence and figures. Additive.

  4. 23 Oct 2026

    Insider transfers followed. A wallet that received the token from an insider outside a trade now counts as an insider: its holdings and sells are the insiders', instead of the tokens counting as sent. token.insiders gains followed_wallets, followed_held_bps and followed_sold_bps. Additive.

  5. 5 Oct 2026

    Outcomes and launch facts. Reports and summaries gain token.outcomes (the outcomes object): historical outcome rates of comparable launches. The report's context gains the keys creator_launches (the creator's other launches in the 24 hours before), creator_sold and holder_concentration. Informational: neither changes the verdict. Additive.

  6. 2 Oct 2026

    Insiders with unknown history. token.insiders gains unknown_wallets and figures_unavailable: an insider whose trades on the token Feyn no longer holds is left out of the figures (marked at_least), never counted as 0. Additive.

  7. 2 Oct 2026

    Insiders, net of round trips. token.insiders gains net_bought_bps, net_sold_bps, round_trip_bps and held_of_net_bought_bps (and per group net_bought_bps, net_sold_bps, received_bps, sent_bps, so each group adds up on its own): a wallet's buys it sold back no longer count as supply bought. The gross bought_bps / sold_bps keep their meaning and cap at 100%; the site shows them while they stay under 100% and the net pair once they reach it. Additive.

  8. 1 Oct 2026

    Insiders. Reports and summaries gain token.insiders (the insiders object): how much of the supply the wallets named by the findings bought, sold and still hold, and their realised profit. Additive.

  9. Versioning

    The path carries the major version (/v1). New optional fields, message types, section keys and error codes are added without a new version, so ignore what you don't know. A breaking change moves to /v2.