> ## Documentation Index
> Fetch the complete documentation index at: https://docs.backquant.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Market bundle

> Price, open interest and funding for one symbol and window in a single request, on one aligned time axis.

Candles, open interest and funding for one symbol over one window, in a single
request.

Six series in one request: candles, open interest, funding, CVD on both the
perpetual and spot aggregates, and liquidations.

Use this instead of calling the individual endpoints. It is one round trip
rather than six, and measured about **3x faster** than making those calls
separately.

Pick a subset with `series=` when you do not need all six.

## One shared time axis

**Every series shares the response's single `timestamps` array.** Index `i` of
any series corresponds to `timestamps[i]`, so you can zip them positionally
without joining on time.

This matters more than it sounds. The series come from different tables and do
not all carry every bucket. Measured over a year at `1d`: candles 372, funding
364, liquidations 367, across the *same* first and last timestamp. Returned
unaligned, a positional zip would pair one day's open interest with another
day's price and every number would still look plausible.

A bucket a series does not have is **`null` at that position**, never carried
forward from the previous one. Filling it would invent a print that did not
happen and bias any average computed over the column.

`meta.extra.coverage` reports how many buckets each series actually filled, so
sparsity is visible without counting nulls:

```json theme={null}
{
  "data": {
    "timestamps": [1756080000000, 1756166400000, 1756252800000],
    "candles":    { "close": [80332.7, 80383.1, 80401.2] },
    "funding":    { "rate_open": [0.0001, null, 0.0003] }
  },
  "meta": {
    "extra": {
      "coverage": { "candles": 3, "funding": 2 },
      "aligned": true
    }
  }
}
```

<Note>
  The individual endpoints still return their own `timestamps`. Only the bundle
  reindexes onto a shared axis, because doing that join is the reason it exists.
</Note>

<Note>
  **Quota is charged per series, not per request.** Three series cost three
  units, exactly as three separate calls would, and the count is reported in
  `meta.extra.quota_units_charged`.

  The bundle saves you round trips and alignment work, never allowance. Ask only
  for the series you need.
</Note>

## Partial results

A series that cannot be served comes back as `null`, with its reason under
`meta.extra.errors`. The rest of the bundle is unaffected.

This is deliberate: one cold or unavailable series should not cost you the two
that were ready.

```json theme={null}
{
  "data": { "candles": { }, "open_interest": { }, "funding": null },
  "meta": {
    "extra": {
      "series": ["candles", "open_interest", "funding"],
      "quota_units_charged": 3,
      "errors": { "funding": "No funding data for SOLUSDT" }
    }
  }
}
```

## Windows and cost

Long windows are read from continuous aggregates rather than raw one-minute
rows, so a year at `1d` is no more expensive than a day at `1m`. The source
actually used is reported in `meta.extra.sources`.

Windows are capped at one year, matching retention. Your plan's historical
window applies on top of that: see `history_depth` and `your_plan` in
[`/v2/meta`](/api/v2/discovery/meta).

## See also

<CardGroup cols={2}>
  <Card title="Candles" href="/api/v2/market/candles" icon="chart-candlestick" />

  <Card title="Rate limits" href="/concepts/rate-limits" icon="gauge" />
</CardGroup>


## OpenAPI

````yaml GET /market/bundle
openapi: 3.1.0
info:
  title: BackQuant API v2
  description: >-

    # BackQuant API v2


    Options + gamma-exposure focused public API. Built on the same data the

    BackQuant Pro Terminal renders - pre-computed every 30s, served from cache.


    ## What's in v2


    * **Discovery**: `/v2/symbols` (universe + per-symbol freshness + supported
      endpoint list), `/v2/expiries` (active expiry tokens with DTE), `/v2/status`
      (per-symbol-per-category health with thresholds + overall classification).
    * **GEX**: composable levels (HVL / call wall / put support / max-pain /
      expected move / gamma-flip zones), strike profile with typed expiry
      filter, expiry profile, strike × expiry heatmap with downsampling,
      greek time-heatmap with DTE/OI/IV filters, history (cursor-paginated),
      Postgres-backed stress history, **per-expiry max-pain with pain curve**.
    * **Options**: filtered options chain with two-layer projection (top-level
      `?fields=` and per-contract `?include=oi,iv,greeks,bid_ask,volume,gex`)
      plus moneyness filter (`?moneyness_min=0.9&moneyness_max=1.1`), expiry
      summary table, full IV suite (surface / term structure / 25Δ-10Δ skew /
      curves / **single-expiry smile** / IV-RV history / VRP), expected move,
      **Breeden-Litzenberger probability density and surface**, typed greek
      profiles (delta / theta / vanna / charm / vega), strike × time charm/vega
      surfaces, strike × expiry 3D greek surface, OI by expiry + history,
      put/call ratio (intraday or daily), 0DTE & weekly premium tide,
      dated-futures term structure.
    * **Liquidation**: heatmap + leverage-tiered distribution.

    * **Multi**: `/v2/multi/gex/levels` - bundled multi-symbol read across the
      universe in one round-trip, with the same `?include=` model as the
      single-symbol endpoint.

    ## Authentication


    Every v2 route (except `/v2/openapi.json`, `/v2/docs`, `/v2/redoc`, and

    `/v2/health`) requires the `X-API-Key` header.


    ```

    X-API-Key: bq_live_your_api_key_here

    ```


    **Get your API key at
    [backquant.com/api-access](https://backquant.com/api-access).**


    The same key works across v1 and v2 - if you already have a v1 key, no

    re-issuance is needed.


    ## Rate limits


    Two budgets, both derived from your subscription tier: a **monthly request

    allowance** per account, and a **per-minute burst** per key. Size your

    integration against the monthly figure - sustained polling at the burst rate

    exhausts the month early.


    | Plan                      | Monthly allowance | Burst    | Sustained   |

    |---------------------------|-------------------|----------|-------------|

    | Starter (Terminal Yearly) | 10,000 req/month  | 10/min   | ~333/day    |

    | Standard (Crypto API)     | 250,000 req/month | 60/min   | ~8,300/day  |

    | Pro (Terminal + API)      | 1,000,000/month   | 120/min  | ~33,000/day |

    | Enterprise                | custom            | 600/min  | custom      |


    Headers `X-RateLimit-Limit / -Remaining / -Reset` (burst) and

    `X-Quota-Limit / -Used / -Remaining / -Period` (monthly) are included in

    every response; the rate-limit triple is echoed inside `meta.rate_limit`

    when populated by middleware. Exceeding either budget returns `429`.


    The live ladder is served at `GET /v2/meta` under `rate_limits`.


    ## Response envelope


    ```json

    {
      "success": true,
      "data": { ... },
      "meta": {
        "version": "2.0",
        "timestamp": "2026-04-29T12:00:00.000Z",
        "request_id": "req_…",
        "symbol": "BTCUSDT",
        "spot_price": 67213.5,
        "computed_at": "2026-04-29T11:59:48.000Z",
        "freshness_seconds": 12.0,
        "source": ["deribit","bybit","okx","binance","derive","thalex","delta_india","delta"],
        "exchanges_filtered": ["deribit","bybit"]
      }
    }

    ```


    ## Errors


    ```json

    {
      "success": false,
      "error": {
        "code": "NOT_FOUND",
        "message": "No data for BTCUSDT"
      },
      "meta": { "version": "2.0", "timestamp": "..." }
    }

    ```


    | Code                  | When |

    |-----------------------|------|

    | `UNAUTHORIZED`        | Missing or invalid API key |

    | `FORBIDDEN`           | Subscription doesn't allow API access |

    | `NOT_FOUND`           | Symbol/expiry/etc. not present in cache |

    | `VALIDATION_ERROR`    | Bad query parameters |

    | `RATE_LIMIT_EXCEEDED` | Per-tier limit hit |

    | `UPSTREAM_ERROR`      | Cache or DB temporarily unreachable |

    | `INTERNAL_ERROR`      | Anything else |
        
  version: '2.7'
servers: []
security: []
tags:
  - name: Meta
    description: Unauthenticated metadata + endpoint catalog (planning your integration)
  - name: Discovery
    description: Symbol catalog, active expiries, and service status
  - name: GEX
    description: Gamma exposure analytics
  - name: Options
    description: Options chain, IV, greeks, probability, premium tide
  - name: Liquidation
    description: Liquidation heatmap and distribution
  - name: Multi
    description: Bundled multi-symbol endpoints (single round-trip across the universe)
paths:
  /market/bundle:
    get:
      tags:
        - Market
      summary: Price, open interest and funding in one call
      description: >-
        Every time-indexed market series for a symbol, over one window, in a
        single request: price, open interest, funding, CVD on both the perpetual
        and spot aggregates, and liquidations.


        Use this instead of calling the individual endpoints: it is one round
        trip instead of six, and measured about 3x faster.


        **Every series shares one `timestamps` array.** Index `i` of any series
        corresponds to `timestamps[i]`, so you can zip them positionally without
        joining on time. The series come from different tables and do not all
        carry every bucket, so a gap is `null` at that position - never carried
        forward from the previous bucket, which would invent data.
        `meta.extra.coverage` reports how many buckets each series actually
        filled.


        **Quota:** charged per series requested, not per request. Three series
        cost three units, exactly as three separate calls would. The saving is
        latency and round trips, not allowance, so bundling is never a way to
        get more data for less.


        **Cost to us:** none beyond the individual endpoints. Each series is
        fetched through the same cache key those endpoints use, so a bundle
        following individual calls is served from cache, and concurrent callers
        share one computation.


        Partial failure does not fail the request: a series that cannot be
        served is returned as `null` with its reason under `meta.extra.errors`,
        so one cold series never costs you the others.
      operationId: get_bundle_market_bundle_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: interval
          in: query
          required: false
          schema:
            enum:
              - 1m
              - 5m
              - 15m
              - 30m
              - 1h
              - 4h
              - 1d
            type: string
            description: Interval for the gridded series.
            default: 1h
            title: Interval
          description: Interval for the gridded series.
        - name: hours
          in: query
          required: false
          schema:
            type: integer
            maximum: 8760
            minimum: 1
            description: Lookback window in hours, applied to every series.
            default: 168
            title: Hours
          description: Lookback window in hours, applied to every series.
        - name: series
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              Comma-separated subset of `candles`, `open_interest`, `funding`,
              `cvd_perp`, `cvd_spot`, `liquidations`. Defaults to all six. Ask
              only for what you need - each one costs a quota unit.
            title: Series
          description: >-
            Comma-separated subset of `candles`, `open_interest`, `funding`,
            `cvd_perp`, `cvd_spot`, `liquidations`. Defaults to all six. Ask
            only for what you need - each one costs a quota unit.
        - 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: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Your BackQuant API key (same key as v1)

````