---
title: "List matched copy-trade signals"
method: GET
path: "/copytrade/signals"
tags: ["Copy-Trade"]
---

# List matched copy-trade signals

`GET /copytrade/signals`

## Request

- Base URL: `https://madeonsol.com/api/v1`
- URL: `https://madeonsol.com/api/v1/copytrade/signals`
- Auth: HTTP bearer

Signals matched against your copy-trade subscription rules, newest first. Filter by subscription and time window. The market-cap band (min_mc_usd / max_mc_usd) is applied BEFORE limit and cursor (since 2026-10-05): limit=N returns up to N matching signals, and paging with cursor= (the next_cursor of the previous page) neither skips nor repeats matches. With a band set, `scan` reports the bounded scan: has_more is false only when your signals ran out, and scan_truncated=true means the per-request budget ran out first (more matches MAY exist past next_cursor). PRO/ULTRA only.

## Query parameters

- `subscription_id` integer
- `since` string
- `limit` integer
- `cursor` string
- `min_mc_usd` number
- `max_mc_usd` number

## Response `200`

OK

- object
  - `signals` object[]
    - `id` integer
    - `subscription_id` integer
    - `fired_at` string, date-time
    - `source_wallet` string
    - `action` 'buy' | 'sell'
    - `token_mint` string
    - `token_symbol` string, nullable
    - `token_name` string, nullable
    - `source_sol_amount` number, nullable: SOL size of the source wallet's trade
    - `suggested_sol_amount` number, nullable: Sized suggestion per this subscription's sizing_mode
    - `tx_signature` string: Source transaction. With copy-trade identity v2 active (identity_version 2) one transaction can yield several signals, one per independent action: dedupe on id or economic_action_id, never on tx_signature.
    - `economic_action_id` string, nullable: Present once copy-trade identity v2 is active. The independent action in the source transaction this signal copies (router legs of one swap are one action; independent outer instructions are separate actions). null on identity_version 1 rows.
    - `source_actor` string, nullable: Present once copy-trade identity v2 is active. The followed wallet that performed this action.
    - `co_actors` string[]: Present once copy-trade identity v2 is active. Other wallets your rule follows that took part in the same action.
    - `identity_version` 1 | 2: Present once copy-trade identity v2 is active. 1 = one signal per (rule, transaction), 2 = one signal per (rule, economic action).
    - `delivered` boolean: True once the signal was actually written to one of your live sockets or accepted by your webhook. A frame that was only queued for a socket still running a replay and then dropped (queue overflow or a 4008 close) is NOT delivered and records delivery_error 'ws_replay_dropped'.
    - `delivered_at` string, date-time, nullable
    - `market_cap_usd_at_trade` number, nullable: Market cap (USD) of the source trade when the signal fired, for any source wallet (KOL or not). Stored on the signal at fire time since 2026-10-04; older signals read it from the source KOL trade. null = unknown (no price at fire time).
    - `price_usd_at_trade` number, nullable: Token price (USD) of the source trade when the signal fired; same source and null rule as market_cap_usd_at_trade.
    - `market_cap_usd` number, nullable: Live token market cap, for comparing against market_cap_usd_at_trade
    - `last_price_usd` number, nullable
    - `mc_change_pct` object, nullable: MC % change per lookback window (5m/15m/1h/2h/4h), only for windows with enough history
    - `volume_usd` object, nullable: Organic volume per lookback window
    - `mev_volume_pct` object, nullable: MEV/bot volume share per lookback window
    - `history_age_seconds` integer, nullable
  - `next_cursor` string, nullable: Opaque keyset cursor for the next (older) page — pass as `cursor=`. null = end of feed. PREFERRED over the strict-timestamp legacy cursor, which skips same-timestamp siblings of the boundary row.
  - `has_more` boolean: false only when the candidate 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

- `400`: Invalid limit, since, cursor or market-cap bound
- `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`: 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`.

---

[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)
