> ## Documentation Index
> Fetch the complete documentation index at: https://docs.testnet.dev.adipredictstreet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Crypto markets

> Price-based markets on BTC / ETH: the machine-readable crypto rule, the product catalog, the underlying-price curve endpoint, and how to find them in the catalog.

A **crypto market** is a binary market whose question is about the price
of a digital asset rather than a sporting or political outcome. It
trades, settles and redeems exactly like any other
[binary market](/concepts/markets/binary) — same orderbook, same
EIP-712 order, same `ConditionalTokens` redemption — but the payload
carries an extra machine-readable block so you never have to parse the
market title or the rules text.

Two product lines exist today:

<CardGroup cols={2}>
  <Card title="Long-dated price markets" icon="chart-line">
    Operator-listed `YES` / `NO` questions — "will BTC touch $64,400
            before 30 July?", "will the July UTC close be above $65,000?".
    Lifetimes of days to months. Described on this page.
  </Card>

  <Card title="Up/Down windows" icon="timer" href="/concepts/markets/crypto-updown">
    Machine-minted `Up` / `Down` markets on a recurring 15-minute (or
    hourly / daily) grid, grouped into a **series**. Their own page —
    they add a `cryptoWindow` block and a series navigation endpoint.
  </Card>
</CardGroup>

Both lines carry the same `crypto` block, the same instrument catalog
and the same live price feed, so a client that renders one renders the
other with one extra null-check.

## Finding crypto markets

Crypto markets live under the `crypto` **vertical**. The event list
takes the filter directly:

```http theme={null}
GET /api/events?vertical=crypto&limit=20
GET /api/events?vertical=crypto&asset=btc
GET /api/events?vertical=crypto&cadence=15-minute
```

| Query             | Behaviour                                                                                                                                                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `vertical=crypto` | Every crypto event. Open, data-driven vocabulary — see `GET /api/verticals`.                                                                                                                                                                                                                                 |
| `asset=<slug>`    | Coin slug (`btc`, `eth`, …). **Open set**: any coin with data gets its own slug, and a well-formed but unknown slug matches nothing (`200` with an empty list) rather than erroring. `asset=others` selects crypto events whose markets carry no identifiable asset. A malformed slug → `400 invalid_asset`. |
| `cadence=<slug>`  | Recurring-window cadence: `15-minute`, `hourly`, `daily`, `weekly`, `monthly`, `annual`. **Closed set** — an unknown value → `400 invalid_cadence`. Only [Up/Down window](/concepts/markets/crypto-updown) markets carry a cadence.                                                                          |

All three accept repeated keys (`?asset=btc&asset=eth`) or CSV
(`?asset=btc,eth`), up to 20 distinct values per field.

`GET /api/verticals` returns the counts behind those chips — on the
`crypto` vertical only, the summary carries an extra `crypto` block:

```json theme={null}
{
  "slug": "crypto",
  "group": "crypto",
  "count": 13,
  "live": 0,
  "openMarketCount": 22,
  "totalMarketCount": 93,
  "competitions": [],
  "scopes": [],
  "crypto": {
    "cadences": [{ "slug": "15-minute", "count": 1 }],
    "assets":   [{ "slug": "btc", "name": "Bitcoin", "count": 10 },
                 { "slug": "eth", "name": "Ethereum", "count": 3 }],
    "preMarket": { "count": 1 },
    "others":    { "count": 0 }
  }
}
```

Each bucket is an independent overlap count (one event can sit in
several) — never derive one from another, or from `count`. `cadences`
and `assets` map 1:1 onto `?cadence=` / `?asset=`; `preMarket` is
**informational only** — there is no matching `/api/events` filter, so
it has no list to reconcile against. Only buckets with `count > 0` are
emitted.

## The `crypto` block

Every market payload — `/api/markets`, `/api/markets/{slug}`, and the
nested `markets[]` on `/api/events` — carries a `crypto` field. It is
`null` on every non-crypto market, and on a crypto market it is the
structured counterpart of the human `resolutionCriteria` text:

```json theme={null}
{
  "symbol": "BTC-64400-20260730",
  "title": "64400",
  "vertical": "crypto",
  "marketType": "CRYPTO_PRICE_EVENT",
  "outcomeLabels": ["YES", "NO"],
  "crypto": {
    "productType":  "THRESHOLD_BY_DEADLINE",
    "direction":    "ABOVE",
    "targetPrice":  "64400",
    "instrumentId": "BTC-USD",
    "deadlineAt":   "2026-07-30T00:00:00.000Z",
    "month":        null
  },
  "cryptoWindow": null,
  "resolutionCriteria": "Resolves YES if the Chainlink BTC/USD one-minute candle high is at or above $64,400 USD at any time from 2026-07-29T07:00:00.000Z up to 2026-07-30T00:00:00.000Z (UTC). Equality counts as a touch. …"
}
```

| Field          | Type                             | Meaning                                                                                                                                                                                                               |
| -------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `productType`  | string                           | Product vocabulary, passed through **verbatim** from the operator side (see the catalog below). Treat it as an open string — a newly enabled product appears here with no API change.                                 |
| `direction`    | `"ABOVE"` \| `"BELOW"` \| `null` | Barrier direction. `null` on direction-less products (month-over-month close, Up/Down windows). Only `ABOVE` is listable in the current release.                                                                      |
| `targetPrice`  | string \| null                   | The strike, as a canonical **decimal string** (`"64400"`). `null` on strike-less products. Never a float — a threshold must round-trip exactly.                                                                       |
| `instrumentId` | string \| null                   | Price-catalog instrument (`BTC-USD`, `ETH-USD`, …). This is the key for both the [price-history endpoint](#underlying-price-curve) and the [`crypto_price_tick`](/concepts/websocket/crypto-price) WebSocket channel. |
| `deadlineAt`   | string \| null                   | `THRESHOLD_BY_DEADLINE` only — the observation cutoff, ISO-8601 UTC.                                                                                                                                                  |
| `month`        | string \| null                   | Monthly products only — `"YYYY-MM"` (UTC calendar month).                                                                                                                                                             |

<Note>
  **`title` carries the strike.** On a crypto market with a
  `targetPrice`, the market `title` is the bare strike digits
  (`"64400"`) — the surrounding copy lives on the parent event. Format
  the number yourself (thousands separators, currency); don't
  string-parse the rule out of the title.
</Note>

<Warning>
  **Branch on `crypto`, not on `marketType`.** Operator-listed crypto
  markets carry `marketType: "CRYPTO_PRICE_EVENT"`, but factory-minted
  [Up/Down windows](/concepts/markets/crypto-updown) leave `marketType`
  `null`. The reliable discriminators are `crypto !== null` (any crypto
  market) and `cryptoWindow !== null` (Up/Down window specifically).
</Warning>

## Product catalog

`productType` values the platform models. The **listable** column marks
what an operator can create in the current release; the rest are
modelled and understood by the settlement engine but gated off.

| `productType`                          | Question                                           | Window         | Strike | Listable                                                         |
| -------------------------------------- | -------------------------------------------------- | -------------- | ------ | ---------------------------------------------------------------- |
| `THRESHOLD_BY_DEADLINE`                | Price touches the level any time before a deadline | `deadlineAt`   | yes    | ✅                                                                |
| `THRESHOLD_DURING_MONTH`               | Price touches the level any time in a UTC month    | `month`        | yes    | ✅                                                                |
| `NEW_ALL_TIME_HIGH`                    | Asset sets a new all-time high during a UTC month  | `month`        | —      | ✅ (BTC only)                                                     |
| `MONTH_OVER_MONTH_CLOSE`               | Month's UTC close above the prior month's close    | `month`        | —      | ✅                                                                |
| `MONTHLY_CLOSE_ABOVE_STRIKE`           | Month's UTC close above a strike                   | `month`        | yes    | ✅                                                                |
| `UP_DOWN_WINDOW`                       | Window close above its pinned open                 | `cryptoWindow` | —      | machine-minted only ([details](/concepts/markets/crypto-updown)) |
| `BTC_DOMINANCE_AT_MONTH_END`           | BTC dominance at UTC month-end above a level       | `month`        | yes    | ⛔ no feed yet                                                    |
| `TOTAL_CRYPTO_MARKET_CAP_AT_MONTH_END` | Total market cap at UTC month-end above a level    | `month`        | yes    | ⛔ no feed yet                                                    |

Touch products (`THRESHOLD_*`) settle on a **first touch**: equality
counts, and the YES outcome locks the moment the barrier is touched,
even though the market settles after the observation window ends.

## Instruments

| `instrumentId` | Asset / quote | Metric                  | Status            |
| -------------- | ------------- | ----------------------- | ----------------- |
| `BTC-USD`      | BTC / USD     | spot price              | live              |
| `ETH-USD`      | ETH / USD     | spot price              | live              |
| `BTC-DOM`      | BTC / %       | BTC dominance           | modelled, no feed |
| `TOTAL-MCAP`   | TOTAL / USD   | total crypto market cap | modelled, no feed |

Prices are sourced from **Chainlink** — one-minute candlestick data for
long-dated products, Data Streams reports for the Up/Down open pin. The
`crypto-price-history` endpoint below rejects an instrument outside this
catalog with `422 instrument_unsupported`.

## Underlying price curve

The probability charts (`/ohlc`, `/price-history`) describe the
**market**. This endpoint describes the **coin** — the reference price
the market is written against, from the same finalized one-minute
candles the oracle ingests:

```http theme={null}
GET /api/markets/{symbol}/crypto-price-history?range=1h
```

Path parameter is the market **`symbol`** (like the other market
sub-resources — only the root `/api/markets/{slug}` detail takes a
slug). `range` is **required**:

| `range` | Bucket | Lookback  | Points (max) | Cache TTL |
| ------- | ------ | --------- | ------------ | --------- |
| `10m`   | 1 min  | 10 min    | 10           | 30 s      |
| `1h`    | 1 min  | 1 h       | 60           | 60 s      |
| `1d`    | 5 min  | 24 h      | 288          | 60 s      |
| `1w`    | 1 h    | 7 d       | 168          | 120 s     |
| `1m`    | 4 h    | 30 d      | 180          | 120 s     |
| `all`   | 1 d    | up to 2 y | ≤ 730        | 120 s     |

Buckets with no source candle are **absent**, not zero-filled — a
`1h` request typically returns 57–60 points. Draw gaps, don't
interpolate across them.

```json theme={null}
{
  "symbol":        "BTC15M-20260729T1730",
  "instrumentId":  "BTC-USD",
  "asset":         "BTC",
  "quoteAsset":    "USD",
  "range":         "10m",
  "bucketSeconds": 60,
  "from":          "2026-07-29T17:28:00.000Z",
  "to":            "2026-07-29T17:38:00.000Z",
  "points": [
    { "t": "2026-07-29T17:28:00.000Z", "price": "63695.98649614" },
    { "t": "2026-07-29T17:29:00.000Z", "price": "63670.46906982" }
  ],
  "spot":        { "price": "63744.09454151", "at": "2026-07-29T17:35:00.000Z" },
  "priceToBeat": "63672.45929874"
}
```

* `points[]` are **per-bucket closes** in ascending time order, already
  scaled to human decimal strings. Only complete, grid-aligned buckets
  are returned, so the series is stable between polls — `from` / `to`
  echo the snapped window the server actually queried.
* `spot` is the latest finalized minute for the instrument and may sit
  **past `to`**; `null` when no candles exist yet.
* `priceToBeat` is the reference line to draw on the same axis — the
  frozen open of an [Up/Down window](/concepts/markets/crypto-updown),
  already scaled. It is `null` on long-dated markets: their reference
  line is `crypto.targetPrice`.

| Status | Code                            | When                                                                                     |
| ------ | ------------------------------- | ---------------------------------------------------------------------------------------- |
| `400`  | `invalid_range`                 | `range` missing or not one of the presets — `error.details.allowed` echoes the valid set |
| `404`  | `market_not_found`              | unknown symbol, or a market that isn't publicly visible                                  |
| `422`  | `instrument_unsupported`        | crypto market whose `instrumentId` is outside the catalog above                          |
| `422`  | `instrument_source_unavailable` | crypto market whose price source is not registered yet                                   |

Public endpoint — no auth required. Two IP buckets apply together: the
shared markets ceiling (`600 req/min`) and this route's own
(`120 req/min`). Responses carry `Cache-Control: public, max-age=30`.

<Tip>
  For a live price, don't poll this endpoint — subscribe to
  [`crypto_price_tick`](/concepts/websocket/crypto-price) and append
  ticks to the curve you fetched. One feed per instrument serves every
  strike and every window on that asset.
</Tip>

## Settlement

Crypto markets settle through the standard binary path — oracle
proposes, challenge window, finalize, redeem — see
[Settlement flow](/concepts/settlement/flow#crypto-markets). The
difference is the evidence: the outcome is derived from Chainlink price
data against the immutable rule captured in the `crypto` block at
listing time, not from a sports data provider. The rule is written once
and never edited, so `crypto` on an open market is the same rule the
settlement engine will bind.
