Skip to content

Generate your Agent API Key

Free users can generate a read-only Agent API Key for MCP tools with signals:read and 200 MCP requests/day. Pro adds real-trade REST/MCP scopes; Elite adds full agent write access.

PolyTrackers API (External + AI Agent Guide)

This is the production-facing API guide for external developers and AI agents.


Scope

This guide/spec covers the full AI agent surface:

  • Auth: /api/auth/refresh
  • Account/Tier: /api/account, /api/account/stats, /api/account/streak, /api/account/risk-profile
  • Discovery: /api/markets, /api/market-signals, /api/leaderboard, /api/clob/price, /api/trader-lookup
  • Anomalies: /api/anomalies, /api/anomalies/{id}, /api/anomalies/performance, /api/scan/trigger, /api/scan/run
  • Backtesting: /api/backtest
  • Recommendations, activity & feedback: /api/recommendations, /api/bets/sync, /api/feedback
  • Agent API Keys: /api/keys, /api/keys/generate, plus public feeds
  • Trading: /api/trades, /api/trade/execute, /api/trade/history, /api/trade/ledger, /api/trade/money-summary, /api/trade/orders*
  • Alerts & webhooks: /api/alerts/preferences, /api/webhooks*
  • Whale service + ingest webhooks: wallets/roster/whale control + /api/whale/webhook*

Excluded intentionally:

  • /api/mock/* internal/MCP-only simulation helpers (use the registered pt_mock_* tools)
  • /api/cron/* scheduled internal jobs
  • /api/admin/*, /api/stripe/*, /api/telegram/*, /api/onboarding/* and other UI/operator-only routes

The MCP-only pt_whale_forward_returns_get read accepts 1–50 arbitrary wallet entries with exact per-wallet since timestamps and a shared as_of cutoff. It returns one request-ordered row per entry with bounded fill coverage, gross/post-cost capital-accounting ROI, unresolved capital, resolution provenance, and isolated ok|partial|error state. Its Data API and Gamma calls use the low-priority backfill request budget and are counted in _budget. Zero resolved capital produces null ROI; compute cohort medians client-side over non-null rows. Re-runs at the same cutoff may incorporate later authoritative Gamma outcome/closedTime evidence as corrected upstream data.

The Elite, read-only pt_copy_replication_aggregates_get tool is MCP-only, operator-allowlisted through ADMIN_EMAILS, and has no public REST or browser route. Ordinary customer keys fail closed. It requires one exact whale, fixes the service-role Copy Receipt V1 RPC limit at one, and uses index-backed lookup with a caller-side five-second deadline that aborts the PostgREST request. Scored metrics stay null below 20 distinct settled conditions and high confidence still requires 40+ conditions with at least 75% complete receipts. Do not present it as a public copy score or recommendation.

GET /api/recommendations includes the stored botLikeness aggregate. A high tier adds a copy-fill-lag warning and demotes matchScore; the response never includes raw Polymarket trade history. Rows also include tradingStyle, a deduped BUY entry-price distribution with sample/window metadata. Its badge classification is withheld below 20 fills; sufficiently sampled distributions also contribute to deterministic risk-fit scoring. Recommendation rows also expose name_source (tracked_alias, leaderboard, or address_fallback), name_as_of, and Polymarket's rename-stable pseudonym when available. Address-prefixed legacy aliases are treated as absent; wallet address remains the identity key.

GET /api/leaderboard and GET /api/recommendations include upstream metadata for the shared Polymarket leaderboard fetch. Check its per-timeframe outcomes plus degraded and stale before interpreting an empty candidate pool. A stale response is explicitly labeled, retains the original fetchedAt timestamp, and is bounded to the latest fully healthy snapshot from the prior 24 hours. staleExpiresAt is the absolute cutoff; public, REST, and MCP response layers do not cache leaderboard payloads beyond the durable 10-minute Redis cadence, and each response revalidates after downstream enrichment so an embedded fallback is removed at that cutoff. Candidate actionability also distinguishes known dormancy from missing ingestion coverage: zero local activity inside the warning window for an untracked, newly tracked, or recently resumed wallet returns warning-only ACTIVITY_COVERAGE_UNKNOWN plus recent_activity.source/coverage metadata, never DORMANT_WHALE. MCP capability revisions are roi-sample-provenance-v6 for the leaderboard, recommendations-prior-removal-v5 for recommendations, and digest-prior-removal-v13 for the copy-trading digest. Wallet-shaped suggestion/trader rows use the canonical recent-flow majority predicate, filter majority-ephemeral and unknown-coverage rows under durable_only, retain dormant wallets, and expose bounded durability_profile provenance.

Recommendation and digest-suggestion rows expose previously_removed_by_caller by normalized wallet address. It is null or contains { count, last_removed_at, last_reason }, where count covers currently inactive assignments across the caller's mock wallets rather than lifetime removal cycles. These candidates remain visible with a warning by default. REST accepts exclude_previously_removed=true|false; direct MCP accepts the boolean exclude_previously_removed and binds it to continuation cursors. Filtered counts appear as omitted_by_prior_removal_filter.

PATCH /api/roster/{address} treats fromWalletId as the source-assignment selector. If one address has active assignments on multiple visible wallets, an address-only write fails with HTTP 409, code AMBIGUOUS_ASSIGNMENT, and candidate_wallet_ids rather than changing an arbitrary row.

POST /api/roster accepts any exact Polymarket whale wallet address; the whale does not need to appear in leaderboard or recommendation results. The walletId may select a caller-owned paper roster or dedicated real-money copy roster returned by GET /api/wallets. name is optional, and copyEnabled:false stages the assignment watch-only. When starting from a username, resolve and review the exact address with GET /api/trader-lookup before creating the assignment. Username resolution uses Polymarket's public profile search, then fetches and validates trades for only that canonical wallet; trade pages are never used to infer username ownership.


Auth modes

Most user-scoped agent endpoints accept authenticated user context via:

  • Supabase session cookie (sb-* managed cookies)
  • Authorization: Bearer <supabase-jwt-access-token>

2) JWT-only bearer

POST /api/keys/generate strictly verifies Supabase JWT bearer identity.

3) Agent API Key bearer

Agent API Keys are prefixed ptk_ and are passed as Authorization: Bearer <agent-api-key>. The same key works for PolyTrackers MCP server access and direct REST/API requests when it has the required scopes:

  • signals:read — read-only data
  • agent:scan — scan trigger where allowed
  • trade:execute — real trade execution only, after readiness and preflight gates pass
  • agent:full — full agent/write/trade access (currently Elite-only for Agent API Keys)

Endpoints that accept Agent API Keys:

EndpointRequired scopeDirect REST tier
GET /api/anomaliessignals:readPro+
GET /api/tradessignals:readFree+
GET /api/leaderboardsignals:readPro+
POST /api/backtestsignals:readFree+ (fixed demo config on Free)
GET /api/anomalies/performancesignals:readPro+
GET /api/public/whale-signalssignals:readElite
GET /api/public/copy-signalssignals:readElite
POST /api/trade/executetrade:executePro+
GET /api/trade/historyagent:fullFree+
GET /api/trade/ledgeragent:fullFree+
GET /api/trade/money-summaryagent:fullFree+
GET /api/trade/orders, GET /api/trade/orders/{id}agent:fullElite
DELETE /api/trade/orders/{id}agent:fullPro+
POST /api/feedbackagent:fullElite

The direct REST tier applies only when the bearer is a ptk_ Agent API Key; session-authenticated callers follow the tier behavior below. Free Agent API Keys work on POST /api/backtest, but only with the fixed demo configuration, and the returned trade list is capped at 5 rows. New agent:full keys are normally issued only to Elite accounts; the Pro+ cancellation tier allows an existing agent:full key to keep reducing exposure after an account downgrade.

4) Service auth

For whale-service control endpoints:

  • x-whale-service-secret: <secret> or
  • Authorization: Bearer <WHALE_SERVICE_SHARED_SECRET>

5) Scan secret

POST /api/scan/run also accepts x-scan-secret: <SCAN_SECRET> (bypasses the tier gate; used by cron/internal automation).

6) Webhook signature auth

POST /api/whale/webhook requires:

  • x-alchemy-signature = HMAC_SHA256(raw_body, ALCHEMY_WEBHOOK_SIGNING_KEY)

When a registration has a secret, outbound deliveries from /api/webhooks* include X-Webhook-Signature with the value sha256=<HMAC_SHA256(raw_body, secret)>. Compute the HMAC over the raw request body before parsing it.


Tier gating (important)

Effective tier resolution: a user on free with an active trial (trial_ends_at > now) is treated as pro. Tier failures return TIER_UPGRADE_REQUIRED with the required tier and an upgradeUrl.

Free

  • GET /api/trades: 7-day mock history plus full-depth own real-trade receipts.
  • GET /api/anomalies: max 5 results, 7-day base retention window — each activated referral adds 7 days, up to 28 days total — with a 1-hour visibility delay (detections newer than 1 hour are hidden).
  • POST /api/scan/trigger: not allowed (403).
  • /api/keys + POST /api/keys/generate: allowed; generated keys always get exactly signals:read (requested write/automation scopes are stripped). Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement. MCP usage is capped at 10 requests/min per tool and 200 requests/day total.
  • POST /api/trade/execute: session callers can execute only when manual trading-readiness checks pass. Agent API Key/MCP execution is unavailable because Free keys never carry trade:execute.
  • Session callers can read their own /api/trade/history, /api/trade/ledger, and /api/trade/money-summary; direct Agent API Key calls still require agent:full. Order listing/detail remain unavailable. DELETE /api/trade/orders/{id} may cancel the signed-in user's own resting order because cancellation reduces exposure.
  • /api/webhooks (create): not allowed (403).
  • /api/public/whale-signals, /api/public/copy-signals: not allowed (403).

Pro

  • GET /api/trades: 90-day mock history plus full-depth own real-trade receipts.
  • GET /api/anomalies: max 100 results, 90-day retention window, no delay.
  • POST /api/scan/trigger: allowed, rate limited to 2/hour.
  • /api/keys + POST /api/keys/generate: allowed; scopes default to signals:read, with optional trade:execute for real trade execution and agent:scan for scan automation where allowed. Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement.
  • /api/webhooks (create): up to 5 active webhooks.
  • /api/public/whale-signals, /api/public/copy-signals: not allowed (Elite only).
  • POST /api/trade/execute: session callers can execute only when manual trading-readiness checks pass. Agent API Key/MCP execution requires trade:execute scope.
  • Session callers can read their own /api/trade/history, /api/trade/ledger, and /api/trade/money-summary; direct Agent API Key calls still require agent:full. Order listing/detail remain unavailable. DELETE /api/trade/orders/{id} may cancel the signed-in user's own resting order.

Elite

  • GET /api/trades returns full-depth mock and own real-trade history.
  • GET /api/anomalies: max 200 results, full retention window.
  • POST /api/scan/trigger: allowed, rate limited to 10/hour.
  • /api/keys + POST /api/keys/generate: scopes may include signals:read, trade:execute, agent:scan, and/or agent:full. Limit: 1 active Agent API Key per user; rotate by revoking the old key, then generating a replacement.
  • /api/trade/execute, /api/trade/history, /api/trade/ledger, /api/trade/money-summary, and /api/trade/orders*: allowed where the route-specific readiness checks pass (trade:execute or agent:full for execute API-key auth). Browser/session order cancellation is allowed for the signed-in user's own resting order regardless of tier.
  • /api/public/whale-signals, /api/public/copy-signals: allowed.
  • /api/webhooks (create): up to 50 active webhooks.

Rate limits

Agent API Key REST limits use the scope shown below; MCP separately applies per-identity/per-tool and aggregate request buckets (see MCP rate limits). Session requests are limited per user, while the public discovery and refresh limits are per IP.

EndpointLimitScope
POST /api/auth/refresh30/minuteIP
Anonymous discovery GETsshared 60/minute anonymous budgetIP
POST /api/scan/triggerPro: 2/hour, Elite: 10/houruser
POST /api/keys/generate5/hour per useruser
POST /api/trade/execute10/minute per user + 20/minute per API keyuser + key
GET /api/public/whale-signals120/minute per API keyAPI key
GET /api/public/copy-signals120/minute per API keyAPI key

The shared anonymous discovery budget covers GET /api/markets, GET /api/events, GET /api/home-snapshot, and GET /api/market-signals. One anonymous caller consumes the same IP-scoped 60-request bucket across all four routes.

Rate limit response headers

Every rate-limited response — a success as well as a 429 — carries the RFC RateLimit-* quota view for the bucket that governed it. Read it and pace yourself; do not discover the ceiling by being refused.

HeaderMeaning
RateLimit-LimitEffective ceiling for this caller, after tier multipliers (Pro x2, Elite x5).
RateLimit-RemainingRequests left in the current window.
RateLimit-ResetDelta-seconds until the quota refills. Never a unix timestamp.
RateLimit-Policy"bucket";q=<limit>;w=<window seconds> — endpoints sharing a bucket share one budget.
bash
curl -si https://polytrackers.com/api/markets | grep -i '^ratelimit-'
RateLimit-Limit: 60 RateLimit-Remaining: 59 RateLimit-Reset: 60 RateLimit-Policy: "public-market-read";q=60;w=60

Rules a client can rely on:

  • On a 429, Retry-After stays the authoritative back-off signal, and RateLimit-Reset is never smaller than it. Sleeping for RateLimit-Reset is therefore always safe.
  • RateLimit-Reset rounds up, so it never expires early.
  • Paths with no limiter behind them omit the headers entirely rather than advertise a quota nobody enforces: /api/health, /api/cron/*, the signed provider webhooks, and the cached GET /api/mcp capability probe.
  • Two limiters signal with Retry-After and a structured body but publish no quota view: the POST /api/trade/execute per-API-key limiter, and POST /api/auth/refresh (whose 429 can be proxied from upstream). Treat a missing quota view as "unknown budget", never as "unlimited".
  • The MCP per-user/per-tool buckets are metered in-route and report retry_after_seconds inside the JSON-RPC error payload; the RateLimit-* headers on /api/mcp describe the per-IP transport cap only.

MCP rate limits

MCP (POST /api/mcp) is metered separately from the REST limits above, in three per-user layers plus one transport cap. All apply at the same time; the tightest one binds.

BucketFreeProEliteScope
Per-tool tools/call10/minute60/minute300/minuteuser, per tool name
Daily tools/call quota (all tools)200/dayno daily capno daily capuser
All JSON-RPC requests (setup + listings)120/minute600/minute1800/minuteuser

Transport caps on /api/mcp apply per IP before authentication, regardless of tier: 300/minute for authenticated traffic and 60/minute for the anonymous health probe and handshakes.

  • The per-tool bucket is keyed by tool name, so a burst on one tool does not consume another tool's budget. Blocked calls return RATE_LIMITED with retry_after_seconds.
  • The daily quota is consumed only after the per-minute bucket allows the call, so minute-throttled calls do not burn the daily budget.
  • The request bucket charges every authenticated POST one setup unit, plus one extra unit for catalog listings and resource/prompt reads. tools/call, initialize, ping, and notifications/* pay only the setup unit.
  • The transport IP caps are enforced before JSON-RPC handling and may return a non-JSON 429; treat any raw 429 as RATE_LIMITED and back off.

The same figures are published machine-readably in the polytrackers://mcp/capabilities resource (rate_limit_per_minute, rate_limit_per_day, transport_rate_limits.streamable_http).

404 response shape (unknown paths)

Any /api/** path that matches no endpoint returns HTTP 404 with a JSON body. It is never the HTML app shell, so a client can branch on the payload:

json
{
  "error": "Not Found",
  "code": "NOT_FOUND",
  "message": "No PolyTrackers API endpoint matches GET /api/nope. The OpenAPI contract lists every documented path.",
  "documentation": {
    "openapi": "https://polytrackers.com/openapi.json",
    "apiDocs": "https://polytrackers.com/docs/api",
    "llmsTxt": "https://polytrackers.com/llms.txt",
    "agentSkill": "https://polytrackers.com/skill.md",
    "mcp": "https://polytrackers.com/api/mcp"
  }
}

Send Accept: text/markdown for the same recovery links as a short markdown document. Both variants set Vary: Accept. NOT_FOUND is not retryable — re-read the OpenAPI contract instead of backing off.

Page (non-/api) paths keep serving the regular HTML 404 page.

Markdown content negotiation (public pages)

A curated set of public pages answers Accept: text/markdown with a short machine-readable summary instead of HTML — the page title, its description, and the discovery links to read next. The set is every static public page — the homepage, the documentation hub and API reference, the guides, the comparison pages, and the policy pages — reachable either by sending the header or by appending .md to the path (/docs -> /docs.md). Live-data pages (the leaderboard, anomalies, wallet tracker, analytics, search, and copy trading) are excluded on purpose: a summary of a continuously refreshed page would carry none of its data. public/llms.txt publishes the live list.

bash
curl -s -H 'Accept: text/markdown' https://polytrackers.com/docs/api

Rules:

  • The markdown body is a summary, not a transcription. Fetch the canonical HTML URL it names when you need the full document.
  • Both representations set Vary: Accept, so a shared cache cannot mix them.
  • Ranking honors q-values. text/markdown must outrank text/html; a tie serves HTML, because a client that accepts both can render the page.
  • An Accept that matches neither representation and carries no wildcard gets 406 Not Acceptable. A missing or wildcard Accept gets HTML.
  • Every other path ignores the header and serves HTML exactly as before.

429 response shape

Every 429 body includes "error" (string). The Retry-After header (seconds) is the authoritative back-off signal — it is present on all rate-limited responses (the one exception: 429s proxied from the upstream auth provider on POST /api/auth/refresh may omit it). Other body fields vary by endpoint, so treat them as optional:

Endpoint / limiter429 body
GET /api/public/whale-signals{ error }
GET /api/public/copy-signals{ error }
POST /api/trade/execute (per API key){ error, code: "API_KEY_RATE_LIMITED", retryAfter, docsUrl }
POST /api/trade/execute (per user){ error, code: "RATE_LIMITED" }
POST /api/keys/generate{ error, code: "RATE_LIMITED" }
POST /api/auth/refresh{ error, code: "RATE_LIMITED" }
POST /api/scan/trigger{ error }

The fullest shape — returned by the /api/trade/execute per-API-key limiter:

json
{
  "error": "This API key has exceeded its trade execution rate limit. …",
  "code": "API_KEY_RATE_LIMITED",
  "retryAfter": 7,
  "docsUrl": "https://polytrackers.com/docs/api"
}

Idempotency for /api/trade/execute

Trade execution is idempotent when you supply an Idempotency-Key header. For Agent API Key requests the header is required (400 IDEMPOTENCY_KEY_REQUIRED otherwise); session requests may still pass idempotencyKey in the body. Every /api/trade/execute body must also include regionCertification: true to certify the caller is permitted to trade in their jurisdiction and is not in a restricted or prohibited Polymarket trading region.

  • Accepted formats: UUIDv4 (preferred) or any 8–256 printable-ASCII token.

  • Retrying the same key replays the original trade result (200 with idempotencyReused: true) instead of re-executing. The replay window is indefinite — keys never expire. Generate one fresh key for each new trade intent. Reuse that same key for every retry or reconciliation attempt for that intent, including after an uncertain timeout or a TRADE_IN_FLIGHT response; never reuse the key for a different trade intent.

  • Two concurrent requests with the same key return 409 TRADE_IN_FLIGHT for the loser:

    json
    {
      "error": "trade_in_flight",
      "code": "TRADE_IN_FLIGHT",
      "idempotencyKey": "agent-trade-2026-04-20-001",
      "retryAfter": 5
    }

Lifecycle contradictions and order-book indicators

Agent API and MCP clients must treat Gamma lifecycle fields as conservative write gates. When pt_mock_price_get or a composed market-intel/copy-desk workflow reports PAST_END_DATE_BUT_MARKET_APPEARS_LIVE, that is contradiction evidence only; it is not permission to place, mirror, or override by default. Live CLOB/order-book indicators, bid/ask/spread rows, recent trades, or enableOrderBook signals do not override MARKET_PAST_END_DATE, MARKET_CLOSED, auth, tier, anti-IDOR, preflight/idempotency, wallet-readiness, region, or real-money execution safeguards.

A future manual/gated exception must prove the Gamma lifecycle field is stale, require explicit user/operator approval for the exact action, write an audit log, and must not broaden default Agent API/MCP write privileges.

Bot challenge (403)

Session traffic that BotID flags as automated receives:

json
{
  "error": "bot_challenge_failed",
  "reason": "automated_traffic_detected",
  "suggestedAction": "authenticate_with_api_key",
  "docsUrl": "https://polytrackers.com/docs/api"
}

Valid Authorization: Bearer ptk_… requests bypass BotID entirely — see llms.txt.


Quickstart (agent flow)

1) Sign in and obtain a session

PolyTrackers auth is passwordless. Create an account or sign in through the web app at /register or /login using Google OAuth or an emailed magic link (Terms of Service and Privacy Policy acceptance is captured during sign-up). This yields a Supabase session — a session cookie plus an access_token / refresh_token pair you can pass as a bearer token.

Exchange a refresh token for a fresh session pair when the access token nears expiry:

bash
curl -X POST "https://polytrackers.com/api/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"'"$REFRESH_TOKEN"'"}'

Response includes:

  • access_token
  • refresh_token
  • token_type
  • expires_in

2) Read account + tier

bash
curl "https://polytrackers.com/api/account" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Returns tier (effective tier, honoring active trial), trialEndsAt, apiKeysCount, walletsCount, onboarding + wallet-link status.

3) Discover markets

bash
curl "https://polytrackers.com/api/markets?query=btc&status=open&sort=volume&limit=25"

To decorate public market cards with coarse recent signal evidence, pass up to 100 comma-separated condition IDs. The response contains only 24-hour whale and anomaly booleans—no wallet, trade-size, price, or execution data:

bash
curl "https://polytrackers.com/api/market-signals?conditionIds=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

For one market or event, the anonymous detail endpoints return more than the list rows do. GET /api/markets/{slug} includes the full resolution-rules description plus a heuristic resolutionRisk score; GET /api/events/{slug} returns the event's complete outcome ladder (every priced leg, not just the legs on a paginated list page); GET /api/events/featured returns the current featured multi-outcome event, or {"event": null} when none qualifies:

bash
curl "https://polytrackers.com/api/markets/some-market-slug"
curl "https://polytrackers.com/api/events/some-event-slug"
curl "https://polytrackers.com/api/events/featured"

Anonymous requests to /api/markets, /api/events, /api/home-snapshot, and /api/market-signals share one 60 requests/minute per IP discovery budget. Pace discovery loops across the group and honor Retry-After on 429. The {slug} detail endpoints are metered separately: each has its own 60 requests/minute per IP bucket, and both can return 503 with reason: "upstream_request_budget_deferred" when the shared upstream request budget defers the fetch — honor retryAfterMs before retrying.

4) Trigger anomaly scan (Pro/Elite)

bash
curl -X POST "https://polytrackers.com/api/scan/trigger" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

5) Read anomalies

Session, or a Pro+ Agent API Key (signals:read):

bash
curl "https://polytrackers.com/api/anomalies?limit=50&severity=high" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Free-tier session callers see at most 5 rows, within their retention window (7-day base, up to 28 days with activated referral rewards), and only those detected more than 1 hour ago. Direct REST access with an Agent API Key requires Pro+.

6) Read anomaly detector performance

Aggregate win-rate, average return, and P&L for historical anomaly detections. Backed by the anomaly_performance_daily view.

bash
curl "https://polytrackers.com/api/anomalies/performance?since=2026-01-01T00:00:00Z&until=2026-04-01T00:00:00Z&group_by=month&anomaly_type=SHARP_MOVE" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Query parameters:

  • since, until — ISO-8601 datetimes. Defaults to the last 30 days; the window is capped at 365 days.
  • group_by — one of day (default), month, anomaly_type, severity, all.
  • anomaly_type, severity — optional filters passed straight through to the view.

Response body:

  • window{ since, until } echoing the resolved window (ISO-8601).
  • totals — aggregate { flagged, resolved, wins, losses, voids, winRate, avgReturnPct, totalPnlUsd, equalDollarPnlPer100 } across the window.
  • series — same shape as totals but keyed by cohort (the group_by bucket), sorted ascending.
  • coverageresolved / flagged for the window (0–1).
  • coverageThreshold — the coverage at which the cohort is considered mature enough to surface headline numbers (currently 0.5).
  • minDecidedForHeadline — the absolute number of decided outcomes (wins + losses) that is, on its own, enough to surface headline numbers (currently 20).
  • headlineReliablecoverage >= coverageThreshold || (wins + losses) >= minDecidedForHeadline. Because markets resolve long after detection, a window can have low coverage while still holding a large, meaningful sample of decided outcomes; either condition makes the win rate reliable.

equalDollarPnlPer100 is a simulation: $100 flat-staked on every decided anomaly in the cohort, expressed as total dollars of P&L.

Accepts session auth (cookie/JWT) or a Pro+ Agent API Key with signals:read scope. Responses are cached privately with s-maxage=60, stale-while-revalidate=300.

7) Read unified trade history

bash
curl "https://polytrackers.com/api/trades?type=all&status=all&sort=newest&limit=25&offset=0" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Response body includes tier and historyDepthDays; the latter is the mock history window (7 for Free, 90 for Pro, null for Elite). Own real-trade receipts are full-depth at every tier. For real rows, status describes the position: a filled BUY remains open until resolution or exit accounting is recorded. rawStatus keeps the order lifecycle, and closedAt is only an actual resolution/settlement time—never a bookkeeping updated_at value. Real rows also carry failureReason and failureDetail explaining why a failed/cancelled attempt terminated; both are null on healthy rows. fillAmount, requestedShares, and price expose the durable execution facts; isDustFill is derived when filled shares are below the applicable market share minimum, executed notional is below $1, or the fill is under 10% of requested shares. Execution reuses live market/book metadata; bounded list and metric reads use the centralized conservative fallback and make no per-row provider calls. Dust remains real P&L but is excluded from win-rate and mock↔real parity grading. payoutReconciliationStatus carries the receipt-backed payout state, while modeledPayout is the deterministic binary/SPLIT estimate used during reconciliation and must not be presented as observed spendable cash.

A type=real&status=executed response also includes realWalletStats. That object is aggregated server-side over every matching executed real-trade row, independent of limit/offset, and reports realized P&L, all-time decided-BUY cost basis/ROI inputs, open positions, and resolved rows still awaiting P&L attribution. The trades array remains paginated.

conditionId narrows the feed to one exact Polymarket condition ID. Offset pagination is reliable only before offset 1000 because PostgREST caps each source window. A page that reaches that boundary returns its reachable prefix as an explicit terminal page with pagination.depthLimited: true, depthLimit: 1000, and depthLimitReason: "postgrest_max_rows"; narrow by type, status, wallet, or condition instead of continuing.

When the wallet-scope integrity tripwire drops a foreign raw row, the returned trade rows remain verified against the requested wallet but count metadata cannot be proven from the same suspect result set. The response therefore adds pagination.degraded: true, degradedReason: "wallet_scope_invariant_violated", degradedFields: ["total", "hasMore"], and droppedRows. Treat total and hasMore as untrusted for that incident response; normal-path pagination is unchanged.

Direct REST access with an Agent API Key requires signals:read.

8) Run a historical backtest

Free Agent API Keys can run the fixed demo configuration with signals:read; the returned trade list is capped at 5 rows. Pro and Elite keys can customize the configuration within their tier's window limits.

bash
curl -X POST "https://polytrackers.com/api/backtest" \
  -H "Authorization: Bearer $AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source":{"kind":"anomalies","minSeverity":"HIGH"},
    "stake":{"mode":"flat","amountUsd":100},
    "startingBankrollUsd":1000,
    "windowDays":30,
    "slippageBps":50,
    "feeBps":0
  }'

9) Readiness-gated real trade execution

Session auth requires manual trading readiness: trading wallet/signing setup, wallet funding, wallet risk config, circuit-breaker state, idempotency, reservations, region certification, and audit logging.

Session auth:

bash
curl -X POST "https://polytrackers.com/api/trade/execute" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "conditionId":"0xconditionid",
    "side":"YES",
    "amount":20,
    "limitPrice":0.5,
    "idempotencyKey":"agent-trade-2026-04-16-001",
    "regionCertification":true
  }'

Agent API Key with trade:execute scope:

bash
curl -X POST "https://polytrackers.com/api/trade/execute" \
  -H "Authorization: Bearer $AGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c2b7f9e-47b8-4ad2-95b5-8f8d5f2f6a3c" \
  -d '{"conditionId":"0x...","side":"YES","amount":20,"regionCertification":true}'

CDP delegated-wallet browser users must submit explicit limit orders until the SDK adapter supports cap-priced immediate-or-cancel market orders. Non-CDP market buys retain the server-side slippage-cap path.

10) Cancel an own resting real order

Session callers can cancel their own resting order even when their tier cannot list all CLOB orders:

bash
curl -X DELETE "https://polytrackers.com/api/trade/orders/$ORDER_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

The two programmatic paths have different gates: direct REST bearer calls to GET /api/trade/history, GET /api/trade/ledger, GET /api/trade/money-summary, and GET /api/trade/orders* require agent:full. MCP pt_trade_history_get requires signals:read at every tier, while pt_trade_orders_list and pt_trade_orders_get require signals:read plus the Elite tier.


Agent API Key lifecycle

Generate Agent API Key (JWT bearer only)

Free callers can generate read-only Agent API Keys for MCP access; requested scopes are reduced to signals:read and MCP usage is capped at 200 requests/day. Pro callers may request signals:read, the narrow trade:execute scope, and the narrow agent:scan scope; Elite callers may additionally request agent:full. Accounts keep 1 active Agent API Key; rotate by revoking the old key, then generating a replacement and updating clients and agents (409 API_KEY_LIMIT_REACHED when the cap is reached).

bash
curl -X POST "https://polytrackers.com/api/keys/generate" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"nickname":"Agent API Key","expiresInDays":90,"scopes":["signals:read"]}'
  • expiresInDays: 1–365, defaults to 90.
  • scopes: defaults to ["signals:read"]. Free keys are always reduced to signals:read; paid-tier validation still applies outside the free MCP/read surface. Add trade:execute only when intentionally creating a real-trading key. Pro-tier requests for agent:full are stripped, while trade:execute and agent:scan are allowed where their automation surfaces are enabled.
  • The raw api_key is returned once in the response — store it securely.

List keys

bash
curl "https://polytrackers.com/api/keys" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Use Agent API Key for public whale feed (Elite)

bash
curl "https://polytrackers.com/api/public/whale-signals?limit=25" \
  -H "Authorization: Bearer $AGENT_API_KEY"

Outbound webhooks (Pro/Elite)

Create, list, delete, and test delivery endpoints live under /api/webhooks*:

  • GET /api/webhooks — list your registrations.
  • POST /api/webhooks — register a new URL with optional filters (minScore, severity[], anomalyTypes[], marketCategories[]) and a secret. Capped at 5 (Pro) / 50 (Elite); over-limit returns WEBHOOK_LIMIT_REACHED.
  • DELETE /api/webhooks/{id} — soft-delete a registration. The row is retained for audit (with deleted_at stamped and status=paused), it will stop receiving future deliveries, and historical delivery_log entries remain queryable.
  • GET /api/webhooks/{id}/deliveries — recent delivery logs.
  • POST /api/webhooks/{id}/test — fire a test payload.

Webhook delivery contract

Matching anomaly alerts are delivered as anomaly.detected JSON:

json
{
  "event": "anomaly.detected",
  "timestamp": "2026-07-20T14:30:00.000Z",
  "anomaly": {
    "id": "anomaly-id",
    "score": 82,
    "market_title": "Will BTC reach $150k in 2026?",
    "market_slug": "will-btc-reach-150k-in-2026",
    "market_url": "https://polymarket.com/event/will-btc-reach-150k-in-2026",
    "current_price": 0.64,
    "anomaly_type": "WHALE_ANOMALY",
    "severity": "HIGH",
    "side": "BUY",
    "outcome": "YES",
    "bet_recommendation": {
      "recommendedBet": 100
    },
    "whale_flag": true
  }
}
  • Delivery targets must be public HTTPS URLs. Redirects are treated as failures and are not followed.
  • Any 2xx response completes delivery. A failed initial attempt is retried after 30 seconds, then 5 minutes, then 30 minutes: 4 total delivery attempts.
  • Each registration is capped at 100 delivery attempts per rolling hour, including retries.
  • When the registration has a secret, verify X-Webhook-Signature against sha256=<HMAC_SHA256(raw_body, secret)> before parsing the JSON.

X-Webhook-Signature signs outbound user deliveries. It is distinct from the inbound x-alchemy-signature required by /api/whale/webhook.


Mock-wallet unrealized P&L freshness

GET /api/wallets exposes mock-wallet unrealizedPnl as a persisted point-in-time snapshot, not a continuously priced equity value. unrealizedPnlMarkedAt records the pricing-pass timestamp; null means no trustworthy mark timestamp exists. The companion unrealizedPnlIsSnapshot, unrealizedPnlCaveat, and unrealizedPnlAuthority fields keep this contract machine-readable.

For current equity decisions, use pt_mock_analytics_get.wallet_mark_to_market.preferred_unrealized_pnl and inspect live_pricing_status. A complete analytics pricing pass refreshes the persisted snapshot after the read returns. Partial or unavailable pricing never overwrites the last complete mark.

The reserved real-money-copy row is never accepted by mock analytics and never receives its write-behind. In normalized /api/wallets and GET|POST /api/trade/real-wallet responses, its mark timestamp is null and its provenance instead points to real_trades for position P&L and /api/trade/wallet-funds for spendable funds.

GET /api/wallets derives current-day realized P&L from get_wallet_daily_copy_stats. dailyPnl and the backward-compatible daily_pnl alias always carry the same UTC-day value. If the projection fails, both are null and dailyPnlDegraded:true / dailyPnlStatus:"degraded" make the partial response explicit; the route never substitutes the denormalized mock-wallet counter. Reserved real-wallet legacy performance aliases are projected from real_trades. A reserved real wallet whose risk configuration is missing or does not select the real lane makes the daily projection fail closed instead of reading mock_trades.

GET /api/trade/wallet-funds also returns equityIdentity for the authenticated real wallet. Its complete identity is:

spendablePusd + openMarkedValue + pendingRedemptionValue - netDeposits = lifetimePnl

pendingRedemptionValue comes from the bounded Polymarket Data API positions read and includes only positions marked redeemable. The separate modeledPendingRedemptionValue is the unresolved-payout-row projection for operations diagnostics only: it never enters lifetimePnl, and its absence alone does not degrade the identity. netDeposits is the signed sum of deposit and withdrawal ledger entries, while openMarkedValue excludes redeemable venue positions so pending winnings are not double-counted. If the positions read or another required component is unavailable, status: "degraded" and lifetimePnl: null fail closed; callers must not reconstruct or display a partial lifetime P&L as complete.


Deletion & audit retention

PolyTrackers favors soft deletion for user-owned resources so that reporting, billing, copy-trading analytics, and security audits remain reliable. The behavior per-resource is:

ResourceEndpoint(s)Behavior
Agent API KeysDELETE /api/keys?id=…Soft-revoke: is_active=false, revoked_at=now(). Authentication is rejected immediately; row is retained for audit.
Mock walletsDELETE /api/wallets, DELETE /api/mock/wallet/{id}Soft-delete: is_active=false, deleted_at=now(). Whale assignments are detached (active=false, removed_at=now()). mock_trades, whale_activity, and whale_decision_traces are preserved. Wallet names can be reused.
Outbound webhook registrationsDELETE /api/webhooks/{id}Soft-delete: deleted_at=now(), status=paused. No future deliveries fire; historical delivery_log remains queryable via GET /api/webhooks/{id}/deliveries.
Alert preferencesDELETE /api/alerts/preferencesHard reset of the preferences row.
Roster (whale) assignmentsDELETE /api/roster/{walletId}Detach: row retained, active=false, removed_at=now().
Trade ordersDELETE /api/trade/orders/{id}Cancels the live order at the CLOB; hard-removes the queued row.

Mock trades are not user-deletable by design once placed — open, closed, and resolved rows feed the leaderboard, copy-trading scoring, and tier-history analytics. To exit an open mock position, close it through POST /api/mock/resolve; this records the terminal state and preserves the trade row for history.


Whale service + inbound webhooks

Existing whale-service endpoints remain supported and documented in OpenAPI:

  • Wallet/roster management (/api/wallets, /api/roster*)
  • Service status/config/control (/api/whale/*)
  • Webhook ingest/events/replay (/api/whale/webhook*)

For replay automation, use dryRun: true first, then replay live once validated.


Discovery endpoints

Anonymous, CDN-cached, and safe to fetch before you have a key. Every one of them describes a surface documented in this file:

PathMedia typeWhat it is
/openapi.jsonapplication/jsonThe OpenAPI 3.1 contract for the REST API. /api/openapi.json is a 301 alias.
/.well-known/api-catalogapplication/linkset+jsonRFC 9727 API catalog: a linkset naming the REST, MCP, and documentation MCP entry points, each with its service-desc (machine-readable description), service-doc (human documentation), and service-meta (metadata) links. The one URL to fetch when you want the developer resources and nothing else.
/.well-known/mcpapplication/jsonMCP handshake manifest (alias /.well-known/mcp.json).
/llms.txttext/plainAgent instructions: when to use PolyTrackers, automation-safe lanes, error shapes, and the crawl policy.
/skill.mdtext/markdownThe official agent skill — tool catalog, scopes, and workflows.

MCP server

PolyTrackers exposes a stateless MCP endpoint for agent clients:

  • GET /api/mcp — unauthenticated health/capability probe.
  • GET /.well-known/mcp (alias GET /.well-known/mcp.json) — unauthenticated manifest describing this server: transport URL, advertised protocol versions, capabilities, auth scheme, and the stdio bridge package. Generated from the same constants the server itself uses, so it cannot drift.
  • POST /api/mcp — JSON-RPC 2.0 over Streamable HTTP. Send Authorization: Bearer <agent-api-key> — use the same ptk_... key as your REST/API examples.
  • GET /api/mcp/events — optional authenticated SSE notifications for anomalies, whale_signals, copy_signals, and scan_progress, with : keepalive every 15s. Send Authorization: Bearer ptk_.... The whale_signals and copy_signals topics are Elite-only; requests for them from Free or Pro keys are silently dropped while the 200 stream remains live for permitted topics.

SSE connections last at most 800 seconds (~13 minutes), so reconnect when the server closes the stream. There is no Last-Event-ID resumption: each reconnect starts at the current Redis position ($), so events published while disconnected are permanently missed.

When the client supports Streamable HTTP, point it at https://polytrackers.com/api/mcp with Authorization: Bearer <agent-api-key>.

For stdio-only MCP hosts, run the published bridge with npx -y @polytrackers/mcp-stdio; it relays stdio JSON-RPC to that endpoint. It reads POLYTRACKERS_API_KEY (required), POLYTRACKERS_MCP_URL (optional, defaults to the endpoint above), POLYTRACKERS_MCP_ALLOWED_HOSTS (optional host allowlist), and POLYTRACKERS_MCP_TIMEOUT_MS (optional). Full hosted and stdio setup examples are published in /skill.md.

To test strategies programmatically with no real money — the mock (paper) trading tool flow, free-tier reads, and going-live fees — see the Polymarket Mock Trading API guide.

Agent skill distribution

Install the PolyTrackers agent skill (which bundles this MCP setup) with one command: npx skills add polytrackers/polymarket-copy-trading-skill. Official distribution channels:

Scopes:

  • signals:read — read-only tool/resource catalog for Free+; free keys are shaped by 7-day base anomaly history (up to 28 days with activated referral rewards), top-5 delayed anomalies, and the 200 requests/day MCP quota.
  • trade:execute — permits pt_trade_execute for Pro+ after pt_trade_preflight approval.
  • agent:scan — permits pt_scan_trigger for Pro+ without granting broader writes.
  • agent:full — current Elite write/execution superset.

Versioning & changes

The current REST/OpenAPI contract version is 2.0.0, sourced from the canonical OpenAPI document and served at /openapi.json. The hosted MCP server version is 0.1.0, sourced from the server package version and returned by the MCP initialize response and each tool response's _meta.server_version. Additive endpoints and tools may ship without a version bump.

Version header

The REST contract is versioned in a response header, not in the URL path. Every /api/* response carries:

HeaderMeaning
API-VersionContract version that produced this response, e.g. 2.0.0. Matches info.version in the OpenAPI document.

Assert on it at startup and fail loudly on an unexpected major, rather than discovering a change through a parse error later. The header is response-only: no request header changes how a route behaves, so there is nothing here that can be pinned to and later withdrawn. Paths outside the proxy's matcher (/api/cron/*, /api/whale/webhook, /api/goldsky/shadow) are machine-to-machine and omit it.

The OpenAPI document declares this header on every response it describes, as $ref: "#/components/headers/ApiVersion" — so a generated client sees it without reading this page, and a spec parser can detect the versioning strategy. The four POST /api/whale/webhook responses deliberately carry no such declaration, because that path is outside the matcher. A contract test holds both halves in place against the matcher in proxy.ts.

How the number moves:

  • Patch/minor — additive only. New endpoints, new optional request fields, and new response fields ship without a major bump, so clients must ignore response fields they do not recognise.
  • Major — anything that could break a correct client: a removed or renamed field, a narrowed type, a changed status code, or a retired endpoint. A major is always preceded by the deprecation signal below.

Deprecation and sunset headers

A retired endpoint announces itself on the wire, not only in this document:

HeaderMeaning
DeprecationRFC 9745 structured-field date — @<epoch seconds> — for when the deprecation was announced.
SunsetRFC 8594 HTTP-date for when the endpoint stops serving. A date in the past means it is already gone.
Linkrel="successor-version" pointing at the replacement, plus rel="deprecation" pointing at this page.

POST /api/whale/trade is the current example: retired on 2026-02-27, it answers 410 Gone and carries all three headers pointing at POST /api/whale/webhook. Poll Deprecation on the endpoints you depend on — it is the earliest machine-readable warning available, and it is set long before the Sunset date arrives.

Compatibility and deprecation policy:

  • Breaking REST contract changes are recorded in this section when they ship.
  • MCP tool additions, removals, and renames are recorded in the polytrackers://changelog resource. Its current catalog count and fingerprint are checked against the registered catalog in CI.
  • Breaking MCP changes bump the server minor version. A renamed tool remains available for one minor version with deprecated: true and a replacement in its description before removal.
  • The MCP HTTP transport does not emit a Deprecation response header for tool changes; the REST headers above cover REST endpoints only. For MCP, consult this section and polytrackers://changelog.

Change log:

  • 2026-08-23: The OpenAPI document now declares API-Version on every response it describes, instead of only defining the header component and describing it in prose. Documentation only — the header itself has shipped on the wire since earlier the same day, and no endpoint's status, body, or media type changed.

  • 2026-08-23: Every /api/* response now carries API-Version, and the retired POST /api/whale/trade additionally carries Deprecation, Sunset, and a successor Link. Additive headers only — no status code, body, or cache directive changed, and page routes are untouched. Every OpenAPI operation also now has a description; a contract test fails the build if one ships without.

  • 2026-08-14: Wallet copy pause is now a three-state REST/MCP contract: copying, buys_paused, or fully_paused. Buys-only mode blocks new BUY mirroring with WALLET_COPY_BUYS_PAUSED while paper and real SELL exits keep flowing. The v2 MCP setter prefers mode and retains legacy copyPaused:true|false; every live transition remains atomically audited, with explicit confirmation and a best-effort security email for real wallets.

  • 2026-08-14: Added the operator-allowlisted, Elite, read-only, MCP-only pt_copy_replication_aggregates_get internal evidence tool over the service-role Copy Receipt V1 aggregate RPC. It requires one exact whale, fixes the RPC limit at one, and adds index-backed lookup plus a caller-side five-second PostgREST request deadline. Database-owned 20/40-condition confidence floors and below-floor null metrics are unchanged; ordinary customer keys fail closed and no REST or browser surface was added.

  • 2026-08-18: Paper and dedicated real-copy wallets now expose fixedStakeUsd. Zero preserves conviction/percentage sizing; a positive value of at least $1 requests the same USD amount for every copied BUY and bypasses the copy multiplier. Kelly, position, max-bet, balance, authorization, breaker, reservation, and venue gates remain authoritative. The dashboard plus both guarded MCP config writers can read, preview, and update the wallet-scoped field; existing wallets default to off.

  • 2026-08-11: Added the free read-only MCP tool pt_whale_forward_returns_get for bounded forward-return counterfactuals across arbitrary wallet cohorts. It preserves one row per request entry, enforces exact decision/cutoff windows, and exposes unresolved, truncation, fee, resolution-source, and vendor-call-budget evidence.

  • 2026-08-10: Dedicated real-copy wallets now persist a guarded 0–10% realCopySlippageTolerance, exposed through the real-wallet MCP config tools. Where the venue ceiling permits, BUY FAK caps retain at least one market tick of headroom above the observed ask, and deterministic signed-cap rejections are no longer replayed through Goldsky promotion recovery.

  • 2026-08-10: Recommendation and digest-suggestion rows now expose display-name source/time plus Polymarket's stable pseudonym. Legacy aliases beginning with a wallet address no longer override the current leaderboard name, and compact/byte-capped/cursor-replayed shapes retain the provenance.

  • 2026-08-07: Rollout-eligible dedicated real-copy wallets can now set a 1–2x owner-only sizing multiplier through the authenticated dashboard. The server gate fails closed to 1x, all existing caps remain authoritative, and MCP real-wallet writes continue to exclude the field.

  • 2026-08-10: Real-wallet dashboard and Elite MCP config surfaces now read, preview, and write authorization-scoped minPerTrade. Null/0 disables the floor; positive values are bounded to $1 through the signed copy-trading authorization's maxPerTrade, restore only percentage-reduced dust, and never exceed the strategy-sized cost. The floor applies only when fixedStakeUsd is off and is explicitly distinct from risk-rule maxPerTrade and minTradeSize.

  • 2026-08-07: Session-authenticated PUT /api/whale/config now routes real-wallet risk changes through the atomic config-plus-audit RPC and sends the owner security email. Real-wallet requests reject executionMode; paper-wallet behavior is unchanged.

  • 2026-08-07: Whale-edge loads are now batch-independent under deterministic per-wallet caps. coverageTruncated is wallet-specific, SPLIT/non-directional evidence is exposed through nonDirectionalSampleCount*, and roi_provenance includes the capital used by each ROI window.

  • 2026-08-07: Leaderboard, recommendation, and digest-suggestion rows now share canonical ROI/sample projections and a roi_provenance block across full, compact, and replayed snapshot shapes. Bare sampleCount means the directional edge-row count; recommendation admission evidence is separately named. Explicit edge-load fallback uses labeled 365-day receipts-only counts with null ROI.

  • 2026-08-04: pt_recommendations_get now returns snapshot-pinned opaque continuation cursors plus explicit total/returned/has-more counts. Continue with the same projection and settled-sample filter when the byte cap trims a page; min_settled_samples narrows the population and is not continuation.

  • 2026-08-03: Added Elite agent:full pt_real_wallet_copy_config_get / pt_real_wallet_copy_config_update and pt_real_wallet_breaker_resume / pt_real_wallet_breaker_kill. Every real-money preview or write requires confirm_real_money:true; live changes append an audit event and send a best-effort security email. The strict config writer excludes executionMode and copySizingMultiplier.

  • 2026-07-31: Added Elite agent:full pt_mock_wallet_copy_config_get / pt_mock_wallet_copy_config_update for full caller-owned paper-wallet risk reads and guarded dry-run/live updates. The strict write schema excludes executionMode, real-wallet rows are unreachable, and zero-value warnings are returned with the preview.

  • 2026-07-26: Added GET /api/trade/money-summary (user-scoped real-money totals/counts aggregate; session or agent:full, same auth semantics as GET /api/trade/ledger).

  • 2026-07-21: Added the paper-only wallet copy-sizing multiplier to the wallet risk-config contract and pt_mock_wallet_copy_sizing_update; the MCP catalog baseline was then 65 tools and real-copy sizing was 1x. The 2026-08-07 owner-dashboard 1–2x rollout supersedes that historical behavior.

  • 2026-07-20: Published the version/deprecation policy and recorded the 2.0.0 OpenAPI contract plus the then-current 64-tool MCP catalog baseline.


Reliability notes

  • Handle 429 on all rate-limited endpoints and respect Retry-After.
  • Treat trade execution as idempotent when you provide idempotencyKey (idempotent replays return 200 with idempotencyReused: true).
  • POST /api/keys/generate returns the raw key once — store it securely. Each account keeps one active Agent API Key; rotate by revoking the old key, then generating a replacement and updating clients/agents.
  • /api/anomalies/{id} may return 404 when a record falls outside your tier window.
  • TIER_UPGRADE_REQUIRED responses include requiredTier, currentTier, and upgradeUrl — surface these to the user/agent verbatim.