Migrating from v1
v2 is the current Polywhaler API. v1 is frozen and will be retired. This page maps every v1 route to its successor and lists what a client has to change.
Timeline#
v1 is deprecated
410 gone.- From v2's general availability, every v1 response carries
Deprecation,Sunsetand aLinkheader pointing here and to the route's v2 successor. - Before the sunset, v1 is switched off for a few short, announced brownouts, so a forgotten integration fails while there is still time to fix it. Accounts still calling v1 are emailed beforehand.
- Until then v1 gets security fixes only. Its response shapes do not change, with two exceptions: bad input answers
400instead of500, and/v1/btc-forecastserves the same model runs as v2, so new runs carrybtc-forecast/1.1.0and the insight typeleanin place ofskew(see the changelog). - Your keys, quota and webhook endpoints carry over: the same
pwhk_key works on both versions, and both count against one daily quota. The Usage page splits your traffic by version.
What changed#
One envelope#
Every success is {data, meta?, pagination?} and nothing else at the top level; route context that v1 mixed into the body (venue, methodology version, totals) is in meta.
One error shape#
Every error is {error: {type, code, message, param?, docUrl, requestId}}, with a fixed code catalog (API errors). Branch on code, not on the message. Every response carries X-Request-Id.
camelCase, strictly checked#
Parameters and fields are camelCase: min_amount is minAmount, event_slug is eventSlug. An unknown parameter is a 400 unknown_param (with a hint when it is the snake_case spelling) instead of being ignored, so a typo can no longer return unfiltered data.
Units#
- Rates and probabilities are fractions from 0 to 1: a 62% win rate is
0.62. Scores (insider, Smart Money) stay 0 to 100. - Money is US dollars in fields ending
Usd:amountisamountUsd. - Wallets are always a lowercase
wallet(v1'sproxyWallet).
Timestamps#
Times are ISO-8601 UTC strings in fields ending At (timestamp: 1791555807 becomes tradedAt: "2026-10-09T14:03:27.000Z"), and from/to take ISO-8601 instants. Days are YYYY-MM-DD.
Cursor pagination#
v1's cursor was a Unix timestamp, which skipped trades that shared a second. v2's is opaque and signed: pass pagination.nextCursor back unchanged while pagination.hasMore is true. A cursor only works with the filters it was made for. There is no count.
Key scopes#
Keys now carry scopes (read, watchlist:write, webhooks:write, exports:write). Existing keys hold all four, so nothing breaks; new keys can be read-only or restricted, and can expire.
Webhooks#
v2 endpoints are signed to the Standard Webhooks spec (webhook-id, webhook-timestamp, webhook-signature) with a whsec_ secret, so replays can be rejected and secrets rotated without downtime. Events are renamed after the resource in data (trade.high_insider is trade.insider_flagged), and data is the same shape the REST API returns. Upgrade to v2 in the portal converts an endpoint for you; see v1 endpoints.
Side by side#
Route map#
Every v1 route and the v2 route that replaces it. The v2 side is what the Link: rel="successor-version" header on a deprecated v1 response points to. Details of each are in the API reference.
- GET
/api/v1/black-swan/api/v2/black-swanGET read - GET
/api/v1/btc-forecast/api/v2/forecasts/btcGET read - GET
/api/v1/btc-forecast/history/api/v2/forecasts/btc/historyGET read - GET
/api/v1/btc-forecast/markets/api/v2/forecasts/btc/marketsGET read - GET
/api/v1/clusters/api/v2/clustersGET read - GET
/api/v1/consensus/api/v2/watchlist/consensusGET read - GETPOST
/api/v1/exports/api/v2/exportsGET read, POST exports:write - GET
/api/v1/exports/{id}/api/v2/exports/{id}GET read - GET
/api/v1/leaderboard/api/v2/smart-money/leaderboardGET read - GET
/api/v1/markets/api/v2/marketsGET read - GET
/api/v1/openapi.json/api/v2/openapi.jsonGET no key - GET
/api/v1/oracle/api/v2/oracleGET read - GET
/api/v1/outcomes/api/v2/outcomesGET read - GET
/api/v1/positions/aggregated/api/v2/positions/aggregatedGET read - GET
/api/v1/smart-money-flow/api/v2/smart-money/flowGET read - GET
/api/v1/suspicious-events/api/v2/insider/eventsGET read - GET
/api/v1/trades/api/v2/tradesGET read - GET
/api/v1/trades/{hash}/api/v2/trades/{hash}GET read - GET
/api/v1/usage/api/v2/usageGET read - GET
/api/v1/wallets/{addr}/api/v2/wallets/{addr}GET read - GET
/api/v1/wallets/{addr}/cluster/api/v2/wallets/{addr}/clusterGET read - GET
/api/v1/wallets/{addr}/positions/api/v2/wallets/{addr}/positionsGET read - GET
/api/v1/wallets/{addr}/trades/api/v2/wallets/{addr}/tradesGET read - GETPOSTDELETE
/api/v1/watchlist/api/v2/watchlistGET read, POST watchlist:write, DELETE watchlist:write - GETPOST
/api/v1/webhooks/api/v2/webhook-endpointsGET read, POST webhooks:write - GETPATCHDELETE
/api/v1/webhooks/{id}/api/v2/webhook-endpoints/{id}GET read, PATCH webhooks:write, DELETE webhooks:write - POST
/api/v1/webhooks/{id}/test/api/v2/webhook-endpoints/{id}/testPOST webhooks:write
Checklist#
- Point your base URL at
/api/v2and rename parameters to camelCase. Unknown parameters now fail, which finds the ones you missed. - Read
datafrom the envelope, convert rates from percentages to fractions, and parse*Atfields as ISO-8601. - Page with the opaque cursor until hasMore is false.
- Handle errors by
error.code, and logX-Request-Id. - Upgrade each webhook endpoint to v2 and switch your receiver to the Standard Webhooks check.
- Watch the v1 requests in your logs drop to zero.