---
title: "KOL trade feed on Robinhood Chain (BASIC+; 5-min delay on free)"
method: GET
path: "/rhc/kol/feed"
tags: ["Robinhood Chain"]
---

# KOL trade feed on Robinhood Chain (BASIC+; 5-min delay on free)

`GET /rhc/kol/feed`

## Request

- Base URL: `https://madeonsol.com/api/v1`
- URL: `https://madeonsol.com/api/v1/rhc/kol/feed`
- Auth: HTTP bearer

Every buy/sell from tracked Solana KOLs' verified EVM wallets on Robinhood Chain (chain id 4663), attributed to the effective trading account from our self-hosted node (tx.from normally, or the ERC-4337 userOp sender when bundled — never the bundler) — DEX swaps and launchpad curve snipes, within seconds of execution on PRO and up (the free tier serves this feed on a 5-minute delay). The KOL→EVM mapping is recovered by tracing each KOL's Solana→EVM bridge deposits (deBridge / Relay / Mayan / Wormhole) — a dataset unique to MadeOnSol. Every row is enriched with the token's current MC/liquidity/peak, the deployer's reputation tier, and `mc_multiple_since_trade` (current MC ÷ MC when the KOL bought — 'did the call run'). Tier: **BASIC** (any valid key).

## Query parameters

- `limit` integer
- `before` string, date-time
- `cursor` string
- `action` 'buy' | 'sell'
- `kol` string
- `min_eth` number
- `exclude_sells` 'true' | 'false' | '1' | '0'
- `min_mc_usd` number
- `max_mc_usd` number
- `token_age_max_min` integer
- `min_kol_winrate` number
- `strategy` 'scalper' | 'day_trader' | 'swing' | 'inactive' | 'unscored'

## Response `200`

`chain` echoes `robinhood`; `next_before` is the pagination cursor; `data_age_seconds` is the age of the newest row.

- object
  - `chain` 'robinhood'
  - `trades` object[]
    - `evm_address` string: The KOL's Robinhood-Chain wallet (0x).
    - `kol_name` string, nullable
    - `kol_twitter` string, nullable
    - `token_address` string
    - `token_symbol` string, nullable
    - `token_name` string, nullable
    - `launchpad` string, nullable: pons | flap | clanker | hood.fun | virtuals | null.
    - `is_graduated` boolean, nullable
    - `deployer_tier` 'elite' | 'good' | 'neutral' | 'spammer', nullable: Reputation tier of the token's deployer.
    - `token_age_minutes` integer, nullable: Token age at request time (first-seen → now).
    - `action` 'buy' | 'sell'
    - `eth_amount` number, nullable: Trade size in ETH.
    - `token_amount` number, nullable
    - `price_usd_at_trade` number, nullable
    - `market_cap_usd_at_trade` number, nullable: Token market cap when the KOL traded.
    - `current_mc_usd` number, nullable: Token market cap now.
    - `peak_mc_usd` number, nullable: All-time-high market cap observed since ingestion.
    - `liquidity_usd` number, nullable
    - `liquidity_basis` 'v4_virtual_ceiling' | 'measured': How liquidity_usd was derived. uniswap-v4 has no per-pool balances, so its figure is virtual reserves clamped to the PoolManager's total holdings — a provable CEILING, not measured per-pool TVL. All other venues are measured from real balances.
    - `risk` RhcTokenRiskSummary, nullable: Precomputed risk summary for a Robinhood Chain token, from the same engine as `/rhc/tokens/{address}/risk` (proxy resolution, capability scan, and a live router sell-simulation). **`null` means NOT ASSESSED, not safe.** The sweep covers tokens with liquidity on a ~24h rotation, so a brand-new or illiquid token has no row yet. These values are a SNAPSHOT — always read `checked_at`. Sellability can change the moment an owner flips a setting, which is why `/rhc/tokens/{address}/risk` is computed live and never served from this cache. Use this to triage a feed; use the live endpoint before acting on a single token.
      - `sellable` 'yes' | 'no' | 'unknown', nullable: `no` means a sell was simulated through the router and REVERTED — bought-but-cannot-sell.
      - `upgradeable` boolean, nullable: Token is a proxy whose implementation can be swapped (EIP-1967 / beacon / legacy). EIP-1167 minimal proxies are NOT upgradeable — the target is baked into immutable code.
      - `score` integer, nullable: 0–100, conservative. Absence of evidence is not evidence of safety.
      - `checked_at` string, date-time: When this assessment was computed.
    - `mc_multiple_since_trade` number, nullable: current_mc_usd ÷ market_cap_usd_at_trade — how far the token ran after the KOL's trade.
    - `dex` string: uniswap-v2/v3/v4 or the launchpad name for curve trades.
    - `pool` string, nullable
    - `tx_hash` string
    - `block_number` integer
    - `traded_at` string, date-time
    - `kol_score` object, nullable: The KOL's row in mv_rhc_kol_scores. null = no row yet (too few closed positions), never "not looked up".
      - `winrate_7d` number, nullable: 0–1, CLOSED positions only (sold ≥ 90 % of bought).
      - `winrate_30d` number, nullable
      - `strategy` string, nullable: Hold-time bucket: scalper | day_trader | swing | inactive | unscored.
      - `closed_positions_30d` integer, nullable
  - `count` integer
  - `filtered_by` object: Echo of the KOL-score filters (null = not applied).
    - `min_kol_winrate` number, nullable
    - `strategy` string, nullable
  - `matched_kols` integer, nullable: KOLs matching min_kol_winrate/strategy; null when neither filter is set.
  - `data_age_seconds` integer, nullable
  - `next_before` string, date-time, nullable
  - `next_cursor` string, nullable: Opaque keyset cursor for the next (older) page — pass as `cursor=`. PREFERRED over next_before, which is a strict timestamp bound that skips same-timestamp siblings of the boundary row.
  - `has_more` boolean: false only when the feed is exhausted (not merely when the page is short).
  - `scan` object: Present when a filter had to be applied after the candidate fetch (audit F09): has_more is then false only when the candidates ran out, and scan_truncated=true means the per-request scan budget ran out first — more matches MAY exist past next_cursor.
    - `post_filtered` boolean
    - `scanned` integer: Candidate rows examined
    - `scan_truncated` boolean: The per-request scan budget ran out before `limit` matches — more matches MAY exist past the cursor/offset
    - `scan_budget` integer

## Other responses

- `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/96ce84657f55?raw)
