# CrowdIntel API — AI context

> Drop this file into Claude, ChatGPT, Cursor, or any LLM to build against the CrowdIntel API. It covers authentication, every /api/v1 endpoint with params and tier requirements, response conventions, common workflows, and the MCP server alternative.

CrowdIntel (https://crowdintel.xyz) is a read-only Polymarket intelligence API built on an independently indexed ledger of on-chain Polymarket trade fills (1.6B+) from Polygon. It serves data Polymarket's public API does not: smart-money signals, wallet dossiers with resolved PnL, curated wallet cohorts, funding/sybil clusters, insider-score alerts, entry-timing edge, curated investigations, webhooks, and backtests. Data is near-real-time — a few minutes behind chain.

Last updated: 2026-07-31

## Authentication

Every request needs a bearer API key:

```
Authorization: Bearer cint_api_xxxxxxxxxxxxxxxxxxxxxxxx
```

- Key format: `cint_api_` + 48 hex chars. Mint at https://crowdintel.xyz/settings/api — free on every plan, no credit card.
- Keys are secrets. The API is server-to-server only and sends no CORS headers — never call it from a browser.
- Base URL: `https://crowdintel.xyz/api/v1`
- Machine-readable spec: https://crowdintel.xyz/openapi.json (OpenAPI 3.1). Human reference: https://crowdintel.xyz/api-docs

## Tiers and rate limits

**Every endpoint is reachable on every tier, including free.** A key resolves to a
tier from its owner's subscription, and the tier decides throughput — not which
endpoints answer.

| Tier | Plan | Hourly | Monthly | Backtest runs | Webhooks |
|------|------|--------|---------|---------------|----------|
| `free` | Free ($0, no card) | 50/hr | 500/mo | 3/mo | 1 |
| `terminal` | Terminal ($99/mo) | 1,000/hr | 50,000/mo | uncapped | 10 |
| `terminal_pro` | Terminal Pro ($299/mo) | 5,000/hr | 250,000/mo | uncapped | 10 |

Terminal Pro additionally carries a commercial license. Backtest jobs are also
capped by in-flight concurrency (1 on free, 3 on paid).

Limits are rolling hourly AND monthly windows; whichever binds first returns `429`. Every response that reaches a handler — plus the `429` itself — carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (epoch seconds); a `429` adds `Retry-After`. `401` and `403` are rejected before the limiter runs and carry none. No overage billing, no metered top-ups. Plans: https://crowdintel.xyz/pricing

## Response conventions

- All JSON is `snake_case`.
- Errors: `{ "error": { "code", "message", "request_id" } }` with matching HTTP status. Codes: `unauthorized` (401), `forbidden` (403 — a per-plan allowance, e.g. webhook count, is exhausted), `rate_limited` (429), `not_found` (404), `bad_request` (400), `internal_error` (500). `request_id` is echoed in the `X-Request-Id` header.
- Cursor pagination: cursor-paginated list endpoints return `{ "data": [...], "next_cursor": "..." }`. Pass `next_cursor` back as `?cursor=` for the next page; it is `null` on the last page. A few bounded collections use a named key instead of `data` (e.g. `GET /v1/webhooks` returns `{ "webhooks": [...] }`, `/v1/clusters/sybil` returns `{ "clusters": [...] }`, `/v1/wallets/search` returns `{ "query", "results" }`) and carry no cursor. Single-resource GETs return a bare object with no envelope.

## Endpoints

All endpoints are `GET` unless noted. Every one of them answers on a free key;
the grouping below is by subject, and the rate limit is the only difference
between tiers.

### Core lookups

| Endpoint | What it returns |
|----------|-----------------|
| `/v1/wallets/{address}` | Wallet profile — aggregate stats for one proxy wallet. |
| `/v1/signals/wallet/{address}` | Wallet smart-money signal — score + confidence. |
| `/v1/signals/market/{slug}` | Market smart-money signal. |
| `/v1/cohorts` | The curated-cohort registry — every cohort's slug, membership floor, default ranking, accepted sort keys, and depth cap. No DB query; call it before spending quota on `/v1/cohorts/{slug}`. |
| `/v1/status/parity` | Data-quality transparency: nightly PnL parity scores vs Polymarket's own profit API, last 30 days. |

### Signals, wallets, markets, trades

Signals:

| Endpoint | Params |
|----------|--------|
| `/v1/signals/screen` | `min_edge` (per-category win rate minus global, e.g. 0.15), `min_bets`, `category`, `limit` (default 25, max 100), `cursor` — the edge screener for category specialists. |
| `/v1/signals/movers` | `period_hours` (default 24, max 168), `category`, `limit` (default 20, max 100) — market movers. |

Wallets:

| Endpoint | Params |
|----------|--------|
| `/v1/wallets/search` | `q` (min 3 chars — address fragment or Polymarket name), `limit` (default 10, max 25). |
| `/v1/wallets/{address}/categories` | Per-category stats for one wallet. |
| `/v1/wallets/{address}/timing` | Bet-timing distribution — how early the wallet enters vs resolution. |
| `/v1/wallets/{address}/correlated` | `limit` (default 5, max 20) — wallets that trade the same markets. |
| `/v1/wallets/{address}/connections` | `limit` (default 100, max 500) — funding-graph connections. |
| `/v1/wallets/{address}/trades` | `limit` (default 20, max 100), `cursor` — per-wallet trade history. |

Leaderboards:

| Endpoint | Params |
|----------|--------|
| `/v1/leaderboards/whales` | `sort` (`volume` \| `winRate` \| `zScore` \| `totalBets`, default `volume`), `min_volume`, `min_resolved_bets`, `limit` (default 50, max 100), `cursor`. |
| `/v1/leaderboards/pnl` | `sort_dir` (`asc` \| `desc`, default `desc`), `min_resolved_bets` (default 5), `limit`, `cursor`. |
| `/v1/leaderboards/category/{cat}` | `cat` (`politics` \| `crypto` \| `sports` \| `culture` \| `economics` \| `breaking-news`), `sort` (`win_rate` \| `counter_consensus_wins` \| `pnl` \| `volume`, default `pnl`), `min_bets` (default 20), `limit`, `cursor`. |

Cohorts:

| Endpoint | Params |
|----------|--------|
| `/v1/cohorts/{slug}` | `slug` (from `/v1/cohorts`), `sort` (`pnl` \| `volume` \| `resolved_bets` \| `win_rate`, default = the cohort's own `order_by`; must be in its `sortable` list), `sort_dir` (`asc` \| `desc`, default `desc`), `min_bets` (raises the cohort's resolved-bet floor — it can only raise it), `limit` (default 50, max 100), `cursor` — the ranked members of one curated cohort. |

A cohort is a named, floor-gated group of wallets. Every row in the page clears every line of the cohort's floor, so the page needs no further filtering. `GET /v1/cohorts` (above) is the registry: it returns each cohort's `floors` (machine-readable) and `floor_lines` (the same thresholds as display copy) — read the floor from there instead of hardcoding one.

Seven cohorts are enabled today: `profit-leaders`, `six-figure-club`, `deep-sample-winners`, `high-confidence-sharps`, `whale-class`, `fresh-faces-90d`, `market-maker-bots`. Six category cohorts (`best-sports-traders`, `best-politics-traders`, `best-crypto-traders`, `best-economics-traders`, `best-culture-traders`, `best-breaking-news-traders`) are **listed by `/v1/cohorts` with `enabled: false` and a public `disabled_reason`**; requesting one from `/v1/cohorts/{slug}` returns `400 bad_request` quoting that reason rather than a `200` with an empty page. Branch on `enabled`, not on a hardcoded slug list.

Four more things to know:
- Membership is recomputed on read from pre-aggregated wallet rollups — a wallet that stops clearing the floor leaves the cohort. These aggregates lag, so the response reports `data_as_of` (freshest underlying rollup timestamp, `null` on an empty page); do not present the figures as live.
- Ranking is PnL-first. `pnl` is Polymarket's own reported figure when present, else CrowdIntel's on-chain ledger figure (`polymarket_pnl` and `ledger_pnl` are both returned so you can tell which).
- `sort=win_rate` ranks on the sample-shrunk `bayesian_win_rate`. The raw per-fill `win_rate` column is returned for continuity but is contaminated — it is never a sort key and never a floor. **No cohort has a win-rate floor.**
- Per-request context arrives as siblings of `data`: `slug`, `title`, `thesis`, `member_count` (wallets clearing the cohort's own floor in total — unaffected by `min_bets`, `limit`, or paging, and it drifts as the rollups are rewritten), `data_as_of`, `floor_lines`, `sort`, `sort_dir`, `min_bets`. `free` keys see the top 25 with no pagination; `terminal`+ paginate to the cohort's `max_rows` cap (500), and a cursor past it returns `400`.

Unrelated to the `cohort` field in `/v1/status/parity`, which names a parity *sampling* cohort (`random` | `top`) — different concept, same word.

Markets:

| Endpoint | Params |
|----------|--------|
| `/v1/markets` | `q` (title substring, min 3 chars; relevance-ordered, first 200 matches), `category` (canonical slug), `limit` (default 25; max 25 free / 100 terminal / 250 terminal_pro), `cursor` — list, search and filter the ~2.1M-market catalog. |
| `/v1/markets/{slug}` | Market detail — volume, flow, resolution, top holders, recent trades. |
| `/v1/markets/{slug}/sentiment` | Whale sentiment + implied probability vs price. |
| `/v1/markets/{slug}/flow` | `minutes` (default 60, range 5-1440), `whales_only` (default true — wallets with >$50k total volume) — order flow. |
| `/v1/markets/{slug}/candles` | `interval` (1m/5m/15m/30m/1h/4h/1d, default 1h), `from`, `to` (ISO-8601 or unix-ms), `token_id`, `limit` (default 500, max 1000), `cursor` — full-lifetime OHLCV. |
| `/v1/markets/{slug}/twap` | `window` (30 \| 60 seconds, default 30) — the Chainlink observations across the window that settles a crypto up/down market. |
| `/v1/markets/trending` | `period_hours` (default 24, max 168), `category`, `search` (title substring), `limit`, `cursor`. |
| `/v1/markets/moving` | Same params as trending — biggest price moves. |

Historical price series live on `/v1/markets/{slug}/candles`. It is the only endpoint here without a retention window — every other market surface is capped (`/flow` at 24h, trending/moving at 168h). Buckets return `open`, `high`, `low`, `close`, `vwap`, `volume_shares`, `volume_usd`, `trades`, `buys`, `sells`, oldest first, cursor-paged.

Two things to know before charting it:
- The `{slug}` segment also accepts a raw `condition_id` (`0x` + 64 hex). Some markets have on-chain history but no catalog slug, and this is the only way to reach them.
- Every bucket carries `quality`. `degraded` means the fills in that window are not attributable to an outcome, so **volume is understated there** — price is unaffected. Filter on it rather than assuming a volume dip is real.

Crypto settlement (TWAP):

| Endpoint | Params |
|----------|--------|
| `/v1/crypto/twap` | `symbol` (e.g. `btc/usd`), `window` (0 \| 30 \| 60 seconds; omit for all), `from`, `to` (ISO-8601 or unix-ms), `limit` (default 1000, max 5000), `cursor`. |
| `/v1/crypto/twap/coverage` | No params — ticks held per symbol and window, with the first and last observation. |

Since 2026-08-07 00:00 UTC, Polymarket settles crypto up/down markets on a Chainlink TWAP instead of a single price snapshot: a **30-second** window for 5-minute markets, **60-second** for 15-minute ones. `window=0` is the Chainlink spot reference carried alongside, which is what makes spot-vs-settlement divergence measurable. Eight symbols publish: `btc`, `eth`, `sol`, `xrp`, `bnb`, `doge`, `zec`, `hype` (all `/usd`), about one observation per second each.

Three things to know before trading against it:
- **Settle on `value_e18`, not `value`.** `value_e18` is the exact E18 fixed-point integer as a string; `value` is a convenience float and is lossy above 2^53, which BTC already exceeds.
- **Coverage has a hard floor and no backfill.** The upstream feed is live-stream only, so nothing before our capture start exists or ever will. Always read `/v1/crypto/twap/coverage` before assuming a quiet range is quiet — check it against `first_ts`.
- **`/v1/markets/{slug}/twap` returns a `coverage` verdict** of `none`, `partial` or `complete` alongside `window_mean`. An empty `data` array alone cannot tell you whether the window was unrecorded or simply flat. `window` is your input, not something derived from the market: the catalog cannot say whether a given market is the 5-minute or 15-minute kind, so pick the window that matches the market you are trading. Equity and index up/down markets (`aapl-`, `spx-`) return `404` — they settle on Pyth feeds that are not recorded here.

Trades:

| Endpoint | Params |
|----------|--------|
| `/v1/trades` | `wallet`, `market` (condition id), `side` (`BUY` \| `SELL`), `min_bet` (USD), `start`/`end` (ISO-8601 or unix-ms; required together when no wallet/market filter; window capped at 90 days), `limit` (default 25, max 100), `cursor`. |
| `/v1/trades/{txHash}` | One trade fill by transaction hash. |

Clusters:

| Endpoint | What it returns |
|----------|-----------------|
| `/v1/clusters/by-id/{id}` | One funding cluster by id. |

### Backtests, clusters, investigations, webhooks

| Endpoint | Params / notes |
|----------|----------------|
| `/v1/analyze/market/{slug}` | On-demand market analysis object. |
| `/v1/clusters/sybil` | `sort` (`members` \| `volume` \| `pnl`, default `members`), `limit` (default 100, max 200) — coordinated wallet groups. |
| `/v1/clusters/{funder}/graph` | Funding-cluster graph for one root funder. |
| `/v1/investigations` | `type`, `category`, `limit` (default 20, max 100), `cursor` — curated investigations. |
| `/v1/investigations/{id}` | One investigation. |
| `POST /v1/backtest/run` | Enqueue a backtest job (see Backtest workflow below). |
| `/v1/backtest/{job_id}` | Poll a backtest job: `queued` → `running` → `done` (result: equity curve + metrics) or `error`. Jobs are visible only to the submitting token's owner. |
| `POST /v1/webhooks` | Register a callback: `callback_url` (https only, publicly routable host), optional top-level filters `min_score` (0-100), `score_type` (`whale` \| `insider`), `category`. `201` returns an HMAC secret ONCE. Max 10 subscriptions per user (exceeding it is a `400`, not `409`). Deliveries are POSTs signed with `X-CrowdIntel-Signature`, with bounded retries. |
| `/v1/webhooks` | List your subscriptions as `{ "webhooks": [...] }` — no cursor, `secret` always `null`. |
| `DELETE /v1/webhooks/{id}` | Remove a subscription. Returns `200 { "id", "deleted": true }` (not `204`); unknown or other-account ids return `404`. There is no `PATCH` — delete + recreate rotates the secret. |

## Workflows

### Insider screen

1. `GET /v1/signals/screen?min_edge=0.15&min_bets=20` — wallets whose per-category win rate beats their global rate by ≥15 points over ≥20 resolved bets.
2. For each hit: `GET /v1/wallets/{address}/timing` — do they enter unusually early before resolution?
3. `GET /v1/wallets/{address}/connections` — is the "edge" actually one coordinated cluster?
4. `GET /v1/markets/{slug}/flow?whales_only=true` — confirm where smart money sits on the specific market.

### Cohort → copy-trade candidates

1. `GET /v1/cohorts` — pick a cohort whose `floor_lines` match your bar (e.g. `deep-sample-winners` for track-record depth, `six-figure-club` for size). Skip anything with `enabled: false`.
2. `GET /v1/cohorts/{slug}?sort=pnl&limit=50` — the head of that board. Every row already clears the floor; raise it further with `min_bets` if you want a deeper sample than the cohort requires. Check `data_as_of` before quoting any figure.
3. Mind the machines: `market-maker-bots` is a cohort you filter *against*, not a shortlist. Cohorts whose `floors.exclude_machines` is true have already dropped bots; the ones that have not (`whale-class`, `market-maker-bots`) can contain them — read `wallet_class` on each row.
4. Vet each candidate with the copy-trading pipeline below (steps 2-5) before risking money.

### Copy-trading pipeline

1. Screen candidates: `GET /v1/cohorts/{slug}` (above), `GET /v1/leaderboards/category/{cat}?sort=pnl&min_bets=20`, or the edge screener above.
2. Vet: `GET /v1/signals/wallet/{address}` (any valid key) + `/v1/wallets/{address}/categories` + `/timing`.
3. Rule out coordination: `/v1/wallets/{address}/correlated` + `/connections`.
4. Follow fills: poll `GET /v1/wallets/{address}/trades` with `cursor`, or push via `POST /v1/webhooks` (1 endpoint on free, 10 on paid).
5. Validate before risking money: run a backtest (below), and read https://crowdintel.xyz/docs/copy-trading-polymarket — naive copying loses to slippage and survivorship bias.

### Backtest

`POST /v1/backtest/run` (3 runs/mo on free, uncapped on paid) with a JSON body:

```json
{
  "signal": "whale | insider | all (default all)",
  "filters": {
    "min_score": 0, "max_score": 100,
    "category": "politics",
    "min_odds": 0.0, "max_odds": 1.0,
    "min_bet_value": 0
  },
  "period": { "from": "2025-01-01", "to": "2026-01-01" },
  "stake_rule": { "kind": "flat | fractional", "size": 100, "fraction": 0.02, "max_fraction": 0.1, "scale_by_score": false },
  "fees": { "fee_bps": 0, "slippage_bps": 0 },
  "initial_capital": 10000
}
```

Every field is optional — an empty body `{}` backtests everything with defaults — but unknown fields are rejected with `400`, and the body must be valid JSON (a bodyless POST is a `400`). Guardrails: max 3 queued/running jobs per key (`429 rate_limited` — poll your jobs, don't sleep for the quota window), simulation window capped at 1,095 days, `503` with code `internal_error` and no `Retry-After` when the global queue hits 50. No `Idempotency-Key`: a retried POST burns another slot.

Returns `202 { "job_id", "status": "queued", "poll" }`; poll `GET /v1/backtest/{job_id}` until `done` (result holds the equity curve, metrics, and documented assumptions) or `error`. Jobs are scoped to the submitting key's owner — someone else's id returns `404`.

## Prefer MCP? (agent-native alternative)

The same intelligence is exposed as a remote MCP server — the better choice when an AI client (Claude Desktop, Claude Code, Cursor, Zed) should query the data conversationally instead of you writing HTTP code. MCP is free on every plan too — 20 calls/day on free, 50 on Pro ($29), 300 on Terminal ($99), 1,500 on Terminal Pro ($299).

- Endpoint: `https://mcp.crowdintel.xyz/mcp`
- Transport: remote Streamable HTTP (stateless JSON)
- Auth: `Authorization: Bearer cint_mcp_…` on every request (mint at Account → API, Pro plan+)
- Limits: daily call caps by plan — 20 (free) / 50 (Pro) / 300 (Terminal) / 1,500 (Terminal Pro) — plus a 1,000/hr ceiling
- Read-only: no tool can mutate state

Claude Desktop config (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "crowdintel": {
      "type": "http",
      "url": "https://mcp.crowdintel.xyz/mcp",
      "headers": { "Authorization": "Bearer cint_mcp_YOUR_TOKEN_HERE" }
    }
  }
}
```

Cursor uses the same URL/headers in `~/.cursor/mcp.json`. Full setup: https://crowdintel.xyz/mcp

The 18 tools:

- Wallets: `get_wallet_stats`, `get_wallet_alerts`, `search_wallets`, `get_wallet_timing`, `get_leaderboard`
- Cohorts: `list_cohorts` (registry — call it first for slugs, floors and sort keys), `get_cohort` (ranked members of one cohort)
- Clusters: `get_cluster`, `find_funding_clusters`
- Markets: `get_market`, `get_market_alerts`, `get_market_flow`
- Investigations: `get_investigation`, `list_investigations`
- Alpha discovery: `scan_insider_signals`, `screen_wallets_by_edge`, `get_market_movers`, `get_whale_activity`

## Quick smoke test

```bash
curl https://crowdintel.xyz/api/v1/signals/wallet/0xYOUR_WALLET \
  -H "Authorization: Bearer cint_api_xxxxxxxxxxxxxxxxxxxxxxxx"
```

## More

- Docs home: https://crowdintel.xyz/docs · Quickstart: https://crowdintel.xyz/docs/quickstart
- Guides: https://crowdintel.xyz/docs/how-to-find-insiders · https://crowdintel.xyz/docs/how-to-track-a-whale · https://crowdintel.xyz/docs/how-to-read-an-investigation
- Methodology: https://crowdintel.xyz/methodology · Glossary: https://crowdintel.xyz/glossary
- Product: https://crowdintel.xyz/whales (leaderboard) · https://crowdintel.xyz/investigations · https://crowdintel.xyz/insider-radar
- Pricing: https://crowdintel.xyz/pricing · llms.txt: https://crowdintel.xyz/llms.txt
