Crypto Markets
Per-coin market data from CoinGecko: price, market cap and volume across thousands of cryptocurrencies.
crypto_markets — the dataset name to pass to the Obscura API.
What one row means
One CoinGecko top-market-cap coin's price/market-cap/volume/supply snapshot as returned by the `coins/markets` endpoint at the moment of one scrape, upserted per coin per UTC calendar day.
One row per (coin_id, captured_date).
Point-in-time availability
Every Obscura dataset carries available_date: the calendar day the publisher made the row
available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the
same thing on every dataset in the catalog.
For crypto_markets: snapshot: available_date = captured_date, a STORED GENERATED column (migration 000205). WHY THAT IS THE DAY IT BECAME AVAILABLE: /coins/markets is a CURRENT-STATE endpoint — it answers only for `now`, offers no historical parameter, and keeps no archive — so this row exists solely because the collector asked on that day, and the day it asked IS the day the fact was obtainable. There is no publication event to lag behind and no source date to prefer. EVIDENCE that captured_date is the capture day and not a payload date: the collector sets it to Utc::now().date_naive() on a live run and to BronzeObject::captured_day() on a replay, so replaying a year-old capture reproduces that capture's day rather than today's. It is generated rather than written because a column a collector writes can be written from a clock, which is what a replay must never do.
Refresh cadence
Obscura refreshes crypto_markets daily — the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.
Schema — 22 columns
The full public column list for crypto_markets, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/crypto_markets.
| Column | Type | Description |
|---|---|---|
| coin_id | text · not null | CoinGecko's slug id for the coin (e.g. "bitcoin", "ethereum", "tether"), taken verbatim from the API's `id` field. Half of the composite primary key; stable across runs and the natural join key back to CoinGecko for any given asset. |
| captured_date | date · not null | The UTC calendar day the collector CONTACTED CoinGecko: Utc::now().date_naive() once per task invocation on a live run, and the bronze object's own capture day on a replay. Other half of the composite primary key. Because /coins/markets answers only for `now` and keeps no archive, the day we asked is the day this state was obtainable — which is why available_date is generated directly from this column. |
| symbol | text | Ticker symbol for the coin, upper-cased on ingest (e.g. "BTC", "ETH", "USDT"). Sourced from the API's `symbol` field; nullable because the upstream JSON field is optional and a small share of listings omit it. |
| name | text | Human-readable display name of the coin/project (e.g. "Bitcoin", "Ethereum", "Tether"), copied from the API's `name` field; nullable when upstream omits it. |
| price_usd | double precision | Spot price in the requested vs_currency (default USD; CoinGecko field `current_price`) at the moment of the API call — an instantaneous quote, not an OHLC close. Nullable if CoinGecko has no live quote for the coin. |
| market_cap | double precision | Circulating-supply market capitalization in the vs_currency at capture time (API field `market_cap`); price_usd * circulating_supply as computed by CoinGecko, not derived locally. |
| market_cap_rank | integer | CoinGecko's market-cap rank among all listed coins at capture time (API field `market_cap_rank`, 1 = largest). Also drives which coins are included: the task requests order=market_cap_desc so market_cap_rank is monotonic with the row's position in the fetched page. |
| total_volume | double precision | Trailing 24-hour trading volume in the vs_currency across CoinGecko's aggregated exchanges (API field `total_volume`), as of capture time — a rolling window, not a calendar-day total. |
| high_24h | double precision | Highest price observed over the trailing 24 hours ending at capture time (API field `high_24h`), in the vs_currency. |
| low_24h | double precision | Lowest price observed over the trailing 24 hours ending at capture time (API field `low_24h`), in the vs_currency. |
| price_change_pct_24h | double precision | Percent change in price_usd over the trailing 24 hours ending at capture time (API field `price_change_percentage_24h`, requested explicitly via price_change_percentage=24h). Can be negative; e.g. -0.00883 means roughly flat/-0.009%. |
| circulating_supply | double precision | Number of coin units in public circulation at capture time (API field `circulating_supply`), used by CoinGecko as the market_cap multiplier; denominated in the coin's own native units, not USD. |
| total_supply | double precision | Total coin units in existence (issued, including any not yet circulating) at capture time (API field `total_supply`), in native units; may equal circulating_supply for fully-circulating assets or exceed it for coins with locked/unminted supply, and may be null for uncapped/unknown-supply assets. |
| fully_diluted_valuation | double precision | Fully-diluted valuation in the vs_currency at capture time (API field `fully_diluted_valuation`): CoinGecko's price × max_supply market cap. A distinct valuation measure that is NOT derivable from what we otherwise store; null for coins CoinGecko has no diluted figure for. |
| max_supply | double precision | Hard supply cap in the coin's native units (API field `max_supply`): the maximum number of units that can ever exist (e.g. Bitcoin's 21,000,000). Distinct from total_supply (currently issued); null for uncapped/unknown-supply coins. |
| ath | double precision | All-time-high price in the vs_currency (API field `ath`): the historical maximum price CoinGecko has ever recorded for the coin. A historical extreme not reconstructable from an ingest-onward daily snapshot series. |
| atl | double precision | All-time-low price in the vs_currency (API field `atl`): the historical minimum price CoinGecko has ever recorded for the coin. Same rationale as `ath`. |
| ath_date | timestamp with time zone | Timestamp of the all-time-high price (API field `ath_date`, an RFC3339 instant like "2025-10-06T18:57:42.558Z"). A natural temporal attribute of the coin's price history with no local equivalent. |
| atl_date | timestamp with time zone | Timestamp of the all-time-low price (API field `atl_date`, an RFC3339 instant). Natural temporal attribute, as with `ath_date`. |
| market_cap_change_pct_24h | double precision | Percent change in market_cap over the trailing 24 hours ending at capture time (API field `market_cap_change_percentage_24h`). A rolling metric our daily snapshots can otherwise only approximate; can be negative. |
| last_updated | timestamp with time zone | Upstream quote-freshness timestamp (API field `last_updated`, an RFC3339 instant): the moment CoinGecko last refreshed this row's quote. Finer provenance than the capture-day available_date; distinct from synced_at (our own ingestion time). |
| available_date | date | PUBLIC-availability date = captured_date. STORED GENERATED column (migration 000205), read-only; the point-in-time column to filter and join on. It is the day the collector polled CoinGecko, because a current-state endpoint with no archive makes that the only day this state could have been obtained. |
Access crypto_markets
Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.
import obscura
client = obscura.Client("obs_live_…")
df = client.query(
dataset="crypto_markets",
symbols=["NVDA", "AAPL"],
start="2024-01-01",
)
Create a free account Browse all 95 datasets
Frequently asked questions
What is in the crypto_markets dataset?
Per-coin market data from CoinGecko: price, market cap and volume across thousands of cryptocurrencies. One CoinGecko top-market-cap coin's price/market-cap/volume/supply snapshot as returned by the `coins/markets` endpoint at the moment of one scrape, upserted per coin per UTC calendar day.
How do I avoid look-ahead bias with crypto_markets?
Filter on crypto_markets.available_date, the day the publisher made the row public. For this dataset that date is derived as follows — snapshot: available_date = captured_date, a STORED GENERATED column (migration 000205). WHY THAT IS THE DAY IT BECAME AVAILABLE: /coins/markets is a CURRENT-STATE endpoint — it answers only for `now`, offers no historical parameter, and keeps no archive — so this row exists solely because the collector asked on that day, and the day it asked IS the day the fact was obtainable. There is no publication event to lag behind and no source date to prefer. EVIDENCE that captured_date is the capture day and not a payload date: the collector sets it to Utc::now().date_naive() on a live run and to BronzeObject::captured_day() on a replay, so replaying a year-old capture reproduces that capture's day rather than today's. It is generated rather than written because a column a collector writes can be written from a clock, which is what a replay must never do. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.
In what formats can I get crypto_markets?
As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="crypto_markets". The column schema is public at https://api.obscura.trade/v1/catalog/crypto_markets.
Can I filter crypto_markets by company or symbol?
Yes. crypto_markets carries symbol, the column the API's symbols filter resolves against.
How often is crypto_markets updated?
Obscura refreshes crypto_markets on a daily schedule — that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.