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

# Price across a range of trade sizes

> The same impact model as `/v1/depth/{pair}/quote`, walked into a ladder so it reads like an order book. `asks` is buying (long), `bids` is selling (short); both ascend in notional out to `maxNotionalUsd`.

`cumSizeUsd` is **cumulative**: each level prices one trade of that full size, not an increment on the level below. Summing levels is meaningless.

`maxNotionalUsd` is the pair's max open interest, or its current OI where that is larger — the same extent the trading UI's depth chart draws.

`levels` only sets sampling density. The underlying curve is piecewise linear — flat until volume crosses the pair's threshold, then a straight line — so a higher count draws a smoother chart but adds almost no information. Some pairs return one repeated price, when their threshold exceeds their own OI cap.

Either pair spelling is accepted, as with the quote route.

Rate limit: 100 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.



## OpenAPI

````yaml /api-reference/openapi.json get /v1/depth/{pair}
openapi: 3.1.0
info:
  title: Ostium Builder API
  version: 1.0.0
  description: >
    REST API for the Ostium Builder SDK and integrating partners: live prices,
    depth, OHLC, and market hours.


    ## Authentication


    **None required today.**


    ## Quick start


    1. **Snapshot** — `GET /v1/prices` (or `GET /v1/prices/{pair}`) for an
    initial board.

    2. **Stream** — connect to `WS /v1/prices/stream` for live ticks — see the
    **Price Stream** section.

    3. **Candles** — `POST /v1/ohlc` for historical OHLC.

    4. **Sessions** — `GET /v1/market-hours` for calendars (cache it; use tick
    flags for “open right now”). The pair list is under **Markets**.

    5. **Depth** — `GET /v1/depth/{pair}/quote` for the price a given size
    actually gets.

    6. **Status** — `GET /v1/status` to check the price feed is live before
    relying on quotes.


    Typed clients: use `@ostium/builder-sdk`, or generate one from this document
    with any OpenAPI 3.1 client generator.


    ## Rate limits


    Applied per IP. Most limits are per route; where a group of endpoints shares
    one budget the endpoint's own description says so. Every rate-limited
    response carries `x-ratelimit-limit`, `x-ratelimit-remaining`, and
    `x-ratelimit-reset`; 429s add `retry-after`. **Read the headers** — do not
    hard-code the documented figures.


    ## Errors


    Two envelopes:


    - **Framework** — `{ "error": "<Status Name>", "message": "..." }` (optional
    `issues` / `details`).

    - **Upstream proxy** — `{ "error": "<human message>" }` from OHLC failures.


    `error` means different things across the two shapes — do not switch on it
    alone until a future major version unifies them.


    Every response carries `x-request-id`. Quote it when reporting a problem.


    ## Hosts


    - Production: `https://builder.prod.bedrock.ostium.io`
servers:
  - url: https://builder.prod.bedrock.ostium.io
    description: Production
security: []
tags:
  - name: Prices
    description: Live prices and historical candles
  - name: Price Stream
    description: >-
      Live prices over a WebSocket. This section is the contract — everything
      below is what the

      server actually sends.


      ### Connecting


      ```

      wss://builder.prod.bedrock.ostium.io/v1/prices/stream?pairs=BTC-USD,ETH-USD

      ```


      `?pairs=` is optional; omit it to receive every asset. No authentication,
      though the handshake is

      capped — see **Limits**. Browser clients are subject to an origin
      allowlist; a rejected upgrade is

      closed with a bare `403`.


      ### Server messages


      One snapshot on connect, carrying the `seq` baseline for every asset:


      ```json

      { "type": "snapshot", "seq": { "BTC-USD": 41 }, "data": [{ "pair":
      "BTC-USD", "bid": 1, "mid": 1, "ask": 1 }] }

      ```


      Then one frame per tick:


      ```json

      { "type": "tick", "seq": 42, "data": { "pair": "BTC-USD", "bid": 1, "mid":
      1, "ask": 1 } }

      ```


      `data` is byte-identical to a row from `GET /v1/prices` — `seq` sits on
      the frame, not inside it,

      so the tick payload stays the same shape as REST.


      | `type` | Sent when |

      | --- | --- |

      | `snapshot` | Once, on connect |

      | `tick` | A price updates |

      | `gap` | Ticks were dropped for you — precedes the next frame you receive
      |

      | `ack` | Your `subscribe`/`unsubscribe` was applied |

      | `error` | Your message was rejected; the filter is unchanged |


      ### Client messages


      ```json

      { "type": "subscribe",   "pairs": ["EUR-USD"] }

      { "type": "unsubscribe", "pairs": ["EUR-USD"] }

      ```


      Every message gets exactly one reply — an `ack` carrying `pairCount` (the
      number of assets you now

      receive; `null` is all of them, `0` is none), or an `error` carrying a
      `code` of `malformed`,

      `unknown_type`, `invalid_pairs`, `no_filter` or `rate_limited`.


      ```json

      { "type": "ack",   "for": "subscribe", "pairCount": 2 }

      { "type": "error", "code": "no_filter", "message": "..." }

      ```


      Filters only widen. `subscribe` on an unfiltered connection is a no-op
      acked with

      `"pairCount": null` — to narrow, reconnect with `?pairs=`. `unsubscribe`
      needs a filtered

      connection and is rejected with `no_filter` otherwise. Unsubscribing your
      last asset leaves you

      receiving nothing (`"pairCount": 0`), which `subscribe` recovers without
      reconnecting.


      ### Detecting loss


      Delivery is best effort, but loss is **detectable**. If more than 1 MB is
      buffered for a slow

      client, ticks are dropped rather than queued — and you are told:


      ```json

      { "type": "gap", "dropped": 17 }

      ```


      `seq` is a per-asset counter, so a jump between consecutive `tick` frames
      for one asset means you

      missed that many. Baseline each asset from the `snapshot`, then compare.


      Two limits on what `seq` can tell you. It is **per connection** —
      process-wide, not global, so it

      does not survive a reconnect and is not comparable across replicas. And
      there is **no replay**: the

      stream tells you that you fell behind, not what you missed, so recover by
      re-reading

      `GET /v1/prices`.


      Assets with no trading schedule are omitted entirely, from the snapshot
      and from ticks. A missing

      asset is not a signal that its market is closed.


      ### Limits


      Per pod, so the effective ceiling scales with replica count. Read the
      headers on a rejected

      upgrade rather than hard-coding these.


      | Limit | Default | Rejected with |

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

      | Connections per IP | 20 | `429` |

      | Connections in total | 500 | `503` |

      | Upgrades per IP per minute | 60 | `429` |

      | Client messages per socket per minute | 120 | `error`, `code:
      "rate_limited"` |


      Rejected upgrades carry `Retry-After`.


      ### Heartbeat


      The server pings every 30 seconds and terminates a connection that has not
      ponged since the

      previous ping. Browsers pong automatically; other clients need a library
      that does. On shutdown the

      server closes with `1001 going away` after draining.
    x-traitTag: true
  - name: Liquidity
    description: >-
      What a given trade size actually costs, priced against the live impact
      model
  - name: Markets
    description: >-
      What is listed and when it trades.


      ### Pairs


      Every pair on Ostium. Use `id` wherever a request takes a `pairIndex`, and
      the symbol wherever

      one takes a `pair`. Do not infer the id from a row's position — read the
      column.


      A pair marked `not yet` in **Tradeable** is listed on chain but has no
      open-interest ceiling, so

      it cannot be traded and `GET /v1/depth/{pair}` answers `503` for it (the
      `/quote`

      sub-resource still prices it, with `availableNotionalUsd: 0`). It is shown
      rather than hidden so

      the ids either side of it stay right.


      Note `GET /v1/pairs` returns the subgraph's own spelling in `from`/`to`,
      which for renamed

      assets is the legacy one — `SPX` where this table says `US500`. The table
      uses the symbol the

      price and depth endpoints expect.


      <!-- MARKETS:START -->

      | id | Pair | Tradeable |

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

      | 0 | `BTC-USD` | yes |

      | 1 | `ETH-USD` | yes |

      | 2 | `EUR-USD` | yes |

      | 3 | `GBP-USD` | yes |

      | 4 | `USD-JPY` | yes |

      | 5 | `XAU-USD` | yes |

      | 6 | `XCU-USD` | yes |

      | 7 | `WTI-USD` | yes |

      | 8 | `XAG-USD` | yes |

      | 9 | `SOL-USD` | yes |

      | 10 | `US500-USD` | yes |

      | 11 | `US30-USD` | yes |

      | 12 | `US100-USD` | yes |

      | 13 | `JP225-JPY` | yes |

      | 14 | `UK100-GBP` | yes |

      | 15 | `GER40-EUR` | yes |

      | 16 | `USD-CAD` | yes |

      | 17 | `USD-MXN` | yes |

      | 18 | `NVDA-USD` | yes |

      | 19 | `GOOG-USD` | yes |

      | 20 | `AMZN-USD` | yes |

      | 21 | `META-USD` | yes |

      | 22 | `TSLA-USD` | yes |

      | 23 | `AAPL-USD` | yes |

      | 24 | `MSFT-USD` | yes |

      | 25 | `USD-CHF` | yes |

      | 26 | `AUD-USD` | yes |

      | 27 | `NZD-USD` | yes |

      | 28 | `XPD-USD` | yes |

      | 29 | `XPT-USD` | yes |

      | 30 | `HK50-HKD` | yes |

      | 31 | `COIN-USD` | yes |

      | 32 | `HOOD-USD` | yes |

      | 33 | `MSTR-USD` | yes |

      | 34 | `CRCL-USD` | yes |

      | 35 | `BMNR-USD` | yes |

      | 36 | `SBET-USD` | yes |

      | 37 | `GLXY-USD` | yes |

      | 38 | `BNB-USD` | yes |

      | 39 | `XRP-USD` | yes |

      | 40 | `TRX-USD` | yes |

      | 41 | `HYPE-USD` | yes |

      | 42 | `LINK-USD` | yes |

      | 43 | `ADA-USD` | yes |

      | 44 | `PLTR-USD` | yes |

      | 45 | `AMD-USD` | yes |

      | 46 | `NFLX-USD` | yes |

      | 47 | `ORCL-USD` | yes |

      | 48 | `RIVN-USD` | yes |

      | 49 | `COST-USD` | yes |

      | 50 | `XOM-USD` | yes |

      | 51 | `CVX-USD` | yes |

      | 52 | `URA-USD` | yes |

      | 53 | `USD-KRW` | not yet |

      | 54 | `KR2550-USD` | yes |

      | 55 | `BRENT-USD` | yes |

      | 56 | `GEV-USD` | yes |

      | 57 | `SHEL-USD` | yes |

      | 58 | `UNG-USD` | yes |

      | 59 | `XLE-USD` | yes |

      | 60 | `ARM-USD` | yes |

      | 61 | `ASML-USD` | yes |

      | 62 | `AVGO-USD` | yes |

      | 63 | `CAT-USD` | yes |

      | 64 | `INTC-USD` | yes |

      | 65 | `SMCI-USD` | yes |

      | 66 | `TSM-USD` | yes |

      | 67 | `MU-USD` | yes |

      | 68 | `SNDK-USD` | yes |

      | 69 | `HYG-USD` | yes |

      | 70 | `TLT-USD` | yes |

      | 71 | `MP-USD` | yes |

      | 72 | `DRAM-USD` | yes |

      | 73 | `REMX-USD` | yes |

      | 74 | `BB-USD` | yes |

      | 75 | `CRWV-USD` | yes |

      | 76 | `DELL-USD` | yes |

      | 77 | `MRNA-USD` | yes |

      | 78 | `MRVL-USD` | yes |

      | 79 | `NBIS-USD` | yes |

      | 80 | `LLY-USD` | yes |

      | 81 | `SKHY-USD` | yes |

      | 82 | `SPCX-USD` | yes |

      <!-- MARKETS:END -->
  - name: Portfolio
    description: A wallet's open positions, resting orders and executed history
  - name: Orders
    description: >-
      Partner-signed intents executed as a single transaction — the position
      opens or closes and fills, or the whole call reverts. Onboarding lives
      here too: it is the one-time step every order depends on.
  - name: Status
    description: Whether the API is answering usefully right now
paths:
  /v1/depth/{pair}:
    get:
      tags:
        - Liquidity
      summary: Price across a range of trade sizes
      description: >-
        The same impact model as `/v1/depth/{pair}/quote`, walked into a ladder
        so it reads like an order book. `asks` is buying (long), `bids` is
        selling (short); both ascend in notional out to `maxNotionalUsd`.


        `cumSizeUsd` is **cumulative**: each level prices one trade of that full
        size, not an increment on the level below. Summing levels is
        meaningless.


        `maxNotionalUsd` is the pair's max open interest, or its current OI
        where that is larger — the same extent the trading UI's depth chart
        draws.


        `levels` only sets sampling density. The underlying curve is piecewise
        linear — flat until volume crosses the pair's threshold, then a straight
        line — so a higher count draws a smoother chart but adds almost no
        information. Some pairs return one repeated price, when their threshold
        exceeds their own OI cap.


        Either pair spelling is accepted, as with the quote route.


        Rate limit: 100 requests per 10 seconds per IP. Read `x-ratelimit-*` for
        the live budget rather than assuming this figure.
      parameters:
        - schema:
            type: string
            pattern: ^[A-Z0-9]+-[A-Z0-9]+$
            description: Trading pair as BASE-QUOTE, e.g. BTC-USD or FTSE-GBP
          required: true
          description: Trading pair as BASE-QUOTE, e.g. BTC-USD or FTSE-GBP
          name: pair
          in: path
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            description: Number of price levels per side
          required: false
          description: Number of price levels per side
          name: levels
          in: query
      responses:
        '200':
          description: Ladder for both sides
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                type: object
                properties:
                  pair:
                    type: string
                  mid:
                    type: number
                  asks:
                    type: array
                    items:
                      $ref: '#/components/schemas/DepthLevel'
                    description: Taker buy
                  bids:
                    type: array
                    items:
                      $ref: '#/components/schemas/DepthLevel'
                    description: Taker sell
                  maxNotionalUsd:
                    type: number
                    description: >-
                      Where both ladders stop — the pair's OI ceiling, or
                      current OI where that is larger. Shared across sides, so
                      `asks[i]` and `bids[i]` price the same size and
                      spread-at-size is one subtraction.
                  availableNotionalUsd:
                    type: object
                    properties:
                      bid:
                        type: number
                      ask:
                        type: number
                    required:
                      - bid
                      - ask
                    description: >-
                      Remaining OI capacity per side. Deep levels can exceed
                      these — the ladder is the whole impact curve, and this is
                      how much of it is reachable now.
                  openInterest:
                    type: object
                    properties:
                      longUsd:
                        type: number
                      shortUsd:
                        type: number
                      capUsd:
                        type: number
                    required:
                      - longUsd
                      - shortUsd
                      - capUsd
                    description: Raw open interest behind those numbers
                  topOfBook:
                    $ref: '#/components/schemas/TopOfBook'
                  priceTimestampMs:
                    type: integer
                required:
                  - pair
                  - mid
                  - asks
                  - bids
                  - maxNotionalUsd
                  - availableNotionalUsd
                  - openInterest
                  - topOfBook
                  - priceTimestampMs
              example:
                pair: ADA-USD
                mid: 0.18299
                asks:
                  - cumSizeUsd: 5000
                    cumAvgPrice: 0.182935
                    impactBps: 0.56
                  - cumSizeUsd: 10000
                    cumAvgPrice: 0.182971
                    impactBps: 1.12
                bids:
                  - cumSizeUsd: 5000
                    cumAvgPrice: 0.182845
                    impactBps: 0.56
                  - cumSizeUsd: 10000
                    cumAvgPrice: 0.182809
                    impactBps: 1.12
                maxNotionalUsd: 1000000
                availableNotionalUsd:
                  bid: 610900
                  ask: 587700
                openInterest:
                  longUsd: 412300
                  shortUsd: 389100
                  capUsd: 1000000
                topOfBook:
                  bid: 0.18291
                  ask: 0.18306
                priceTimestampMs: 1787198161000
        '400':
          description: Validation failed (bad pair format, `levels` outside 1-100)
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: The pair is not priced right now, or is not a listed market
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            Temporarily unable to build a book — no usable live quote, the pair
            publishes no open-interest ceiling, or the market parameters behind
            the model are unavailable. Retry rather than fail.
          headers:
            x-request-id:
              schema:
                type: string
              description: >-
                Always present, and always generated by the service — a value
                sent on the request is ignored. Use it to correlate a response
                with your own logs, and quote it when reporting a problem.
            x-ratelimit-limit:
              schema:
                type: string
              description: Requests allowed per window.
            x-ratelimit-remaining:
              schema:
                type: string
              description: Requests left in the current window.
            x-ratelimit-reset:
              schema:
                type: string
              description: Seconds until the window resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    DepthLevel:
      type: object
      properties:
        cumSizeUsd:
          type: number
          description: >-
            Cumulative USD notional at this level — each level prices one trade
            of that whole size, not an increment on the level below. Summing
            levels is meaningless.
          example: 5000
        cumAvgPrice:
          type: number
          description: Average fill price for a single trade of that cumulative size
          example: 0.182935
        impactBps:
          type: number
          description: Impact in basis points
          example: 0.56
      required:
        - cumSizeUsd
        - cumAvgPrice
        - impactBps
    TopOfBook:
      type: object
      properties:
        bid:
          type: number
          example: 0.18291
        ask:
          type: number
          example: 0.18306
      required:
        - bid
        - ask
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: HTTP status name, e.g. "Bad Request"
          example: Bad Request
        message:
          type: string
          description: Human-readable detail
          example: Validation failed
        issues:
          type: array
          items: {}
          description: Raw zod issue array — present on validation failures only
        details:
          description: Optional structured detail from the thrower
      required:
        - error
        - message

````