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.gzThe -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 -
doneStraight 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 API | Every filter, both response shapes, and what each status code actually means. |
| File schemas | Every column and field, per dataset and per venue. The two venues do not share one. |
| How a market settles | Which stream decides the outcome, and how to recompute one yourself. |
| Cancel your subscription | Sign 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. |