Command-line client for Aperiodic.io — institutional-grade market microstructure, liquidity and order flow metrics with full exchange universe coverage.
Latest release: v1.3.0
Download the binary for your platform:
| Platform | Architecture | Download |
|---|---|---|
| Linux | x86_64 | aperiodic-linux-amd64 |
| Linux | ARM64 | aperiodic-linux-arm64 |
| macOS | x86_64 | aperiodic-darwin-amd64 |
| macOS | Apple Silicon | aperiodic-darwin-arm64 |
| Windows | x86_64 | aperiodic-windows-amd64.exe |
| Windows | ARM64 | aperiodic-windows-arm64.exe |
Or use the install script (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/aperiodic-io/cli/main/install.sh | bashOr manually (Linux/macOS):
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsSL "https://github.com/aperiodic-io/cli/releases/latest/download/aperiodic-${OS}-${ARCH}" -o aperiodic
chmod +x aperiodicSign in once per machine:
aperiodic loginIt opens your browser, or, over SSH or on a machine without a display, prints a link to open on any device. Check the code there matches the one in your terminal and allow it. A free account works: it covers every metric for 2025 on BTC, ETH, SOL and 10 RWA perps.
The machine gets a key of its own, named after it and saved in ~/.config/aperiodic/credentials (readable only by you; %APPDATA%\aperiodic on Windows, or $APERIODIC_CONFIG_DIR). The Python client reads the same file. See and revoke each machine's key at aperiodic.io/home/api.
aperiodic whoami # which key is in use, and what the plan covers
aperiodic logout # revoke this machine's key and remove itRunning aperiodic login again keeps a saved key that still works. aperiodic login --force replaces it with a new one and revokes the old one, so keys don't pile up on your account. If a saved key was revoked, every command says so and asks you to sign in again.
For scheduled jobs, CI and containers, set a key instead. Make a named key for the job at aperiodic.io/home/api:
export APERIODIC_API_KEY=apk_…Every command picks its key in this order:
APERIODIC_API_KEY, when it is set;- the shared public demo key, for a
--previewrequest; - the key
aperiodic loginsaved.
So preview data (--preview) needs no API key, and a saved key never takes over a preview request: even a revoked one can't break the free sample. The Python client uses the same order.
Raw data needs a key on the Prime + Raw plan; aperiodic raw coverage and aperiodic raw --preview work without one.
Aperiodic also runs a remote MCP server at https://aperiodic.io/mcp, for Claude, ChatGPT, Cursor and other agents.
Add it to Claude · ChatGPT · VS Code, or see aperiodic.io/mcp for Cursor, Claude Code and other clients.
aperiodic <metric> [flags]
aperiodic symbols [flags]
aperiodic raw <dataset> [flags]
aperiodic raw coverage [flags]
aperiodic stream <dataset> [flags]
The first argument is the metric name. Use symbols to list available symbols for an exchange, raw for raw data, and stream for the live stream.
OHLCV / VWAP
| Metric | Description |
|---|---|
ohlcv |
Open/high/low/close/volume |
vtwap |
Volume/time-weighted average price |
Trade metrics
| Metric | Description |
|---|---|
flow |
Buy/sell trade flow |
trade_size |
Trade size distribution |
impact |
Price impact |
range |
Price range |
updownticks |
Up/down tick count |
run_structure |
Run structure |
returns |
Returns |
slippage |
Slippage |
Order book metrics
| Metric | Description |
|---|---|
l1_price |
L1 best bid/ask price |
l1_imbalance |
L1 order book imbalance |
l1_liquidity |
L1 liquidity |
l2_imbalance |
L2 order book imbalance |
l2_liquidity |
L2 liquidity |
Derivative metrics
| Metric | Description |
|---|---|
basis |
Basis (spot vs. perp) |
funding |
Funding rates |
open_interest |
Open interest |
derivative_price |
Derivative price |
| Flag | Default | Description |
|---|---|---|
--exchange |
binance-futures |
Exchange name |
--symbol |
Trading pair symbol (Atlas unified symbology) | |
--interval |
1h |
Aggregation interval |
--start-date |
Start date (YYYY-MM-DD) |
|
--end-date |
End date (YYYY-MM-DD) |
|
--output-dir |
Output directory for Parquet files (required) | |
--timestamp |
exchange |
Timestamp source (exchange or true) |
--max-concurrent |
10 |
Maximum concurrent downloads |
--preview |
false |
Query the free preview dataset (no subscription; whitelisted parameters only) |
List symbols:
aperiodic symbols --exchange binance-futuresDownload OHLCV data:
aperiodic ohlcv \
--exchange binance-futures \
--symbol perpetual-BTC-USDT:USDT \
--interval 1h \
--start-date 2024-01-01 \
--end-date 2024-03-31 \
--output-dir ./dataDownload trade flow:
aperiodic flow \
--exchange binance-futures \
--symbol perpetual-BTC-USDT:USDT \
--interval 1h \
--start-date 2024-01-01 \
--end-date 2024-03-31 \
--output-dir ./dataDownload basis:
aperiodic basis \
--exchange binance-futures \
--symbol perpetual-BTC-USDT:USDT \
--interval 1h \
--start-date 2024-01-01 \
--end-date 2024-03-31 \
--output-dir ./dataPreview data (no API key required):
aperiodic ohlcv --preview \
--exchange binance-futures \
--symbol perpetual-BTC-USDT:USDT \
--interval 5m \
--start-date 2025-05-01 \
--end-date 2025-05-31 \
--output-dir ./data--preview fetches a free, curated slice of data from the preview endpoint — no subscription and no API key required (the shared public demo key is used automatically). Requests must match one of the whitelisted parameter combinations (exchange, symbol, interval, timestamp, date range) listed at aperiodic.io/catalog.
| Exchange | ID |
|---|---|
| Binance Futures | binance-futures |
| OKX Perpetuals | okx-perps |
| Hyperliquid Perpetuals | hyperliquid-perps |
15s, 30s, 1m, 5m, 15m, 30m, 1h, 4h, 1d
15s and 30s need a Tier 3 subscription and are not available for the derivative metrics.
All metric commands download Parquet files to --output-dir, fetched concurrently (tunable via --max-concurrent). Raw data uses its own file layout.
History up to 2026-07-31 is one file per month; from 2026-08-01 onwards it is one file per day. You always ask for a date range and get back every file covering it, so a range spanning the changeover downloads the earlier months as monthly files followed by a daily file per day.
Filenames follow the granularity, zero-padded so a directory listing sorts chronologically:
2026-07.parquet # monthly
2026-08-01.parquet # daily
2026-08-02.parquet
Raw trades, top-of-book quotes and derivative ticks for Binance, OKX and Hyperliquid perpetuals: the data the metrics are built from. Raw data needs the Prime + Raw plan (pricing); it uses the same API key and symbols as the metrics. To try it without an account, use --preview.
| Dataset | Contents | binance-futures |
okx-perps |
hyperliquid-perps |
|---|---|---|---|---|
trades |
Every trade: id, taker side, price, amount | yes | yes | yes |
quotes |
Top of book: best bid/ask price and amount | yes | yes | yes |
mark_price |
Mark price, one row per change | yes | yes | yes |
index_price |
Index price, one row per change | yes | yes | yes |
funding_rate |
Funding rate and next funding time | yes | yes | yes |
Hyperliquid's derivative feed carries no exchange time, so in its mark_price, index_price and funding_rate files exchange_timestamp is modelled, and an exchange_timestamp_kind column ("modelled") follows it. Raw L2 order books are not offered.
Every file starts with exchange_timestamp (the venue's time) and local_timestamp (when the event reached our capture machine). Timestamps are UTC.
Download a year of trades:
aperiodic raw trades \
--exchange binance-futures \
--symbol perpetual-BTC-USDT:USDT \
--start-date 2025-01-01 \
--end-date 2025-12-31 \
--output-dir ./rawList what is available (no API key needed):
aperiodic raw coverage # one row per dataset and exchange
aperiodic raw coverage --dataset quotes --exchange okx-perps # every symbol: first/last day, days, size
aperiodic raw coverage --symbol perpetual-BTC-USDT:USDT # one symbol across datasets| Flag | Default | Description |
|---|---|---|
--exchange |
binance-futures |
Exchange name |
--symbol |
Trading pair symbol (Atlas unified symbology) | |
--start-date |
First day, UTC (YYYY-MM-DD) |
|
--end-date |
Last day, inclusive (YYYY-MM-DD); ranges over 366 days are split into several requests for you |
|
--output-dir |
Root folder for the file layout below (required) | |
--max-concurrent |
4 |
Maximum concurrent downloads |
--overwrite |
false |
Download files again even if already present with the expected size |
--preview |
false |
Fetch the free preview file with the shared demo key (dates are ignored) |
aperiodic raw coverage takes --dataset, --exchange and --symbol as filters; with none it prints the summary.
Files land in the bucket's own Hive layout, the same one the Python client's download_raw writes, so either can top up a folder the other started. History before 2026-08-01 is one file per month; from then on it is one file per day:
raw/trades/exchange=binance-futures/symbol=perpetual-BTC-USDT:USDT/year=2025/month=06/data.parquet # monthly
raw/trades/exchange=binance-futures/symbol=perpetual-BTC-USDT:USDT/year=2026/month=08/day=01/data.parquet # daily
On Windows, : is not allowed in file names, so it is written as %3A, which Hive-partition readers decode back.
- Monthly files are written whole, so they can reach past the requested range: filter on
exchange_timestampwhen reading. Monthly and daily files sit at different depths, so read a folder with a recursive glob, e.g.pl.scan_parquet("raw/trades/**/data.parquet"). - A file already on disk with the expected size is skipped, so running the same command again resumes an interrupted download. Each file is streamed to
data.parquet.partand renamed once complete. - Download URLs are valid for one hour; one that has expired is re-requested automatically.
- A key whose plan lacks raw data gets a
raw_not_in_planerror with the link to upgrade.
--preview needs no account and no API key (the shared public demo key is used automatically). It returns the 2025-06 file of each venue's BTC perpetual: perpetual-BTC-USDT:USDT on binance-futures and okx-perps, perpetual-BTC-USDC:USDC on hyperliquid-perps, for any dataset the venue serves. --start-date and --end-date are not needed.
aperiodic raw quotes --preview \
--exchange okx-perps \
--symbol perpetual-BTC-USDT:USDT \
--output-dir ./rawaperiodic stream subscribes to the live WebSocket feed (wss://stream.aperiodic.io/v1/stream) and prints one JSON line per row to stdout, so it pipes straight into jq or a file. It needs an API key on a plan with live data; symbols are the same as for the metrics.
aperiodic stream ohlcv \
--exchange binance-futures \
--interval 1m \
--symbols perpetual-BTC-USDT:USDT,perpetual-ETH-USDT:USDT{"channel":"ohlcv.binance-futures.1m","snapshot":true,"data":{"exchange":11,"symbol":"perpetual-BTC-USDT:USDT","interval":"1m","time":1791307200000000,...}}
{"channel":"ohlcv.binance-futures.1m","snapshot":false,"data":{"exchange":11,"symbol":"perpetual-BTC-USDT:USDT","interval":"1m","time":1791307260000000,...}}
datais the server's row object, unchanged apart from whitespace (no re-escaping). On plans that include it, the latest row per symbol arrives first with"snapshot":true.- Granted and rejected channels, server warnings and reconnects go to stderr.
- Dropped connections reconnect with capped exponential backoff and re-subscribe. This covers network drops, server restarts or drains (closes such as 1000, 1001, 1011, 1012), handshake 5xx and 75 s of silence. The backoff starts over only once a connection has delivered a row or stayed up for 60 s.
- Replay. On 1m and longer intervals, a reconnect asks for the bars since the newest one received, so rows published while disconnected arrive first, marked
"replay":true(the server keeps 1 h of 1m to 30m bars, 3 h of 1h, the newest 4h bar and 48 h of 1d).--sincereplays from a time of your choosing on the first connect, too, and--replay=falseturns the reconnect replay off. A summary of each replay goes to stderr, including bars the server does not hold, which you fill from the REST API. A row can arrive both replayed and live with the same key: overwrite it. If the server is too busy replaying (after it restarts), the subscribe is sent again after a short random pause; a replay cut short at the server's size cap is followed up from the next bar, a follow-up refused while the server is busy is sent again, and a drop while a replay is catching up resumes it from where it got to. Below 1m nothing is replayed. - It exits 1 with the server's message, without retrying, if:
- the key is refused (401), the plan has no live access (403), or the handshake gets 426;
- the connection limit is reached (429) on the first connect. After a session has been subscribed, a 429 is retried, because the server may still be counting the dropped socket;
- the first connect gets no HTTP response at all (bad URL, DNS, TLS or proxy failure);
- every channel is rejected, the server unsubscribes the last one, or the server answers the subscribe with an error;
- the server closes with a 4000–4999 code (4001: plan lapsed or key rotated) or 1008 (rate-limit abuse).
- Ctrl-C closes the socket cleanly and exits 130; SIGTERM exits 143.
| Flag | Description | Default |
|---|---|---|
--exchange |
Exchange name | binance-futures |
--interval |
Aggregation interval | 1m |
--symbols |
Comma-separated symbols | every symbol your plan allows |
--count |
Stop after this many live rows (snapshot and replayed rows are printed but not counted) | no limit |
--duration |
Stop after this long, e.g. 90s, 1h |
no limit |
--since |
Replay the bars from this time on before the live rows: an RFC 3339 time (2026-11-09T10:00:00Z) or how long ago (30m). 1m and longer intervals |
none |
--replay |
After a reconnect, replay the bars missed while disconnected | true |
Catch up on the last 30 minutes, then keep streaming:
aperiodic stream ohlcv --symbols perpetual-BTC-USDT:USDT --since 30mTake the next closed 1m bar and stop:
aperiodic stream ohlcv --symbols perpetual-BTC-USDT:USDT --count 1 | jq 'select(.snapshot | not) | .data'Set APERIODIC_STREAM_URL to point at another stream endpoint.
Requires Go 1.24+.
git clone https://github.com/aperiodic-io/cli.git
cd cli
go build -o aperiodic ./cmd/aperiodic