Every API here is available over the APIs.io API and to AI agents over MCP.
{
"openapi": "3.1.0",
"info": {
"title": "BackQuant API v2",
"description": "\n# BackQuant API v2\n\nOptions + gamma-exposure focused public API. Built on the same data the\nBackQuant Pro Terminal renders - pre-computed every 30s, served from cache.\n\n## What's in v2\n\n* **Discovery**: `/v2/symbols` (universe + per-symbol freshness + supported\n endpoint list), `/v2/expiries` (active expiry tokens with DTE), `/v2/status`\n (per-symbol-per-category health with thresholds + overall classification).\n* **GEX**: composable levels (HVL / call wall / put support / max-pain /\n expected move / gamma-flip zones), strike profile with typed expiry\n filter, expiry profile, strike × expiry heatmap with downsampling,\n greek time-heatmap with DTE/OI/IV filters, history (cursor-paginated),\n Postgres-backed stress history, **per-expiry max-pain with pain curve**.\n* **Options**: filtered options chain with two-layer projection (top-level\n `?fields=` and per-contract `?include=oi,iv,greeks,bid_ask,volume,gex`)\n plus moneyness filter (`?moneyness_min=0.9&moneyness_max=1.1`), expiry\n summary table, full IV suite (surface / term structure / 25Δ-10Δ skew /\n curves / **single-expiry smile** / IV-RV history / VRP), expected move,\n **Breeden-Litzenberger probability density and surface**, typed greek\n profiles (delta / theta / vanna / charm / vega), strike × time charm/vega\n surfaces, strike × expiry 3D greek surface, OI by expiry + history,\n put/call ratio (intraday or daily), 0DTE & weekly premium tide,\n dated-futures term structure.\n* **Liquidation**: heatmap + leverage-tiered distribution.\n* **Multi**: `/v2/multi/gex/levels` - bundled multi-symbol read across the\n universe in one round-trip, with the same `?include=` model as the\n single-symbol endpoint.\n\n## Authentication\n\nEvery v2 route (except `/v2/openapi.json`, `/v2/docs`, `/v2/redoc`, and\n`/v2/health`) requires the `X-API-Key` header.\n\n```\nX-API-Key: bq_live_your_api_key_here\n```\n\n**Get your API key at [backquant.com/api-access](https://backquant.com/api-access).**\n\nThe same key works across v1 and v2 - if you already have a v1 key, no\nre-issuance is needed.\n\n## Rate limits\n\nTwo budgets, both derived from your subscription tier: a **monthly request\nallowance** per account, and a **per-minute burst** per key. Size your\nintegration against the monthly figure - sustained polling at the burst rate\nexhausts the month early.\n\n| Plan | Monthly allowance | Burst | Sustained |\n|---------------------------|-------------------|----------|-------------|\n| Starter (Terminal Yearly) | 10,000 req/month | 10/min | ~333/day |\n| Standard (Crypto API) | 250,000 req/month | 60/min | ~8,300/day |\n| Pro (Terminal + API) | 1,000,000/month | 120/min | ~33,000/day |\n| Enterprise | custom | 600/min | custom |\n\nHeaders `X-RateLimit-Limit / -Remaining / -Reset` (burst) and\n`X-Quota-Limit / -Used / -Remaining / -Period` (monthly) are included in\nevery response; the rate-limit triple is echoed inside `meta.rate_limit`\nwhen populated by middleware. Exceeding either budget returns `429`.\n\nThe live ladder is served at `GET /v2/meta` under `rate_limits`.\n\n## Response envelope\n\n```json\n{\n \"success\": true,\n \"data\": { ... },\n \"meta\": {\n \"version\": \"2.0\",\n \"timestamp\": \"2026-04-29T12:00:00.000Z\",\n \"request_id\": \"req_…\",\n \"symbol\": \"BTCUSDT\",\n \"spot_price\": 67213.5,\n \"computed_at\": \"2026-04-29T11:59:48.000Z\",\n \"freshness_seconds\": 12.0,\n \"source\": [\"deribit\",\"bybit\",\"okx\",\"binance\",\"derive\",\"thalex\",\"delta_india\",\"delta\",\"bullish\",\"paradex\"],\n \"exchanges_filtered\": [\"deribit\",\"bybit\"]\n }\n}\n```\n\n## Errors\n\n```json\n{\n \"success\": false,\n \"error\": {\n \"code\": \"NOT_FOUND\",\n \"message\": \"No data for BTCUSDT\"\n },\n \"meta\": { \"version\": \"2.0\", \"timestamp\": \"...\" }\n}\n```\n\n| Code | When |\n|-----------------------|------|\n| `UNAUTHORIZED` | Missing or invalid API key |\n| `FORBIDDEN` | Subscription doesn't allow API access |\n| `NOT_FOUND` | Symbol/expiry/etc. not present in cache |\n| `VALIDATION_ERROR` | Bad query parameters |\n| `RATE_LIMIT_EXCEEDED` | Per-tier limit hit |\n| `UPSTREAM_ERROR` | Cache or DB temporarily unreachable |\n| `INTERNAL_ERROR` | Anything else |\n ",
"version": "2.14"
},
"paths": {
"/health": {
"get": {
"tags": [
"Meta"
],
"summary": "v2 health check (no auth)",
"description": "Lightweight liveness/readiness probe for external monitors. Reports overall status plus per-subsystem (redis, db) flags. Auth-free - uses the same `check_connection_health()` Fly.io's load balancer hits at `/health`. Rate limit: **60/minute per IP**.\n\nStatus values: `ok` (all subsystems healthy), `degraded` (one subsystem failing), `unhealthy` (cache + DB both unreachable).",
"operationId": "get_health_health_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/meta": {
"get": {
"tags": [
"Meta"
],
"summary": "API-level metadata",
"description": "Static metadata about the v2 API. Supported symbol and exchange catalogs, the full error code list with HTTP status mapping, the auth scheme, the rate-limit ladder, the response envelope shape, and links to the OpenAPI spec / Swagger UI / ReDoc / endpoint catalog. Cheap (in-memory, no I/O).\n\nRequires `X-API-Key` like every other v2 route - anonymous discovery is via `/v2/openapi.json` (FastAPI standard, no auth).",
"operationId": "get_meta_meta_get",
"parameters": [
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"401": {
"description": "Invalid or missing API key",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/endpoints": {
"get": {
"tags": [
"Meta"
],
"summary": "Live catalog of every v2 route",
"description": "Introspects the running FastAPI sub-app and returns one entry per route with path, HTTP methods, tag, summary, description, and the parameter list. Each parameter carries its name, location (`query` / `path` / `header`), required flag, type, default, numeric bounds (if any), and `enum` array when the parameter is Literal-typed.\n\nDerived from in-memory route metadata - no Redis or DB I/O. Requires `X-API-Key`. For totally anonymous discovery use `/v2/openapi.json` (FastAPI built-in).",
"operationId": "get_endpoints_endpoints_get",
"parameters": [
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"401": {
"description": "Invalid or missing API key",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/changelog": {
"get": {
"tags": [
"Meta"
],
"summary": "API version history with breaking-change flags",
"description": "Returns the v2 release log plus the lifecycle status of v1. Customers integrating against either version use this to decide when to schedule re-integration work - `breaking: true` on any release is the signal to test, and `v1_status` flips to `deprecated` / `sunset` long before v1 is ever turned off.\n\nResponse is static - no I/O, sub-millisecond - so polling it from a CI job daily is free.",
"operationId": "get_changelog_changelog_get",
"parameters": [
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangelogResponse"
}
}
}
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/V2ErrorEnvelope"
}
}
},
"description": "Unauthorized"
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/symbols": {
"get": {
"tags": [
"Discovery"
],
"summary": "Supported symbols + per-symbol availability",
"description": "Lists every symbol covered by v2 with the data categories that are currently live, the freshness of each category, the spot price, the count of active expiries, and the full set of supported endpoints. Recommended first call for new integrations.",
"operationId": "get_symbols_symbols_get",
"parameters": [
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"401": {
"description": "Invalid or missing API key",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/expiries": {
"get": {
"tags": [
"Discovery"
],
"summary": "Active expiry tokens with DTE",
"description": "Currently-tradeable expiry tokens for a symbol with DTE and an ISO date when the token is parseable. Use this to populate filter UIs without pulling the heavier /options/expiry-summary.",
"operationId": "get_expiries_expiries_get",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": false,
"schema": {
"enum": [
"BTCUSDT",
"ETHUSDT",
"SOLUSDT",
"HYPEUSDT"
],
"type": "string",
"description": "Trading symbol: BTCUSDT, ETHUSDT, SOLUSDT, or HYPEUSDT.",
"default": "BTCUSDT",
"title": "Symbol"
},
"description": "Trading symbol: BTCUSDT, ETHUSDT, SOLUSDT, or HYPEUSDT."
},
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"401": {
"description": "Invalid or missing API key",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/status": {
"get": {
"tags": [
"Discovery"
],
"summary": "v2 service status - per symbol, per category freshness",
"description": "Operational status. Each (symbol × category) cell is classified as `healthy`, `degraded`, `unhealthy`, or `unavailable` based on cache age. The `overall_status` is the worst case across the grid. Thresholds in the response reflect the underlying compute cadence.\n\n**`venues`** reports each of the ten venues as `chain` (options chain aggregation) and `tape` (live trade tape), each `on` / `off` / `unknown`. `chain_symbols` lists the symbols the venue is live on and `chain_contracts` how many instruments it contributed on the last cycle - a venue that is `on` but delivering `0` contracts is visible rather than reading as healthy. `unknown` means no fresh status was published (a writer is down), never `off`.",
"operationId": "get_status_status_get",
"parameters": [
{
"name": "X-API-Key",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Api-Key"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"401": {
"description": "Invalid or missing API key",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"404": {
"description": "No data available for the requested resource",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No data for BTCUSDT"
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"422": {
"description": "Validation error on query parameters",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"errors": []
}
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
},
"429": {
"description": "Rate limit exceeded",
"content": {
"application/json": {
"example": {
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again later."
},
"meta": {
"version": "2.0",
"timestamp": "2026-04-29T12:00:00Z"
}
}
}
}
}
},
"security": [
{
"ApiKeyAuth": []
}
]
}
},
"/gex/levels": {
"get": {
"tags": [
"GEX"
],
"summary": "Composable GEX key levels",
"description": "All-expiry HVL/walls plus 0DTE-specific levels in a single call. Use `?include=` to opt in to optional sections - `spot,candles,zones,max_pain,expected_move,ranked`. Filter by exchange with `?exchanges=deribit,bybit,okx,binance`.\n\n`expected_move` `*_1sd` / `*_2sd` are the move to the nearest expiry with 7 to 30 days left (`days_to_expiry`), not a day's move; `upper_1d` / `lower_1d` are the one-day 1-sigma band (null until published).\n\n`max_pain` is the max pain of the 0DTE expiry (the front expiry: the next 08:00 UTC settle, when it is within a day), from that expiry's own open interest on every venue, the same number as `/v2/gex/max-pain?expiry=0dte`; null when no expiry settles within a day. The `odte` levels are that expiry's. `gamma_flip_zones` is every price where cumulative net GEX changes sign, within 0.5x to 2x spot, under either positioning model. `candles` are the Binance perpetual's 5-minute klines, the last 336 (about 28 hours); `spot.perps_price` is the last close.\n\n**`?exchanges=`** needs the per-venue split only the std model has: with `positioning=flow` a venue filter serves std, with `meta.extra.positioning_fallback_reason` = `exchange_filter_needs_std`. `max_pain` and `gamma_flip_zones` are not venue-filtered (`meta.extra.exchanges_not_applied_to`). Under `as_of` no venue filter is possible and `meta.exchanges_filtered` is null with `meta.extra.exchanges_not_applied`.\n\n**Positioning (default: flow).** `positioning=flow` (default) rebuilds HVL/walls/ranked from the chain + aggressor tape (BTC/ETH). `positioning=std` serves the legacy Redis textbook-signed levels. On flow fallback, `meta.extra.positioning_fallback_reason` explains why. Secondary walls (call_wall_2/3) are only present on the std cache path.\n\nThe WebSocket channel `gex.levels.{SYMBOL}` publishes the SAME flow-signed levels from the same builder - REST and WS agree.",
"operationId": "get_levels_gex_levels_get",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": false,
"schema": {
"enum": [
"BTCUSDT",
"ETHUSDT",
"SOLUSDT",
"HYPEUSDT"
],
"type": "string",
"description": "Trading symbol: BTCUSDT, ETHUSDT, SOLUSDT, or HYPEUSDT.",
"default": "BTCUSDT",
"title": "Symbol"
},
"description": "Trading symbol: BTCUSDT, ETHUSDT, SOLUSDT, or HYPEUSDT."
},
{
"name": "exchanges",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Comma-separated venue filter, e.g. `deribit,bybit`. Restricts the response to those venues' contribution, recomputed from the per-venue breakdown - it is a re-aggregation, not a post-hoc scaling. Unknown names are ignored; an all-invalid filter returns every venue rather than nothing. Venues: `deribit`, `bybit`, `okx`, `binance`, `derive`, `thalex`, `delta_india`, `delta`, `bullish`, `paradex`. A venue with no contracts on the surface contributes zero - use `GET /v2/status` to tell that apart from a venue that is switched off.",
"title": "Exchanges"
},
"description": "Comma-separated venue filter, e.g. `deribit,bybit`. Restricts the response to those venues' contribution, recomputed from the per-venue breakdown - it is a re-aggregation, not a post-hoc scaling. Unknown names are ignored; an all-invalid filter returns every venue rather than nothing. Venues: `deribit`, `bybit`, `okx`, `binance`, `derive`, `thalex`, `delta_india`, `delta`, `bullish`, `paradex`. A venue with no contracts on the surface contributes zero - use `GET /v2/status` to tell that apart from a venue that is switched off."
},
{
"name": "include",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Comma-separated optional sections: candles,expected_move,max_pain,r
# --- truncated at 32 KB (517 KB total) ---
# Full source: https://artifacts.apis.io/backquant-com/openapi/_original/backquant-com-api-openapi.json