Docs

With a key in hand, three commands put a file on your disk. The first block below is copy-pasteable as-is.

Sixty seconds

Replace the key on line 1 with your own (shown once on the page right after payment); paste the rest as-is.

# 1. your key, from the page you saw right after paying
export OT_KEY="ck_a1b2c3d4e5f6.xxxxxxxxxxxxxxxxxxxxxxxx"

# 2. what can I get?
curl -s -H "Authorization: Bearer $OT_KEY" \
  https://outcometick.com/v1/meta

# 3. find a file — with no date filter you get the newest day your key reaches
curl -s -H "Authorization: Bearer $OT_KEY" \
  "https://outcometick.com/v1/files?asset=btc&dataset=prices"

# 4. download it — every file in that listing carries its own url
URL=$(curl -s -H "Authorization: Bearer $OT_KEY" \
  "https://outcometick.com/v1/files?asset=btc&dataset=prices" \
  | jq -er '.files[0].url')

curl -L -H "Authorization: Bearer $OT_KEY" "$URL" -o btc-prices.csv.gz

The -L in step 4 matters: the download endpoint answers with a 302 to a short-lived object-storage URL. Files come straight from storage — they never pass through our server.

One line in Python

pandas, DuckDB and wget cannot attach a header to a URL, so the download endpoint also accepts ?api_key=. Be aware it puts the key in browser history and proxy logs — prefer the header wherever you can set one.

import pandas as pd, requests

KEY  = "ck_a1b2c3d4e5f6.xxxxxxxxxxxxxxxxxxxxxxxx"
auth = {"Authorization": f"Bearer {KEY}"}

r = requests.get("https://outcometick.com/v1/files", headers=auth,
                 params={"asset": "btc", "dataset": "prices"})

# the download endpoint also takes ?api_key=, for clients that cannot set a header
df = pd.read_csv(r.json()["files"][0]["url"] + "?api_key=" + KEY)
print(df.head())

Would rather not download the files at all? The same archive can be replayed against a strategy in a sealed sandbox, with fills matched against the depth that was really there — see the strategy SDK reference

Common tasks

One day, one asset, one interval

Every filter is optional. Use date for a single day, or from / to for a range. None of these examples names a day: they read it out of /v1/meta, which answers with the window your own key reaches.

# the newest day your key reaches
DAY=$(curl -s -H "Authorization: Bearer $OT_KEY" \
  https://outcometick.com/v1/meta | jq -r .lastDay)

# every BTC 5-minute order book for that day
curl -s -H "Authorization: Bearer $OT_KEY" \
  "https://outcometick.com/v1/files?date=$DAY&asset=btc&interval=5m&dataset=book"

Everything for 5min markets: books and settlement

The settlement streams (prices / twap30s / twap60s) are continuous and carry no interval of their own, so interval=5m leaves them out. Add none to get both in one call — settling a 5min market takes twap60s, and the book alone will not do it.

DAY=$(curl -s -H "Authorization: Bearer $OT_KEY" \
  https://outcometick.com/v1/meta | jq -r .lastDay)

# everything a 5-minute market needs: the 5m slices AND the settlement streams
curl -s -H "Authorization: Bearer $OT_KEY" \
  "https://outcometick.com/v1/files?date=$DAY&asset=btc&interval=5m,none"

A month of settlement prices, verified on the way in

Each file carries a sha256 identical to the archive manifest. A match proves you have exactly what we published — not merely that the transfer succeeded.

#!/usr/bin/env bash
# a month of BTC settlement prices, downloaded and checksum-verified
set -euo pipefail
export OT_KEY="ck_a1b2c3d4e5f6.xxxxxxxxxxxxxxxxxxxxxxxx"

# 30 days back from the newest day you can reach, clamped to the oldest —
# one call covers at most 92 days, and your window may be shorter than either
read -r FROM TO < <(curl -s -H "Authorization: Bearer $OT_KEY" \
  https://outcometick.com/v1/meta \
  | jq -r '((.lastDay | strptime("%Y-%m-%d") | mktime) - 29*86400
            | strftime("%Y-%m-%d")) as $back
           | "\(if $back < .firstDay then .firstDay else $back end) \(.lastDay)"')

curl -s -H "Authorization: Bearer $OT_KEY" \
  "https://outcometick.com/v1/files?asset=btc&dataset=prices&from=$FROM&to=$TO" \
| jq -r '.files[] | "\(.url) \(.name) \(.sha256)"' \
| while read -r url name sha; do
    curl -sL -H "Authorization: Bearer $OT_KEY" "$url" -o "$name"
    echo "$sha  $name" | sha256sum -c -
  done

Straight into one DataFrame

import pandas as pd, requests
from datetime import date, timedelta

KEY  = "ck_a1b2c3d4e5f6.xxxxxxxxxxxxxxxxxxxxxxxx"
BASE = "https://outcometick.com"
auth = {"Authorization": f"Bearer {KEY}"}

meta  = requests.get(f"{BASE}/v1/meta", headers=auth).json()
last  = date.fromisoformat(meta["lastDay"])
first = max(date.fromisoformat(meta["firstDay"]), last - timedelta(days=6))

# every 1-minute kline for BTC over the last week your key reaches
r = requests.get(f"{BASE}/v1/files", headers=auth, params={
    "asset": "btc", "dataset": "klines", "interval": "1m",
    "from": first.isoformat(), "to": last.isoformat(),
})
files = r.json()["files"]

df = pd.concat(
    pd.read_csv(f["url"] + "?api_key=" + KEY) for f in files
).sort_values("ts_ms")
print(len(df), "rows")

Reference

Data download APIEvery filter, both response shapes, and what each status code actually means.
File schemasEvery column and field, per dataset and per venue. The two venues do not share one.
How a market settlesWhich stream decides the outcome, and how to recompute one yourself.
Cancel your subscriptionSign in to the Stripe customer portal with the email you paid with. Cancelling takes effect at the end of the paid period and the key works until then; card changes and invoices are there too. Range purchases and stablecoin payments are one-off, so there is nothing to cancel.