Skip to content
Get an API key

Use these canonical production origins directly:

Surface Origin
REST reads https://data.predictefy.com
REST execution https://exec.predictefy.com
WebSocket streaming wss://stream-production-75db.up.railway.app

Existing integrations can keep using the legacy Railway alternates: https://reads-production.up.railway.app for reads and https://execution-production-745f.up.railway.app for execution. Both remain supported.

REST examples below use the reads origin. Execution operations in the REST API reference declare the isolated execution origin themselves. WebSocket clients append /v1/stream; see the WebSocket API reference.

Terminal window
export PREDICTEFY_API_URL="https://data.predictefy.com"

Every request is authenticated and metered against your credit balance. Anonymous requests receive 401 UNAUTHORIZED (only the health endpoints and docs are open).

Keys are created in the Predictefy developer dashboard. Sign-up is free during the private beta: every new account starts on the Free plan with 25,000 credits, refilled monthly. Trading with your own funds ships on every plan, Free included; arbitrage, price-gap (discrepancy) queries, and bulk endpoints start at Builder. Every plan meters all usage in credits, so upgrade when you need more volume or the Builder-and-up features. See Pricing, credits & billing.

  1. Sign up (or sign in) with your email address.
  2. Open API keys and choose Create API key. Signing in never creates a key automatically.
  3. Copy the raw pk_live_… value when it appears. It is shown once and only its hash and display prefix are stored; a lost key must be revoked and re-created.

Treat the key like a password: send it only in the Authorization header, never in URLs (query strings leak into request logs).

List the three highest-volume active Polymarket markets:

Terminal window
curl -s "$PREDICTEFY_API_URL/api/polymarket/fetchMarkets?limit=3&sort=volume" \
-H "Authorization: Bearer pk_live_YOUR_KEY"

Successful responses are always enveloped as { success, data, … }:

{
"success": true,
"data": [
{
"marketId": "",
"title": "",
"outcomes": [{ "outcomeId": "", "label": "Yes", "price": 0.62 }],
"volume24h": 123456.78,
"liquidity": 98765.43,
"url": "https://…",
"asOf": "2026-07-03T09:15:00.000Z",
"provenance": { "source": "venue-rest" },
"capabilities": { "read": true, "trade": false, "depth": true, "history": false }
}
],
"meta": { "asOf": "2026-07-03T09:15:00.000Z", "provenance": { "source": "venue-rest" } },
"page": { "limit": 3, "offset": 0, "total": 1519, "hasMore": true, "nextCursor": "" }
}

Three honest-data fields ride on every record:

  • asOf — when the data was snapshotted (never pretend-fresh).
  • provenance.source — where it came from (venue-rest, predictefy-live, fixture).
  • capabilities — what this venue actually supports (read / trade / depth / history). trade is false on these reads records; public execution availability is documented separately for each supported venue. The history flag is conservative — it flips on per venue as coverage is proven, while the fetchOHLCV endpoint is already live.

Swap the exchange segment for any of the 17 venues, or use router to search across all of them at once:

Terminal window
curl -s "$PREDICTEFY_API_URL/api/router/fetchMarkets?query=election&status=active" \
-H "Authorization: Bearer pk_live_YOUR_KEY"

List verbs accept limit (max 100), offset, page, and cursor pagination. Prefer cursors: the response’s nextCursor freezes the catalog snapshot from page one, so a long walk never skips or double-counts rows that move while you page.

Terminal window
# Page 1
curl -s ".../api/kalshi/fetchMarkets?limit=100" -H "Authorization: Bearer pk_live_…"
# Page 2 — pass the previous response's nextCursor
curl -s ".../api/kalshi/fetchMarkets?limit=100&cursor=CURSOR_FROM_PAGE_1" \
-H "Authorization: Bearer pk_live_…"

Cursors expire after 60 seconds by default; tune that with snapshotTTL (milliseconds, 0 = the cursor chain never expires). The final page omits nextCursor.

page.total can be null. The data page is the product and the total is only metadata, so a count that overruns its own short budget is abandoned and your page is still served — rather than failing the whole request over a number. null means “not counted”, never “zero matches”, and meta.totalUnavailable says so explicitly. Drive your loop with hasMore / nextCursor, not with total: hasMore is decided by fetching one row past your limit, so it stays correct whether or not the total was computed.

Every error — 4xx or 5xx — uses one shape:

{
"success": false,
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "",
"retryable": false
}
}

code, message, and retryable are always present — treat anything else as optional.

The most common codes:

HTTP code Meaning Retry?
400 VALIDATION_ERROR Bad or missing parameters. no
400 NOT_SUPPORTED Accepted-but-unsupported capability (honest gap). no
401 UNAUTHORIZED Missing, unknown, or revoked API key. no
402 INSUFFICIENT_CREDITS Balance below the endpoint weight — see credits & plans. after balance update
403 PLAN_REQUIRED The account does not include this route or history window. after access update
409 API_KEY_LIMIT The account’s active API-key cap has been reached. after revoking a key
404 EXCHANGE_NOT_AVAILABLE, MARKET_NOT_FOUND, EVENT_NOT_FOUND, OUTCOME_NOT_FOUND Unknown venue or record. no
429 RATE_LIMITED Per-key or per-plan request-rate window exceeded. yes (back off)
501 NOT_SUPPORTED Venue has no public feed for this verb (e.g. trades tape). no
503 CATALOG_UNAVAILABLE, PLATFORM_UNAVAILABLE, HISTORY_UNAVAILABLE, BILLING_UNAVAILABLE Temporary outage — fail-closed, never silently wrong. yes (backoff)

The full code enum per endpoint is in the API reference.