Promethean Markets
Log inGet started
Quickstart

First file in under a minute.

Time-to-first-file is the metric. Get a key, list released days, stream one, verify its hash.

1 · Get a key

Keys are created in the portal and prefixed pmk_. The token is shown once at creation and stored only as a hash. Set it in your shell:

$ export PM_API=https://api.promethean.markets
$ export PM_KEY=pmk_your_token

2 · The three curls

availability → download → verify
$ curl -sH "Authorization: Bearer $PM_KEY" \
  "$PM_API/v1/availability?source=binance_spot" | jq .days
$ curl -sH "Authorization: Bearer $PM_KEY" -o trades.parquet \
  "$PM_API/v1/download?source=binance_spot&channel=crypto_trades&scope=BTC&day=<released-day>"
$ curl -sH "Authorization: Bearer $PM_KEY" -o trades.manifest.json \
  "$PM_API/v1/manifest?source=binance_spot&channel=crypto_trades&scope=BTC&day=<released-day>"
$ shasum -a 256 trades.parquet # == .sha256 in the manifest

3 · Read the parquet

$ duckdb -c "select count(*), min(ts_ms), max(ts_ms) from 'trades.parquet'"
# or in python:
import duckdb; duckdb.sql("select * from 'trades.parquet' limit 5")

That is the whole loop. Every file you pull is a released day; unreleased days simply are not there to fetch.

API reference

Four endpoints, released days only.

Base URL https://api.promethean.markets. Bearer auth; keys are prefixed pmk_, shown once at creation, stored hashed at rest. Every key has a daily download budget (default 20,000): only /v1/download consumes it — the catalog, availability, and manifests are free, so verification never costs budget. Exceeding it returns 429 and never charges; unauthorized requests never consume budget.

GET /v1/datasets

Machine-readable catalog: sources, channels, scopes, schema info.

$ curl -sH "Authorization: Bearer $PM_KEY" "$PM_API/v1/datasets" | jq .

GET /v1/availability

ParamRequiredDescription
sourcenoOptional filter — source id, e.g. binance_spot; omit for every source.
$ curl -sH "Authorization: Bearer $PM_KEY" "$PM_API/v1/availability?source=binance_spot"
{ "inception_day": "<inception-day>", "latest_day": "<latest-day>", "gapless": true, "days": { "<day>": {…}, … } }

GET /v1/download & /v1/manifest

ParamRequiredDescription
sourceyesSource id.
channelyesChannel, e.g. crypto_trades.
scopeyesCoin or tier, e.g. BTC.
dayyesUTC day YYYY-MM-DD. Must be released.

/v1/download streams the day's Parquet; /v1/manifest returns the same file's manifest JSON.

Errors

StatusCodeMeaning
401missing_or_invalid_api_keyMissing or unknown bearer token. Never consumes budget.
403source_not_in_key_scopeThe key's scopes do not include this source.
404day_not_committedThe day is not in the released chain (withdrawn or never released).
404object_not_releasedThe source/channel/scope/day object does not exist in the released lane.
416invalid_rangeThe Range header cannot be satisfied for this object.
429daily_budget_exceededKey over its daily download budget. Resets at 00:00 UTC; never charges.

Machine-readable spec: openapi.json — import it into any client generator or API tool.

Read it in your stack

Files are standard Parquet — anything that reads Arrow reads the archive. No SDK required.

import io, requests, pandas as pd

r = requests.get(f"{API}/v1/download",
    params=dict(source="binance_spot", channel="crypto_trades", scope="BTC", day="<released-day>"),
    headers={"Authorization": f"Bearer {KEY}"})
df = pd.read_parquet(io.BytesIO(r.content))
-- download once with curl, then query in place
SELECT count(*), min(ts_ms), max(ts_ms)
FROM read_parquet('trades.parquet');
library(arrow); library(httr)

resp <- GET(paste0(API, "/v1/download?source=binance_spot",
  "&channel=crypto_trades&scope=BTC&day=<released-day>"),
  add_headers(Authorization = paste("Bearer", KEY)))
df <- read_parquet(rawConnection(content(resp, "raw")))
Schemas

Columns, per channel.

Two representative channels below. Columns are marked identity (what the row is) or quality (freshness / lineage metadata).

binance_spot · crypto_trades

ColumnTypeNullDescription
ts_msint64noExchange event time, ms UTC. identity
receive_msint64noReceive time; always ≥ ts_ms. quality
pricedoublenoTrade price.
amountdoublenoTrade quantity.
trade_idstringnoStable row id; unique per scope/day.

polymarket_updown · updown_book_snapshot_100ms

ColumnTypeNullDescription
bucket_msint64no100ms grid timestamp. identity
source_ts_msint64noTimestamp of the source snapshot used. quality
source_lag_msint64nobucket_ms − source_ts_ms; the true freshness. quality
freshness_statestringnoPer-row freshness classification. quality
bid_priceslist<double>noTop-20 bid prices; sizes are in bid_sizes.
Files & manifests

One file, one manifest.

Layout

Day-partitioned Parquet, one file per source / channel / scope / day, each with a sibling manifest:

{source}/{channel}/{scope}/{day}.parquet
{source}/{channel}/{scope}/{day}.manifest.json

Manifest fields

FieldDescription
sha256Hash of the Parquet file. Re-hash yours; it must match.
row_countExact row count. Recompute to confirm.
schema_fingerprintSHA256 schema fingerprint; released sample 4f8b025ecbc7c9cc7b246eda92ff863437722d4c06af919760ff2e0d8cd2d7fc.
min_event_ts_msMinimum event timestamp in the file.
max_event_ts_msMaximum event timestamp in the file.

Re-verify

$ shasum -a 256 trades.parquet
$ jq -r .sha256 trades.manifest.json # must be identical
$ duckdb -c "select count(*) from 'trades.parquet'" # == row_count
Data semantics

Honest caveats, per family.

Every dataset carries millisecond-UTC timestamps: receive_ms is when our collector received the row (our clock, never rewritten); source_ts_ms / ts_ms is the venue's own event time; bucket_ms on aligned grids uses latest-prior semantics — a row can never contain information from its future. Below is every documented caveat, mirrored from the data contract; each is enforced or disclosed by the release audits, not left to fine print.

Day membership

Rows belong to the UTC day of their event timestamp — except polymarket_chainlink_rtds/price_ticks, whose day membership is receive_ms (the venue emits whole-second source stamps); a day's first ticks may carry a source stamp up to a few seconds before midnight, bounded at 60s by the audit.

Cross-midnight carry

The first buckets of a day on aligned datasets legitimately carry the last pre-midnight state (carried_forward with a pre-day source stamp). That is continuity, not leakage.

Predict.Fun venue clock skew

Predict.Fun stamps venue timestamps from its own clock, observed up to ~7.4s ahead of true receive time. Rows store both timestamps honestly; the audit bounds the skew at 30s. Expect small negative receive-minus-source values on this venue.

Predict.Fun missing venue stamps

Some venue book responses carry no update time (updateTimestampMs: 0). Such rows store source_ts_ms = receive_ms — the documented receive fallback — and the audit rejects any non-positive venue epoch. Days collected before that fix (2026-07-16..18) were withdrawn in the launch reset.

Restart seams

The legacy coordinated collector restart at the UTC boundary creates a head seam. Under the binding zero-seam policy that seam is not tolerated in a customer day: the exact receive-cadence and grid ledgers stay red until collection is continuous. Seams are never excused into a released day.

This is the Polymarket-relayed Chainlink reference feed — not paid or signed Chainlink Data Streams, and not the on-chain Data Feeds contract.

Binance trade-stream completeness

Venue trade ids are dense integers, so id arithmetic is a completeness oracle: our 2026-08-01 row-level audit measured small spot losses outside the restart seam (≈0.1–0.3% of a day's ids — sub-second frame drops in trade bursts, plus rare multi-minute upstream feed stalls that also hole trades and candles). The release ledger now computes per-coin id-continuity (missing-id runs with seam attribution, outage runs, missing share) and discloses it in every day's ledger while enforcement thresholds are calibrated. On futures, the venue stream excludes non-market trades (insurance fund / ADL), so id gaps there are structural — only an id gap bracketed by a real time gap marks an outage.

Binance intra-millisecond trade order

Within a single ts_ms, persisted row order is stable-sort order by our content-hash trade id, not venue sequence. Venue sequence is fully recoverable by sorting on venue_trade_id as an integer — tape-replay consumers should do so.

Binance books are sampled with a bounded hold

crypto_book_snapshot_20 re-emits the latest venue book every 100ms and stops once the held book is older than 30s (the stream watchdog window). During an upstream stall, rows keep arriving at receive cadence with a frozen venue ts_msreceive_ms − ts_ms is the honest hold age. Rare venue frames without a timestamp get their ingest time pinned once (collector fix at the 2026-08-03 boundary); earlier days hold a small number of such rows whose ts_ms equals each emit's receive_ms.

Polymarket Up/Down trades before the price window

Markets are tradable from venue listing, so updown_trades legitimately contains prints before a market's price window opens (14.9% of trades on the audited day; zero after window close). Grids are window-bounded by design — joins from trades to grids must expect pre-window prints.

Binance upstream gap allowance (60s/day, disclosed)

Binance streams its market data from infrastructure roughly 9,000km from our collectors and does not replay missed frames. We run two independent merged upstream connections per stream, which mask single-connection stalls — but venue-side shutdown waves can close every connection at once, which redundancy on one venue cannot cover. A Binance day stays green while the summed receive-gap time per coin — time with no book sample at all, beyond the 1.1s freshness line — stays within 1% of the day (864 seconds), itemized in the day's ledger (gap_allowance_used_ms, daily_gap_allowance_ms). Days exceeding the allowance are red, never silently truncated. The 1% figure is calibrated, not convenient: a six-day survey of time when all five coins were unfresh at the same instant — something five independent markets do not do by chance — measured 226–658 seconds per day normally (~99.5% freshness), and 13,681 seconds on the day of our own fleet outage. Normal venue behaviour passes with ~3× headroom; that outage fails by 15×.

A known limit of that measure: during a stall we re-emit the last venue book on the 100ms cadence with a frozen ts_ms, so those rows arrive on schedule while carrying held content and the gap measure does not see them. Every row carries receive_ms − ts_ms as its honest hold age. We do not score held time today, because a quiet market produces the same signal — separating held-because-stalled from held-because-quiet needs cross-connection correlation, and that work is open.

No upper bound is claimed. Binance publishes no availability SLA or maximum recovery time for public market streams. Our largest observed book gap is 104.5s and largest held-book age 64.1s — empirical maxima from our own record, not guarantees. When every ingest path loses the venue at once, order-book states in that interval cannot be reconstructed from Binance's public depth API; trade gaps remain repairable from id-based history.

Limits

Auth, budgets, fair use.

Auth model

Bearer tokens prefixed pmk_. Shown once at creation, stored only as a hash. Rotate by creating a new key and revoking the old one; revoked keys stay listed for audit.

Budgets

Each key carries a daily request budget (default 20,000). Authorized requests count; 401s do not. Over budget returns 429 daily_budget_exceeded; the counter resets at 00:00 UTC.

Requesting a raise

Email your key label and expected daily volume. Desk plans carry higher, custom budgets and per-key overrides.

Changelog

Reverse-chronological.

2026-08-07CONTRACTThe Binance daily gap allowance is recalibrated from a flat 60 seconds to 1% of the day (864s). A six-day survey of genuinely-unfresh time — 100ms buckets where all five coins were unfresh at once, which independent markets do not do by chance — measured 226–658s per day normally (~99.5% freshness) and 13,681s on the day of our own fleet outage. The old 60s figure was unmeetable by a healthy venue; 1% passes normal days with ~3x headroom and still fails that outage by 15x.
2026-08-07DOCSDocumented a known limit of the Binance gap measure: during a stall we re-emit the last venue book on the 100ms cadence, so those rows arrive on schedule while carrying held content and the gap measure does not count them. Every row carries receive_ms − ts_ms as its honest hold age. We do not score held time, because a quiet market produces the same signal. Also stated plainly that no upper bound on venue recovery is claimed — our largest observed gap is an empirical maximum, not a guarantee.
2026-08-05CONTRACTBinance gains a disclosed 60-second daily upstream gap allowance per coin: gaps beyond the 1.1s freshness line are summed against the allowance and itemized in every day ledger instead of redding on the first over-line gap. Motivated by route-level freezes toward Binance Tokyo that survive our redundant dual-connection collectors. Days exceeding the allowance stay red.
2026-08-01DOCSTen-market row-level audit (every finding independently re-verified) documented four new released-lane caveats: Binance trade-stream completeness with a per-coin id-continuity disclosure in every day ledger, intra-millisecond trade ordering, the bounded 30s book hold, and pre-window Up/Down trades. The Data semantics section now mirrors the contract, enforced by a build test.
2026-07-31DOCSPolymarket US (candidate) is formally a documented capped subset — the top 10,000 markets of the venue active-markets walk, with cap state disclosed in every runtime verdict.
2026-07-19CORRECTIONTen-source launch reset: 2026-07-16..18 withdrawn whole-day and the released lane emptied for the ten-source re-inception. Closes continue nightly at full rigor; the customer chain restarts at the ten-source inception day. see the ledger →
2026-07-18RELEASEReleased 2026-07-17 — the chain’s first fully autonomous promotion (the reactor promoted it during the 2026-07-18 close with no human in the loop). 2026-07-18 followed the same night. Both later withdrawn in the 2026-07-19 launch reset.
2026-07-18APIPublic index mirror is live: the website verifies its numbers against the released-lane index at every build, and rebuilds nightly after certification.
2026-07-17CORRECTIONRe-incepted at 2026-07-16 after the 2026-07-15 boundary outage; internal-only 2026-07-13/14 withdrawn to the corrections ledger. see the ledger →
2026-07-16RELEASEInception: the first certified day across all six release sources. Withdrawn 2026-07-19 in the ten-source launch reset.
2026-07-14APIPer-key daily request budgets are live — over-budget requests return 429 daily_budget_exceeded.
2026-07-13DOCSDocumented the venue_unlisted disposition: a venue-side missing window is disclosed as a venue fact, backed by recorded evidence.