Flow by strike
Cumulative multi-venue aggressor buying and selling by strike on still-open contracts, split into call buy, call sell, put buy and put sell.
curl --request GET \
--url https://api.example.com/tape/flow-by-strike \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.example.com/tape/flow-by-strike"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.example.com/tape/flow-by-strike', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/tape/flow-by-strike",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/tape/flow-by-strike"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/tape/flow-by-strike")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/tape/flow-by-strike")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}GET /v2/tape/flow-by-strike
Real taker flow on still-open contracts (expiry_date >= today UTC),
aggregated by strike into four quadrants: call buy, call sell, put buy,
put sell. This is cumulative inventory of aggressor prints behind live
OI, not a rolling lookback window.
Use it to see where aggressors have been active by strike without pulling
the full trade history.
Who it’s for
| You need… | Use |
|---|---|
| Per-strike sold vs bought (calls and puts) | This endpoint |
| Time-bucket buy/sell imbalance | /v2/tape/imbalance |
| Ranked net premium by strike over a lookback | /v2/tape/strike-heat |
| Raw trades | /v2/tape / /v2/tape/recent |
Coins
BTCUSDT and ETHUSDT only. SOLUSDT / HYPEUSDT return 404 - they
do not yet have a multi-venue aggressor tape of this shape.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
symbol | string | required | BTCUSDT or ETHUSDT |
expiry | YYYY-MM-DD | all open | Restrict to one live expiry |
expiries | CSV of YYYY-MM-DD | - | Combine several live expiries; takes precedence over expiry |
venues | CSV | all | Filter: deribit, bybit, okx, binance, derive, thalex, delta_india, delta |
min_premium_usd | number ≥ 0 | 0 | Drop prints below this USD premium (whale floor) |
include | string | - | by_venue adds the per-venue split and the disagreement statistic (see below) |
expiry and expiries to include every still-open contract.
Per-venue split (?include=by_venue)
Dealer flow aggregated across eight venues answers which way are dealers
positioned. It does not tell you which venue is driving it, and that turns
out to matter more often than not.
curl -H "X-API-Key: bq_live_your_key_here" \
"https://api.backquant.com/v2/tape/flow-by-strike?symbol=BTCUSDT&include=by_venue"
by_venue array parallel to strikes, so you can zip the two rather
than join on strike:
"by_venue": [
{
"strike": 30000,
"venues": {
"deribit": { "call_buy": 0.0, "call_sell": 41200.5,
"put_buy": 0.0, "put_sell": 0.0, "net_size": 1413.10 },
"okx": { "...": "...", "net_size": 116.49 },
"bybit": { "...": "...", "net_size": -14.35 }
}
}
]
net_size is sold minus bought, the same convention the dealer flow model
uses, so a venue’s number here and its contribution there point the same way.
Why this exists
Deribit carries about 88% of BTC option premium. That is why most feeds are Deribit-only, and why it looks sufficient. It is not. Measured over seven days of live data, restricted to contracts at least two venues traded $5,000 or more of premium on:| contracts | venues disagree on direction | |
|---|---|---|
| BTC | 391 | 271 (69.3%) |
| ETH | 250 | 172 (68.8%) |
BTC 86,000 call, 11 Sep - aggregate +38.35 Deribit -26.20 (200k)⋅Bybit∗∗+44.63∗∗(143k) · OKX +12.72 ($51k)A Deribit-only view reports dealers short that strike. They are long it, and the venues carrying the other half of the volume are the reason.
venue_disagreement
Returned in the body and in meta.extra:
"venue_disagreement": {
"strikes_compared": 106,
"strikes_disagreeing": 82,
"disagreement_pct": 77.4
}
Cost
The split is opt-in because it multiplies the row count, not because it is expensive: it is one extraGROUP BY column on a scan that already reads these
rows. Measured at 880ms against the aggregated query’s 961ms.
Response shape
{
"success": true,
"data": {
"coin": "BTC",
"expiry": null,
"expiries": null,
"available_expiries": ["2026-08-18", "2026-08-19", "2026-08-21"],
"available_venues": [
"binance", "bybit", "delta", "delta_india",
"deribit", "derive", "okx", "thalex"
],
"spot_price": 63426.5,
"strikes": [
{
"strike": 63000,
"call_buy": { "premium": 120450.2, "size": 12.4, "trades": 38 },
"call_sell": { "premium": 98010.0, "size": 9.1, "trades": 22 },
"put_buy": { "premium": 210300.5, "size": 18.2, "trades": 55 },
"put_sell": { "premium": 175200.0, "size": 15.0, "trades": 41 }
}
],
"totals": {
"call_buy": ...,
"call_sell": ...,
"put_buy": ...,
"put_sell": ...
},
"block_trades": 17,
"ts": "2026-08-18T02:15:00+00:00"
},
"meta": { "...": "..." }
}
Quadrant fields
Each ofcall_buy / call_sell / put_buy / put_sell:
| Field | Unit | Meaning |
|---|---|---|
premium | USD | Sum of premium_usd on matching prints |
size | Coin-equivalent | Sum of tape amount (not raw venue contracts) |
trades | count | Number of prints |
call_sell.size − call_buy.size (passive/maker inventory from aggressor
tape). See Positioning.
Important properties
- Cumulative on open contracts - not “last N hours.” Expired prints
drop when
expiry_daterolls past UTC today. - Native taker side - every venue stamps
directionat ingest (Deribit, Bybit, OKX, Binance, Derive, Thalex, Delta India, Delta Global). This is not quote-rule inference. - Cached ~90s - single-flight compute; safe to poll for dashboards.
Example
curl "https://api.backquant.com/v2/tape/flow-by-strike?symbol=BTCUSDT&expiry=2026-08-18" \
-H "X-API-Key: $BQ_API_KEY"
# Whale floor + venue filter
curl "https://api.backquant.com/v2/tape/flow-by-strike?symbol=BTCUSDT&min_premium_usd=25000&venues=deribit,bybit,delta_india" \
-H "X-API-Key: $BQ_API_KEY"
See also
Positioning (flow vs std)
Venue coverage
Tape overview
Greek TRACE
Authorizations
Your BackQuant API key (same key as v1)
Headers
Query Parameters
Trading symbol: BTCUSDT, ETHUSDT, SOLUSDT, or HYPEUSDT.
BTCUSDT, ETHUSDT, SOLUSDT, HYPEUSDT Single live expiry YYYY-MM-DD. Omit for all open contracts.
Comma-separated live expiries YYYY-MM-DD to COMBINE; takes precedence over expiry.
Minimum premium_usd per trade (whale filter).
x >= 0CSV venue filter. Default: all. Allowed: binance, bybit, delta, delta_india, deribit, derive, okx, thalex
Optional sections. by_venue splits each strike's flow across the venues that traded it, and reports how often they disagree on direction under meta.extra.venue_disagreement. Off by default because it multiplies the row count; the query cost is the same.
Response
Successful Response
curl --request GET \
--url https://api.example.com/tape/flow-by-strike \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.example.com/tape/flow-by-strike"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.example.com/tape/flow-by-strike', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/tape/flow-by-strike",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/tape/flow-by-strike"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/tape/flow-by-strike")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/tape/flow-by-strike")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}