Same firms, verified payouts, and RiseUSD cash-on-hand as the public site — keyed access, cursor pagination, decimal money strings.
Read-only. No writes, no scrape, no second index. Every response is built from the same path as onthetape.io.
API_V1_DEV_KEY (or leave unset and the process mints a one-shot ott_dev_… key when API_KEYS is empty and env is not production). Print it from process logs / your secret store — never commit it.API_KEYS as id:sha256hex:scopes (comma-separated). Scopes: read:firms read:payouts read:cash read:boards read:meta or read:*./docs/api. Machine spec: /v1/openapi.json.# Health (no key) curl -sS https://onthetape.io/v1/health | jq . # Firms (24h ranking) curl -sS -H "Authorization: Bearer $ONTATAPE_API_KEY" \ 'https://onthetape.io/v1/firms?window=24h&limit=20' | jq . # Recent verified payouts curl -sS -H "X-Api-Key: $ONTATAPE_API_KEY" \ 'https://onthetape.io/v1/payouts?limit=30' | jq . # Cash on hand (all mapped RiseUSD treasuries) curl -sS -H "Authorization: Bearer $ONTATAPE_API_KEY" \ 'https://onthetape.io/v1/cash-on-hand?limit=50' | jq . # One firm treasury curl -sS -H "Authorization: Bearer $ONTATAPE_API_KEY" \ 'https://onthetape.io/v1/firms/tradeify-futures/cash-on-hand' | jq .
Send the raw API key (not the SHA-256 digest) on every keyed request.
Authorization: Bearer <key>X-Api-Key: <key>/docs/api, /v1/health, /v1/openapi.json/v1/* routes (firms, payouts, cash-on-hand, search, boards, meta, …)Missing / invalid key → 401 unauthorized. Missing scope → 403 forbidden.
Default: 120 requests / 60 seconds per API key (API_V1_RATE_LIMIT). Exceeding the quota returns 429 rate_limited with Retry-After: 60.
All money fields are objects — never JSON numbers:
{
"amount": "1234.56",
"currency": "USD"
}
Parse amount as a decimal string. Currency is always USD for v1.
Cursor style on list endpoints. Query: limit (default 50, max 200) and cursor.
{
"pagination": {
"next_cursor": "c32",
"has_more": true,
"limit": 50,
"count": 50
}
}
When has_more is true, pass next_cursor as cursor on the next request. Opaque — do not invent cursors.
Every successful envelope includes meta.generated_at, meta.as_of, meta.request_id, and meta.api_version.
as_of is the observation time for the underlying book (may equal generated_at).Full schema: /v1/openapi.json. Windows: 24h 7d 30d 365d ytd alltime.
| Method | Path | Scope | Notes |
|---|---|---|---|
| GET | /v1/health | — | Public liveness |
| GET | /v1/openapi.json | — | OpenAPI 3.1 |
| GET | /v1/meta | read:meta | Capabilities |
| GET | /v1/firms | read:firms | window q limit cursor |
| GET | /v1/firms/{id} | read:firms | Detail + cash + pulse summary |
| GET | /v1/firms/{id}/payouts | read:payouts | Firm tape |
| GET | /v1/firms/{id}/cash-on-hand | read:cash | Latest RiseUSD |
| GET | /v1/payouts | read:payouts | firm_id min_amount |
| GET | /v1/cash-on-hand | read:cash | All mapped treasuries |
| GET | /v1/treasuries · /v1/inflows · /v1/cash | read:cash | Radar aliases |
| GET | /v1/stats | read:firms | Window rankings |
| GET | /v1/whales | read:payouts | Pseudonymous tags |
| GET | /v1/topstep · /v1/lucid | read:boards | Published boards |
| GET | /v1/search?q= | read:firms | Firms + recent payouts |
| GET | /v1/sources | read:meta | Lane health |
| GET | /v1/stream | * | SSE URL + poll fallback |
Errors are JSON with HTTP status and a stable error.code:
{
"error": {
"code": "unauthorized",
"message": "…",
"request_id": "req_…",
"details": {}
}
}
| HTTP | code | When |
|---|---|---|
| 400 | bad_request | Invalid firm_id, short search q, etc. |
| 401 | unauthorized | Missing / invalid / unconfigured API key |
| 403 | forbidden | Key lacks required scope |
| 404 | not_found | Unknown path, firm, payout, or treasury |
| 429 | rate_limited | Over key quota; Retry-After: 60 |
| 500 | internal | Unexpected server error |
Machine-readable OpenAPI 3.1 document (public, no key):
curl -sS https://onthetape.io/v1/openapi.json | jq '.info, (.paths | keys)'
Full sync patterns and money/as_of rules also live in the repo at docs/integration.md.
Stdio MCP server at site/mcp_server.py. Tools wrap the same /v1 handlers (HTTP when a base URL is set; in-process for unit tests).
| Variable | Required | Notes |
|---|---|---|
ONTATAPE_API_KEY | preferred | Partner key (raw secret). Falls back to API_V1_DEV_KEY. |
API_V1_DEV_KEY | local alt | Same key the site process uses in non-production. |
ONTATAPE_API_BASE | recommended | Live site base, e.g. https://onthetape.io or http://127.0.0.1:8080. Without it, tools call handle_v1 in-process (empty index unless the site module is already loaded). |
export ONTATAPE_API_KEY=your_key export ONTATAPE_API_BASE=https://onthetape.io python3 site/mcp_server.py
Docker Compose: docker compose --profile mcp run --rm mcp (sets base to the compose api service).
| Tool | Maps to |
|---|---|
get_capabilities | GET /v1/meta |
list_firms | GET /v1/firms |
get_firm | GET /v1/firms/{firm_id} |
list_payouts | GET /v1/payouts |
get_cash_on_hand | GET /v1/firms/{firm_id}/cash-on-hand |
list_cash_on_hand | GET /v1/cash-on-hand |
search | GET /v1/search?q= |
get_board | GET /v1/topstep or /v1/lucid |
get_health | GET /v1/health (public) |
mcp.json){
"mcpServers": {
"onthetape": {
"command": "python3",
"args": ["/absolute/path/to/site/mcp_server.py"],
"env": {
"ONTATAPE_API_KEY": "your_key",
"ONTATAPE_API_BASE": "https://onthetape.io"
}
}
}
}
Never commit the key. Prefer a secret store / local env override.