openapi: 3.1.0
info:
  title: LiquidCompute API
  version: 0.0.1
  summary: Authenticated GPU price index and market data. (Preview — details may change before general availability.)
  license:
    name: Proprietary
    url: https://liquidcompute.co
  description: |
    LiquidCompute provides read-only GPU price index and current vendor-price
    data over HTTPS. All comparable values use USD per GPU-hour. Dates and timestamps
    are UTC.

    ## Quickstart

    Set your API key:

    ```sh
    export LIQUIDCOMPUTE_API_KEY="your_liquidcompute_api_key"
    ```

    Check service health. This endpoint is public and does not require an API key:

    ```sh
    curl "https://api.liquidcompute.co/health"
    ```

    Fetch the latest H100 custom index value:

    ```sh
    curl -H "X-API-Key: $LIQUIDCOMPUTE_API_KEY" \
      "https://api.liquidcompute.co/v1/custom-index/current?region_group=US&contract_type=combined&vendor_category=neocloud"
    ```

    Fetch daily transaction index data:

    ```sh
    curl -H "X-API-Key: $LIQUIDCOMPUTE_API_KEY" \
      "https://api.liquidcompute.co/v1/txn-index/current?gpu_family=hopper&region_group=US&contract_type=combined"
    ```

    Fetch informational history:

    ```sh
    curl -H "X-API-Key: $LIQUIDCOMPUTE_API_KEY" \
      "https://api.liquidcompute.co/v1/info-index/history?index_name=hopper&region_group=US&start_date=2026-01-01"
    ```

    Fetch the current vendor-price snapshot:

    ```sh
    curl -H "X-API-Key: $LIQUIDCOMPUTE_API_KEY" \
      "https://api.liquidcompute.co/v1/gpu-prices/current"
    ```

    ## Authentication

    Send your LiquidCompute API key in the `X-API-Key` header on every `/v1`
    request. Missing or invalid keys return `401`. A validation-service
    failure returns `503`; it is never reported as invalid credentials.
    Create a new LiquidCompute API key during production onboarding.

    ```http
    X-API-Key: your_liquidcompute_api_key
    ```

    The inference routes (`/v1/chat/completions`, `/v1/models` and
    `/v1/feeds/openrouter`) also accept the key as `Authorization: Bearer`,
    the way OpenAI SDKs send it.
    Send exactly one of the two headers; a request with both returns `401`.

    ## Entitlements

    Access to every data family — custom, transaction, and informational
    indices, private transactions, GPU prices, and reference series — and to
    inference is governed by your organization's entitlements. Requests for an
    index, or an index family, outside your entitlements return `404`
    `not_found`. Intraday custom index history (30-minute and hourly
    resolutions) additionally requires the `intraday` family.

    ## Rate Limits

    Limits are token buckets per API key: you can burst up to your bucket
    size and sustain your refill rate. Metadata is returned in response
    headers after parameter validation and authorization checks (during rare
    degraded operation the `X-RateLimit-*` headers may be omitted;
    `Retry-After` is still set on the gateway's own `429` responses):

    | Header | Description |
    | --- | --- |
    | `X-RateLimit-Limit` | Your bucket size (burst capacity) |
    | `X-RateLimit-Remaining` | Tokens currently available |
    | `X-RateLimit-Reset` | Unix timestamp of the estimated next-token time |
    | `Retry-After` | Seconds to wait; always present for `rate_limited` and `data_unavailable` (gateway default of 300 s when the upstream sends no hint), and forwarded for `upstream_rate_limited` when supplied |

    Two distinct `429` codes: `rate_limited` means your key's bucket is
    empty; `upstream_rate_limited` means the shared upstream data source is
    throttled. `Retry-After` is always present for both codes; when the
    upstream omits a hint, the gateway returns `Retry-After: 1`.

    ## Response Headers

    Every response includes `X-Request-Id`. Quote it when reporting a failed
    request; the same value is logged by the gateway and forwarded upstream.
    An inference response from the model router carries the router's id
    instead, which the gateway logs alongside its own.

    `/v1/custom-index/available` and `/v1/custom-index/reference` vary by API
    key. Their successful responses therefore include
    `Cache-Control: private, no-store` and `Vary: X-API-Key`; upstream cache
    headers are never forwarded on those two routes.

    ## Errors

    Errors are returned as JSON.

    ```json
    {
      "error": {
        "code": "not_found",
        "message": "Endpoint not found"
      }
    }
    ```

    On the inference routes errors also carry OpenAI's error `type`, so
    OpenAI SDKs raise them as usual.

    Common statuses:

    | Status | Code | Meaning |
    | --- | --- | --- |
    | `400` | `invalid_request` | Invalid or unsupported query parameter |
    | `401` | `unauthorized` | Missing or invalid API key |
    | `402` | `billing_required` | Chat completions need an active API billing account for your organization |
    | `402` | `billing_paused` | Your organization's chat completions are paused until a failed charge is paid |
    | `402` | `daily_limit_reached` | Your organization's chat completion spend today (UTC) reached its daily limit |
    | `404` | `not_found` | Unknown endpoint, unavailable resource, or an index or index family outside your authorization |
    | `405` | `method_not_allowed` | Method not allowed; only GET is supported (POST on `/v1/chat/completions`) |
    | `413` | `request_too_large` | An inference request body over 16 MiB |
    | `429` | `rate_limited` | Your key's rate limit was exceeded |
    | `429` | `upstream_rate_limited` | The shared upstream data source is throttled; retry shortly |
    | `429` | `concurrency_limited` | Your key already has as many inference requests in progress as its tier allows |
    | `502` | `upstream_error` | The upstream data source is unavailable |
    | `502` | `configuration_error` | Gateway-side configuration failure |
    | `503` | `service_unavailable` | API-key validation is temporarily unavailable |
    | `503` | `data_unavailable` | The data behind this endpoint has not been produced yet; retry after `Retry-After` |
servers:
  - url: https://api.liquidcompute.co
    description: Production base URL.
  - url: https://api.staging.liquidcompute.co
    description: Staging base URL.
security:
  - ApiKeyAuth: []
tags:
  - name: Health
    description: Service health.
  - name: Custom Index
    description: |
      Intraday per-GPU custom index values on a 30-minute UTC grid. Only
      realized intervals are returned.

      Custom indices are per-GPU series (identifiers like `h100-us`,
      `h200-us`, `b200-us`, `b300-us`, `a100-us`) and are **scoped to your
      API key**: `GET /v1/custom-index/reference` lists exactly the series
      your key can read. Requests for series outside your authorization
      return `404`.

      Supported filters:

      | Filter | Supported Values |
      | --- | --- |
      | `region_group` | `US` |
      | `contract_type` | `combined` |
      | `vendor_category` | `neocloud` |
      | `index_id` | Optional; defaults to your key's default index. Keys authorized for multiple indices with no default must pass it explicitly (`400` otherwise, listing your authorized ids). |
  - name: Transaction Index
    description: |
      Daily GPU price index values by GPU family, region, contract type, and
      index type.

      Index types:

      | `index_type` | Description |
      | --- | --- |
      | `blended_sigmoid` | Recommended default; blended transaction index |
      | `raw` | Raw transaction series without smoothing |

      `blended_static` was retired by the index provider and returns
      `400 invalid_request`.

      Requires the `txn-index` entitlement family; requests without it return
      `404` `not_found`.
  - name: Informational Index
    description: |
      Daily EWMA-smoothed posted-price index values with longer history.

      Requires the `info-index` entitlement family; requests without it return
      `404` `not_found`.
  - name: Private Transactions
    description: |
      Per-deal GPU compute transaction records underlying the transaction
      index — chip, contract type, term, VWAP price, discount, GPU count,
      region, and date for individual private deals.

      Included with Enterprise, with the full dataset available in one response.
      Requires the `private-txns` entitlement family; requests without it
      return `404` `not_found`.
  - name: GPU Market Prices
    description: |
      Current vendor-published GPU offers normalized to USD per GPU-hour.
      The response identifies the scrape run and its per-source coverage;
      `partial` runs are returned with the vendors they could reach rather
      than hidden behind an older snapshot. Region fields are nullable because
      many vendors do not publish regional information on their pricing page.
      The history and change-log routes replay the same scrapes over time.

      Requires the `gpu-prices` entitlement family; requests without it return
      `404` `not_found`.
  - name: Status
    description: |
      When each dataset last updated and when its next update is due. Open
      to any valid API key.
  # ── series lane: reference series ──
  - name: Reference Series
    description: |
      Daily token prices to read beside the GPU index: per-million-token
      prices for model tiers and individual models. Every value carries its
      source, and `attribution` is the text to show beside it.

      | Series | Kind | Unit | Source |
      | --- | --- | --- | --- |
      | `tokens-<tier>-input`, `tokens-<tier>-output` | tokens | USD/Mtok | Median list price across providers for the `open-frontier`, `open-small` and `closed-frontier` tiers (LiteLLM) |
      | `tokens-model-<model>-input`, `tokens-model-<model>-output` | tokens | USD/Mtok | One model's price: a lab's own list price, or for an open-weights model the median across inference providers (LiteLLM) |

      A per-model series names its token model in `model` and its token side
      in `side`; `/v1/series/offers` lists each inference provider's price
      for an open-weights model.

      A token period is the UTC day the price list was read. Values are
      refreshed several times a day and a period's value can be revised.

      Requires the `reference-series` entitlement family; requests without
      it return `404` `not_found`.
  # ── end series lane ──
  # ── inference lane ──
  - name: Inference
    description: |
      OpenAI-compatible chat completions on open-weights models served by
      Liquid Compute GPU nodes. Point an OpenAI SDK at
      `https://api.liquidcompute.co/v1` with your API key:

      ```sh
      curl https://api.liquidcompute.co/v1/chat/completions \
        -H "Authorization: Bearer $LIQUIDCOMPUTE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"model": "MODEL_ID", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
      ```

      `GET /v1/models` lists the models being served right now, with their
      context limits and prices. `GET /v1/feeds/openrouter` is the same
      catalogue in OpenRouter's provider feed format.

      Each completion's `usage` reports the prompt, cached prompt and
      completion tokens it was charged for; a stream reports them in its last
      chunk when `stream_options.include_usage` is true.

      Billing: completions are charged to your organization by the token, at
      the prices `GET /v1/models` lists, once a day (UTC) to the card your
      organization keeps on file under Settings, API billing. Large accounts
      and marketplaces are invoiced monthly instead. Until your organization
      has an active API billing account, a completion returns `402`
      `billing_required`. A daily charge that fails pauses completions
      (`402` `billing_paused`) until it is paid from the same page. Each
      organization has a daily spend limit, $100 until an admin changes it;
      once today's spend reaches it, completions return `402`
      `daily_limit_reached` until the next UTC day. Listing models and the
      provider feed are never charged or refused for billing.

      Limits: a request body may be at most 16 MiB. Each key may have a fixed
      number of completions in progress at once, set by its rate tier; a
      request over it returns `429` `concurrency_limited`. When every GPU
      node serving a model is busy, a completion returns `429`
      `capacity_exhausted` at once, with `Retry-After`, instead of queueing.

      Requires the `inference` entitlement family; requests without it
      return `404` `not_found`.
  # ── end inference lane ──
paths:
  /health:
    get:
      tags: [Health]
      summary: Health check
      description: Returns service health. This endpoint does not require authentication.
      security: []
      operationId: getHealth
      responses:
        '200':
          description: Service is healthy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              examples:
                ok:
                  value:
                    status: ok
        '503':
          description: Service is starting or draining — retry shortly.
          content:
            application/json:
              schema:
                type: object
                required: [status]
                properties:
                  status:
                    type: string
                    enum: [unavailable]
              examples:
                unavailable:
                  value:
                    status: unavailable
        '405':
          description: Method not allowed; only GET is supported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
  /v1/custom-index/available:
    get:
      tags: [Custom Index]
      summary: List custom index series
      description: Lists the custom index series available to your API key and their coverage windows.
      operationId: listCustomIndexSeries
      parameters:
        - $ref: '#/components/parameters/AvailableIndexId'
      responses:
        '200':
          description: Available custom index series.
          headers:
            Cache-Control:
              $ref: '#/components/headers/ScopedCacheControl'
            Vary:
              $ref: '#/components/headers/ScopedVary'
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomIndexAvailableResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/current:
    get:
      tags: [Custom Index]
      summary: Get latest custom index value
      description: Returns the latest realized 30-minute custom index value for the resolved index_id.
      operationId: getCurrentCustomIndex
      parameters:
        - $ref: '#/components/parameters/IndexId'
        - $ref: '#/components/parameters/CustomRegionGroup'
        - $ref: '#/components/parameters/CustomContractType'
        - $ref: '#/components/parameters/CustomVendorCategory'
      responses:
        '200':
          description: Latest custom index value.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomIndexPointResponse'
              examples:
                current:
                  value:
                    data:
                      - timestamp: '2026-06-03T13:00:00+00:00'
                        index_id: h100-us
                        index_name: h100
                        gpu_family: hopper
                        gpu_model: h100
                        region_group: US
                        contract_type: combined
                        vendor_category: neocloud
                        index_value: 2.5795
                        blended_value: 2.532
                        index_units: usd_per_gpu_per_hr
                        p5: 1.5328
                        p95: 4.1658
                    total_count: 1
                    filters:
                      index_id: h100-us
                      region_group: US
                      contract_type: combined
                      vendor_category: neocloud
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/as-of:
    get:
      tags: [Custom Index]
      summary: Get custom index value as of a time
      description: Returns the custom index value as of a UTC date or ISO-8601 datetime.
      operationId: getCustomIndexAsOf
      parameters:
        - name: as_of
          in: query
          required: true
          description: Date (`YYYY-MM-DD`) or ISO-8601 datetime.
          schema:
            type: string
          examples:
            date:
              value: '2026-03-01'
            datetime:
              value: '2026-03-01T14:30:00Z'
        - $ref: '#/components/parameters/IndexId'
        - $ref: '#/components/parameters/CustomRegionGroup'
        - $ref: '#/components/parameters/CustomContractType'
        - $ref: '#/components/parameters/CustomVendorCategory'
      responses:
        '200':
          description: Custom index value for the requested time.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomIndexPointResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/commitment-pricing:
    get:
      tags: [Custom Index]
      summary: Get commitment pricing by contract length
      operationId: getCommitmentCurve
      description: |
        Recent reserved, non-rolling deals for the selected GPU in us, global+us
        and apac+us regions, over 120 UTC dates ending today. Requires BOTH the
        selected index scope and private-txns family, included with Enterprise.
        Supported IDs are a100-us, h100-us, h200-us, b200-us and b300-us.
        Missing index_id follows the key's usual default-index rules. No
        historical as-of override is accepted.

        Exact stored term bounds form buckets. Midpoints divided by 30 define
        months. Bucket means are GPU-weighted. The log-tenor fit minimizes
        sum(sqrt(bucket GPUs) * (mean - a - b*ln(1+months))^2). It requires at
        least three distinct midpoints and is defined only over observed support.
        Twelve months means a 360-day commitment, not delivery twelve months
        from today. A 365-day bucket remains distinct. This is neither a
        forecast nor an executable quote; regions and contract quality vary.
        Insufficient or invalid fits return observations with a null fit.
        Fewer than ten deals sets sparse; it is not a confidence measure.
        Responses, including errors, are private and no-store.
      parameters:
        - $ref: '#/components/parameters/IndexId'
      responses:
        '200':
          description: Commitment observations and optional fitted curve.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/CommitmentCurve'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/forward-curve:
    get:
      tags: [Custom Index]
      summary: Deprecated alias for commitment pricing
      operationId: getLegacyCommitmentCurve
      deprecated: true
      description: |
        Use GET /v1/custom-index/commitment-pricing instead. This alias returns
        the same recent negotiated rates by contract length, with the same
        index-scope and private-txns entitlements, included with Enterprise.
        It does not price future delivery windows.
      parameters:
        - $ref: '#/components/parameters/IndexId'
      responses:
        '200':
          description: Recent commitment pricing observations and optional fitted line.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/CommitmentCurve'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/history:
    get:
      tags: [Custom Index]
      summary: Get custom index history
      description: |
        Returns custom index history with selectable resolution and pagination.

        Intraday resolutions (`30m`, `1h`, and the omitted default, which is
        the native 30-minute grid) require the `intraday` entitlement family on
        top of the index authorization; requests without it return `404`
        `not_found`. Daily history (`resolution=1d`) needs only the index.
      operationId: getCustomIndexHistory
      parameters:
        - name: resolution
          in: query
          required: false
          description: History resolution. `30m` and `1h`, and the omitted default, require the `intraday` family; pass `1d` for daily history.
          schema:
            type: string
            enum: [30m, 1h, 1d]
            default: 30m
        - name: start
          in: query
          required: false
          description: UTC date or ISO-8601 datetime.
          schema:
            type: string
          example: '2026-01-01'
        - name: end
          in: query
          required: false
          description: UTC date or ISO-8601 datetime.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Points per page. Maximum is 10000.
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            default: 1000
        - name: offset
          in: query
          required: false
          description: Pagination offset.
          schema:
            type: integer
            minimum: 0
            default: 0
        - $ref: '#/components/parameters/IndexId'
        - $ref: '#/components/parameters/CustomRegionGroup'
        - $ref: '#/components/parameters/CustomContractType'
        - $ref: '#/components/parameters/CustomVendorCategory'
      responses:
        '200':
          description: Custom index history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomIndexHistoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/custom-index/reference:
    get:
      tags: [Custom Index]
      summary: Get custom index reference
      description: Returns reference information for custom index endpoints available to your API key.
      operationId: getCustomIndexReference
      responses:
        '200':
          description: Custom index reference information (authored by the gateway from this spec and your key's authorizations).
          headers:
            Cache-Control:
              $ref: '#/components/headers/ScopedCacheControl'
            Vary:
              $ref: '#/components/headers/ScopedVary'
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomIndexReferenceResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/txn-index/current:
    get:
      tags: [Transaction Index]
      summary: Get latest transaction index value
      description: Returns the latest transaction index value.
      operationId: getCurrentTransactionIndex
      parameters:
        - $ref: '#/components/parameters/TransactionIndexType'
        - $ref: '#/components/parameters/GpuFamilyOptional'
        - $ref: '#/components/parameters/GpuGroupOptional'
        - $ref: '#/components/parameters/TransactionRegionGroup'
        - $ref: '#/components/parameters/TransactionContractType'
      responses:
        '200':
          description: Latest transaction index values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionIndexCurrentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/txn-index/as-of:
    get:
      tags: [Transaction Index]
      summary: Get transaction index values as of a date
      description: Returns transaction index values for a specific date.
      operationId: getTransactionIndexAsOf
      parameters:
        - name: as_of_date
          in: query
          required: true
          description: Date in `YYYY-MM-DD` format.
          schema:
            type: string
            format: date
          example: '2026-06-01'
        - $ref: '#/components/parameters/TransactionIndexType'
        - $ref: '#/components/parameters/GpuFamilyOptional'
        - $ref: '#/components/parameters/GpuGroupOptional'
        - $ref: '#/components/parameters/TransactionRegionGroup'
        - $ref: '#/components/parameters/TransactionContractType'
      responses:
        '200':
          description: Transaction index values for the requested date.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionIndexCurrentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/txn-index/history:
    get:
      tags: [Transaction Index]
      summary: Get transaction index history
      description: Returns daily transaction index history.
      operationId: getTransactionIndexHistory
      parameters:
        - $ref: '#/components/parameters/GpuFamilyOptional'
        - $ref: '#/components/parameters/GpuGroupOptional'
        - $ref: '#/components/parameters/TransactionIndexType'
        - $ref: '#/components/parameters/TransactionRegionGroup'
        - $ref: '#/components/parameters/TransactionContractType'
        - name: days_back
          in: query
          required: false
          description: Lookback window in days. Maximum is 730.
          schema:
            type: integer
            minimum: 1
            maximum: 730
            default: 365
      responses:
        '200':
          description: Transaction index history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionIndexHistoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/info-index/available:
    get:
      tags: [Informational Index]
      summary: List informational index series
      description: Lists available informational index series and coverage windows.
      operationId: listInformationalIndexSeries
      responses:
        '200':
          description: Available informational index series.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InformationalIndexAvailableResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/info-index/current:
    get:
      tags: [Informational Index]
      summary: Get latest informational index value
      description: Returns the latest informational index value.
      operationId: getCurrentInformationalIndex
      parameters:
        - $ref: '#/components/parameters/InfoIndexName'
        - $ref: '#/components/parameters/InfoRegionGroup'
      responses:
        '200':
          description: Latest informational index values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InformationalIndexResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/info-index/as-of:
    get:
      tags: [Informational Index]
      summary: Get informational index values as of a date
      description: Returns informational index values for a specific date.
      operationId: getInformationalIndexAsOf
      parameters:
        - name: as_of_date
          in: query
          required: true
          description: Date in `YYYY-MM-DD` format.
          schema:
            type: string
            format: date
          example: '2026-06-01'
        - $ref: '#/components/parameters/InfoIndexName'
        - $ref: '#/components/parameters/InfoRegionGroup'
      responses:
        '200':
          description: Informational index values for the requested date.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InformationalIndexResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/info-index/history:
    get:
      tags: [Informational Index]
      summary: Get informational index history
      description: Returns informational index history.
      operationId: getInformationalIndexHistory
      parameters:
        - name: index_name
          in: query
          required: true
          description: Index name.
          schema:
            type: string
            enum: [hopper, blackwell]
        - $ref: '#/components/parameters/InfoRegionGroup'
        - name: start_date
          in: query
          required: false
          description: Start date in `YYYY-MM-DD` format.
          schema:
            type: string
            format: date
          example: '2026-01-01'
        - name: end_date
          in: query
          required: false
          description: End date in `YYYY-MM-DD` format.
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Informational index history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InformationalIndexHistoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/gpu-prices/current:
    get:
      tags: [GPU Market Prices]
      summary: Get current GPU prices
      description: |
        Returns the comparable offers from the newest completed price scrape.
        `data` contains only normalized observations with a USD-per-GPU-hour
        rate; unpriced, unmapped, and unconverted source rows remain in the
        internal ledger and are not presented as comparable prices.

        There is no pagination or server-side filtering: every row belongs to
        the single run identified by `run`, so clients can compute comparisons
        without mixing scrape times. A `partial` run is valid and its `sources`
        array names which vendors were parsed, skipped, failed, or not attempted.

        Until the first scrape has completed, and whenever no completed scrape
        holds a comparable price, the endpoint returns `503` `data_unavailable`
        with a `Retry-After` header. That is an expected state, not an outage:
        retry after the indicated delay.

        Requires the `gpu-prices` entitlement family; requests without it
        return `404` `not_found`.
      operationId: getCurrentGPUPrices
      responses:
        '200':
          description: Current comparable GPU prices and scrape coverage.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GPUPricesResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          description: >-
            Either no completed scrape is available to serve yet
            (`data_unavailable`, with a `Retry-After` header) or API-key
            validation is temporarily unavailable (`service_unavailable`).
          headers:
            Retry-After:
              description: Seconds to wait before retrying; present for `data_unavailable`.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              examples:
                data_unavailable:
                  value:
                    error:
                      code: data_unavailable
                      message: The requested data is not available yet
                service_unavailable:
                  value:
                    error:
                      code: service_unavailable
                      message: Authentication service unavailable
  # ── history lane: provider price history, change log and freshness ──
  /v1/gpu-prices/history:
    get:
      tags: [GPU Market Prices]
      summary: Get GPU price history
      description: |
        Returns each vendor offer's daily price for one GPU model over an
        inclusive UTC date window, as step series built from every completed
        price scrape. A day's point is the offer's last price observed that
        day. Days with no change are still emitted, so a series can be drawn
        as steps: a day on which no scrape read the vendor carries its last
        price forward. A day is omitted while the offer is not listed (before
        the vendor first listed it, or after a scrape found it withdrawn), and
        days after the current UTC date are never emitted.

        An offer is identified by its vendor-published fields, the same
        vocabulary as `/v1/gpu-prices/current`; no two offers in one response
        share all of them. The window spans at most 92 days (`400`
        otherwise), and an empty window is an empty `offers` array.

        Requires the `gpu-prices` entitlement family; requests without it
        return `404` `not_found`.
      operationId: getGPUPriceHistory
      parameters:
        - $ref: '#/components/parameters/GPUPriceModel'
        - $ref: '#/components/parameters/GPUPriceHistoryStart'
        - $ref: '#/components/parameters/GPUPriceHistoryEnd'
      responses:
        '200':
          description: Daily step series per offer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GPUPriceHistoryResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/gpu-prices/changes:
    get:
      tags: [GPU Market Prices]
      summary: Get GPU price changes
      description: |
        Returns the change log for one GPU model: every price move, new
        listing and withdrawal revealed by a completed price scrape started on
        or after `since`, newest first. Each vendor's offers are compared
        with that vendor's previous successful scrape, so a vendor a scrape
        could not reach is never reported as having withdrawn its offers.
        `observed_at` is the start of the scrape that revealed the change.

        `previous_usd_per_gpu_hour` is null for `listed`, and
        `usd_per_gpu_hour` is null for `delisted`. `since` may be at most 91
        days before the current UTC date (`400` otherwise).

        Requires the `gpu-prices` entitlement family; requests without it
        return `404` `not_found`.
      operationId: getGPUPriceChanges
      parameters:
        - $ref: '#/components/parameters/GPUPriceModel'
        - $ref: '#/components/parameters/GPUPriceChangesSince'
      responses:
        '200':
          description: Price changes, newest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GPUPriceChangesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/status/freshness:
    get:
      tags: [Status]
      summary: Get dataset freshness
      description: |
        Returns when each dataset last updated successfully and when its next
        update is expected: one scheduled interval after the last. A
        `next_expected_at` in the past means the update is overdue. A dataset
        that has never updated is `null`.

        Open to any valid API key; no entitlement family is required.
      operationId: getFreshness
      responses:
        '200':
          description: Freshness per dataset.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FreshnessResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  # ── end history lane ──
  /v1/private-transactions:
    get:
      tags: [Private Transactions]
      summary: List private transactions
      description: |
        Returns individual private transaction records, newest first
        (`date` descending). There is no pagination: the full filtered
        window is returned in one response.

        H100 coverage begins September 2024; B200 coverage begins August
        2025; additional models are added as data arrives. Each data
        refresh replaces the full record set for a GPU model with a new
        snapshot — an irregular cadence, not real time. Records returned
        by an earlier request may therefore be restated or removed by a
        later refresh, not only added to, and responses carry no snapshot
        identifier tying a record to the refresh that produced it.

        Included with Enterprise. Requires the `private-txns` entitlement
        family; requests without it return `404` `not_found`.
      operationId: listPrivateTransactions
      parameters:
        - $ref: '#/components/parameters/PrivateTxnGpuModel'
        - $ref: '#/components/parameters/PrivateTxnContractType'
        - $ref: '#/components/parameters/PrivateTxnRegion'
        - $ref: '#/components/parameters/PrivateTxnStartDate'
        - $ref: '#/components/parameters/PrivateTxnEndDate'
      responses:
        '200':
          description: Private transaction records.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PrivateTransactionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  # ── series lane: reference series ──
  /v1/series:
    get:
      tags: [Reference Series]
      summary: List reference series
      description: |
        Returns every token reference series with its unit, source, attribution
        and latest value. `latest` is the most recent period's value, and is
        `null` for a series with no value yet.

        Requires the `reference-series` entitlement family; requests without
        it return `404` `not_found`.
      operationId: listReferenceSeries
      responses:
        '200':
          description: The reference-series catalog.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReferenceSeriesCatalogResponse'
              examples:
                catalog:
                  value:
                    series:
                      - id: tokens-model-deepseek-v4-pro-input
                        name: DeepSeek V4 Pro, input tokens
                        kind: tokens
                        unit: USD/Mtok
                        currency: USD
                        source: LiteLLM
                        attribution: 'Source: LiteLLM'
                        model:
                          id: deepseek-v4-pro
                          name: DeepSeek V4 Pro
                          lab: DeepSeek
                          class: open
                          open_weights: true
                          position: 120
                          succeeds: null
                        side: input
                        latest:
                          period_start: '2026-10-13'
                          value: 1.32
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/series/history:
    get:
      tags: [Reference Series]
      summary: Get reference series history
      description: |
        Returns one series' value for each period starting within the
        inclusive `start`–`end` date window, oldest first. Either bound may
        be omitted. A period's value is the most recent reading of it.
        `start` after `end` returns `400`; an unknown token series returns `404`.
        Retired `power-*` identifiers return `400` before any upstream lookup.

        Requires the `reference-series` entitlement family; requests without
        it return `404` `not_found`.
      operationId: getReferenceSeriesHistory
      parameters:
        - $ref: '#/components/parameters/SeriesId'
        - $ref: '#/components/parameters/SeriesHistoryStart'
        - $ref: '#/components/parameters/SeriesHistoryEnd'
      responses:
        '200':
          description: The series' values, oldest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReferenceSeriesHistoryResponse'
              examples:
                history:
                  value:
                    id: tokens-open-frontier-input
                    points:
                      - period_start: '2026-09-29'
                        period_end: '2026-09-30'
                        value: 1.32
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/series/offers:
    get:
      tags: [Reference Series]
      summary: Get inference providers' prices for a token model
      description: |
        Returns a token model and each inference provider's list price for
        it, in US dollars per million tokens, cheapest input first. The
        prices are those of the newest period (`period_start`, the UTC day
        the price list was read) holding any, each provider's most recent
        reading of it. Free, batch and explicitly quantized offers are
        excluded.

        A closed model is priced by its lab alone, so it lists no offers;
        nor does an open-weights model before its first reading, and then
        `period_start` is `null`. An unknown model returns `404`.

        Requires the `reference-series` entitlement family; requests without
        it return `404` `not_found`.
      operationId: getReferenceSeriesOffers
      parameters:
        - $ref: '#/components/parameters/TokenModelId'
      responses:
        '200':
          description: The model and its providers' prices.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReferenceSeriesOffersResponse'
              examples:
                offers:
                  value:
                    model:
                      id: deepseek-v4-pro
                      name: DeepSeek V4 Pro
                      lab: DeepSeek
                      class: open
                      open_weights: true
                      position: 120
                      succeeds: null
                    period_start: '2026-10-13'
                    offers:
                      - company: fireworks
                        input: 1.15
                        output: 2.4
                        cached_input: null
                      - company: deepinfra
                        input: 1.32
                        output: 2.75
                        cached_input: 0.15
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/series/correlation:
    get:
      tags: [Reference Series]
      summary: Get the correlation of two series
      description: |
        Returns the Pearson correlation of two series' daily log changes over
        the `window` days ending today (UTC). A change is counted on a date
        when both series have a value on that date and on the day before, and
        both values are positive; `overlap_days` is how many dates qualified.
        `coefficient` is `null` below 30 overlapping days, or when either
        series did not move over them.

        Either operand may be a reference series or a custom index id such as
        `h100-us`; the index's daily values are used. An index operand needs
        your key to hold that index — otherwise `404` `not_found`, as on the
        custom-index routes — and an unknown series or index is `404`.
        Retired `power-*` identifiers in either operand return `400` before
        entitlement checks or an upstream lookup.

        Requires the `reference-series` entitlement family; requests without
        it return `404` `not_found`.
      operationId: getReferenceSeriesCorrelation
      parameters:
        - $ref: '#/components/parameters/SeriesCorrelationA'
        - $ref: '#/components/parameters/SeriesCorrelationB'
        - $ref: '#/components/parameters/SeriesCorrelationWindow'
      responses:
        '200':
          description: The correlation over the window.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReferenceSeriesCorrelationResponse'
              examples:
                correlation:
                  value:
                    a: h100-us
                    b: tokens-open-frontier-input
                    window_days: 90
                    overlap_days: 88
                    coefficient: 0.31
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  # ── end series lane ──
  # ── inference lane ──
  /v1/chat/completions:
    post:
      tags: [Inference]
      summary: Create a chat completion
      description: |
        Generates a reply with the requested model, in OpenAI's chat
        completions format. The key may be sent as `Authorization: Bearer` or
        `X-API-Key`, never both.

        With `stream: true` the reply is a server-sent event stream of
        `chat.completion.chunk` objects ending with `data: [DONE]`; a
        `: keep-alive` comment arrives during long silences, such as while a
        long prompt is read. A stream that breaks off without `[DONE]` was cut
        short and should be retried. Request fields other than those listed
        are passed to the model's engine unchanged.

        Requests of one conversation stay on the same GPU node, reusing its
        prompt cache, when they share a `session_id` (or `X-Session-Id`
        header), a `prompt_cache_key`, or their first system and user messages.

        Completions are charged to your organization's API billing account:
        without an active one, while it is paused, or once today's spend
        reaches its daily limit, the answer is `402`.

        Requires the `inference` entitlement family; requests without it
        return `404` `not_found`.
      operationId: createChatCompletion
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/SessionIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatCompletionRequest'
      responses:
        '200':
          description: The completion, or with `stream` set an event stream of completion chunks.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/InferenceRequestId'
            X-Lico-Supply-Tier:
              $ref: '#/components/headers/InferenceSupplyTier'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatCompletion'
            text/event-stream:
              schema:
                type: string
              example: |
                data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1767225600,"model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

                data: [DONE]
        '400':
          description: >-
            The request is not a valid chat completion (`invalid_request`), or
            the model's engine refused it; an engine's error is passed through
            as the engine wrote it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceError'
        '401':
          $ref: '#/components/responses/InferenceUnauthorized'
        '402':
          $ref: '#/components/responses/InferencePaymentRequired'
        '404':
          description: >-
            The model is not served here (`model_not_found`), or the key's
            organization lacks the `inference` entitlement (`not_found`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceError'
              examples:
                modelNotFound:
                  value:
                    error:
                      code: model_not_found
                      message: the model "MODEL_ID" is not served here
                      type: not_found_error
                familyNotFound:
                  value:
                    error:
                      code: not_found
                      message: the inference API is not available for this key
                      type: not_found_error
        '413':
          description: The request body exceeds 16 MiB.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceError'
        '429':
          $ref: '#/components/responses/InferenceRateLimited'
        '502':
          $ref: '#/components/responses/InferenceBadGateway'
        '503':
          $ref: '#/components/responses/InferenceUnavailable'
  /v1/models:
    get:
      tags: [Inference]
      summary: List models
      description: |
        Lists the models at least one healthy GPU node is serving right now,
        in OpenAI's model list format. The key may be sent as
        `Authorization: Bearer` or `X-API-Key`, never both.

        Requires the `inference` entitlement family; requests without it
        return `404` `not_found`.
      operationId: listModels
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: The models being served.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelList'
        '401':
          $ref: '#/components/responses/InferenceUnauthorized'
        '404':
          description: The key's organization lacks the `inference` entitlement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceError'
        '429':
          $ref: '#/components/responses/InferenceRateLimited'
        '502':
          $ref: '#/components/responses/InferenceBadGateway'
        '503':
          $ref: '#/components/responses/InferenceUnavailable'
  /v1/feeds/openrouter:
    get:
      tags: [Inference]
      summary: Get the OpenRouter provider feed
      description: |
        The model feed OpenRouter reads from a provider, in OpenRouter's
        provider schema: every listed model with its context and output
        limits, quantization, prices, capacity and serving locations, and
        whether it is ready to take traffic. The key may be sent as
        `Authorization: Bearer` or `X-API-Key`, never both.

        Requires the `inference` entitlement family; requests without it
        return `404` `not_found`.
      operationId: getOpenRouterFeed
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
      responses:
        '200':
          description: The OpenRouter provider feed.
          content:
            application/json:
              schema:
                type: object
                description: OpenRouter's provider model feed, with each model document under `data`.
        '401':
          $ref: '#/components/responses/InferenceUnauthorized'
        '404':
          description: The key's organization lacks the `inference` entitlement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceError'
        '429':
          $ref: '#/components/responses/InferenceRateLimited'
        '502':
          $ref: '#/components/responses/InferenceBadGateway'
        '503':
          $ref: '#/components/responses/InferenceUnavailable'
  # ── end inference lane ──
components:
  headers:
    RequestId:
      description: Gateway-generated request correlation identifier.
      schema:
        type: string
    ScopedCacheControl:
      description: Scoped responses must not be stored by shared or private caches.
      schema:
        type: string
        enum: ['private, no-store']
    ScopedVary:
      description: The response body varies by API key.
      schema:
        type: string
        enum: [X-API-Key]
    InferenceRequestId:
      description: >-
        Request correlation identifier. On a completion it names the request's
        usage record; quote it when reporting a request.
      schema:
        type: string
    InferenceSupplyTier:
      description: >-
        Who operated the machine that served the request, and so could read
        its prompt: `operated` (machines Liquid Compute runs) or `vetted` (a
        supplier bound by signed confidentiality terms). Requests are never
        placed on unvetted supply.
      schema:
        type: string
        enum: [operated, vetted]
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: LiquidCompute API key. Use a limited-tier key in the explorer; treat API keys pasted into any web page as exposed.
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        LiquidCompute API key sent as `Authorization: Bearer`, as OpenAI SDKs
        do. Accepted on the inference routes only.
  parameters:
    SessionIdHeader:
      name: X-Session-Id
      in: header
      required: false
      description: >-
        Conversation id that keeps a conversation's requests on one GPU node. A
        `session_id` in the body takes precedence.
      schema:
        type: string
    IndexId:
      name: index_id
      in: query
      required: false
      description: Optional custom index identifier. Defaults to your key's default index; keys authorized for multiple indices with no default must pass it explicitly (400 otherwise, listing your authorized ids). Series outside your key's authorization return 404.
      schema:
        type: string
      example: h100-us
    AvailableIndexId:
      name: index_id
      in: query
      required: false
      description: Optional; narrows the listing to one series within your key's authorization (404 if outside it). Omitted, the response lists all authorized series — no defaulting applies on this route.
      schema:
        type: string
      example: h100-us
    CustomRegionGroup:
      name: region_group
      in: query
      required: false
      description: Region group.
      schema:
        type: string
        enum: [US]
      example: US
    CustomContractType:
      name: contract_type
      in: query
      required: false
      description: Contract type.
      schema:
        type: string
        enum: [combined]
      example: combined
    CustomVendorCategory:
      name: vendor_category
      in: query
      required: false
      description: Vendor category.
      schema:
        type: string
        enum: [neocloud]
      example: neocloud
    TransactionIndexType:
      name: index_type
      in: query
      required: false
      description: Transaction index variant.
      schema:
        type: string
        enum: [raw, blended_sigmoid]
        default: blended_sigmoid
    GpuFamilyOptional:
      name: gpu_family
      in: query
      required: false
      description: GPU family. Mutually exclusive with `gpu_group`.
      schema:
        type: string
        enum: [hopper, blackwell]
    GpuGroupOptional:
      name: gpu_group
      in: query
      required: false
      description: GPU-level group. Only the groups listed are served; any other value returns `400 invalid_request`. Mutually exclusive with `gpu_family`; providing both returns `400 invalid_request`.
      schema:
        type: string
        enum: [h100]
    TransactionRegionGroup:
      name: region_group
      in: query
      required: false
      description: Region group.
      schema:
        type: string
        enum: [US, EU, APAC, all]
    TransactionContractType:
      name: contract_type
      in: query
      required: false
      description: Contract type.
      schema:
        type: string
        enum: [spot, ondemand, combined]
    InfoIndexName:
      name: index_name
      in: query
      required: false
      description: Index name.
      schema:
        type: string
        enum: [hopper, blackwell]
    InfoRegionGroup:
      name: region_group
      in: query
      required: false
      description: Region group.
      schema:
        type: string
        enum: [US, EU]
    PrivateTxnGpuModel:
      name: gpu_model
      in: query
      required: false
      description: GPU model.
      schema:
        type: string
        enum: [a100, h100, h200, b200, b300]
      example: h100
    PrivateTxnContractType:
      name: contract_type
      in: query
      required: false
      description: Contract type. On this route `on-demand` is hyphenated — the transaction index's `ondemand` spelling is not accepted here.
      schema:
        type: string
        enum: [reserved, on-demand, spot]
      example: on-demand
    PrivateTxnRegion:
      name: region
      in: query
      required: false
      description: Region. Matching is exact on the stored region value — composite regions (`global+us`, `apac+us`) are distinct values, not unions, so they are not returned by queries for their components (`region=us` does not match `global+us` or `apac+us` deals). The `+` in `global+us` / `apac+us` must be URL-encoded as `%2B` — a raw `+` in a query string decodes to a space and is rejected.
      schema:
        type: string
        enum: [us, eu, apac, global, global+us, apac+us]
      example: global+us
    PrivateTxnStartDate:
      name: start
      in: query
      required: false
      description: Inclusive lower bound on the transaction `date`, in `YYYY-MM-DD` format. Must not be after `end` when both are provided — an inverted window returns `400`.
      schema:
        type: string
        format: date
      example: '2026-01-01'
    PrivateTxnEndDate:
      name: end
      in: query
      required: false
      description: Inclusive upper bound on the transaction `date`, in `YYYY-MM-DD` format. Must not be before `start` when both are provided — an inverted window returns `400`.
      schema:
        type: string
        format: date
      example: '2026-06-30'
    # ── history lane: GPU price history and change-log parameters ──
    GPUPriceModel:
      name: gpu_model
      in: query
      required: true
      description: GPU model.
      schema:
        type: string
        enum: [a100, h100, h200, b200, b300]
      example: h100
    GPUPriceHistoryStart:
      name: start
      in: query
      required: true
      description: First UTC day of the window, `YYYY-MM-DD`, inclusive. Must not be after `end`.
      schema:
        type: string
        format: date
      example: '2026-07-01'
    GPUPriceHistoryEnd:
      name: end
      in: query
      required: true
      description: Last UTC day of the window, `YYYY-MM-DD`, inclusive. The window spans at most 92 days; days after the current UTC date are not emitted.
      schema:
        type: string
        format: date
      example: '2026-09-30'
    GPUPriceChangesSince:
      name: since
      in: query
      required: true
      description: First UTC day of the change log, `YYYY-MM-DD`, at most 91 days before the current UTC date.
      schema:
        type: string
        format: date
      example: '2026-07-01'
    # ── end history lane ──
    # ── series lane: reference series ──
    SeriesId:
      name: id
      in: query
      required: true
      description: Reference series id, as listed by `/v1/series`.
      schema:
        type: string
        pattern: '^tokens(-[a-z0-9]+)+$'
        maxLength: 64
      example: tokens-open-frontier-input
    SeriesHistoryStart:
      name: start
      in: query
      required: false
      description: Inclusive lower bound on `period_start`, `YYYY-MM-DD`. Must not be after `end`.
      schema:
        type: string
        format: date
      example: '2026-07-01'
    SeriesHistoryEnd:
      name: end
      in: query
      required: false
      description: Inclusive upper bound on `period_start`, `YYYY-MM-DD`. Must not be before `start`.
      schema:
        type: string
        format: date
      example: '2026-09-30'
    TokenModelId:
      name: model
      in: query
      required: true
      description: Token model id, as named by a per-model series' `model.id` in `/v1/series`.
      schema:
        type: string
        pattern: '^[a-z0-9]+(-[a-z0-9]+)*$'
        maxLength: 48
      example: deepseek-v4-pro
    SeriesCorrelationA:
      name: a
      in: query
      required: true
      description: First operand, a token series id or a custom index id your key holds; retired power-* ids are invalid.
      schema:
        type: string
        pattern: '^[a-z0-9][a-z0-9._-]{0,63}$'
        not:
          pattern: '^power-'
      example: h100-us
    SeriesCorrelationB:
      name: b
      in: query
      required: true
      description: Second operand, a token series id or a custom index id your key holds; retired power-* ids are invalid.
      schema:
        type: string
        pattern: '^[a-z0-9][a-z0-9._-]{0,63}$'
        not:
          pattern: '^power-'
      example: tokens-open-frontier-input
    SeriesCorrelationWindow:
      name: window
      in: query
      required: false
      description: Window length in days, ending today (UTC).
      schema:
        type: integer
        minimum: 30
        maximum: 365
        default: 90
      example: 90
    # ── end series lane ──
  responses:
    BadRequest:
      description: Invalid or unsupported query parameter.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalidRequest:
              value:
                error:
                  code: invalid_request
                  message: 'Invalid value for parameter: resolution'

    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            unauthorized:
              value:
                error:
                  code: unauthorized
                  message: A valid X-API-Key header is required
    ServiceUnavailable:
      description: API-key validation is temporarily unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            unavailable:
              value:
                error:
                  code: service_unavailable
                  message: Authentication service unavailable
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: string
        X-RateLimit-Limit:
          description: Your bucket size (burst capacity).
          schema:
            type: string
        X-RateLimit-Remaining:
          description: Tokens currently available in your bucket.
          schema:
            type: string
        X-RateLimit-Reset:
          description: Unix timestamp of the estimated next-token time.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: Unknown endpoint, unavailable resource, or an index or index family outside your authorization.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            notFound:
              value:
                error:
                  code: not_found
                  message: Endpoint not found
            familyNotFound:
              value:
                error:
                  code: not_found
                  message: index family "txn-index" is not available for this key
    BadGateway:
      description: Upstream service or gateway configuration failure.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InferenceUnauthorized:
      description: Missing or invalid API key, or both key headers sent.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InferenceError'
          examples:
            unauthorized:
              value:
                error:
                  code: unauthorized
                  message: 'A valid API key is required in the Authorization: Bearer or X-API-Key header'
                  type: authentication_error
    InferencePaymentRequired:
      description: >-
        Your organization has no active API billing account
        (`billing_required`): add a card under Settings, API billing. Its
        completions are paused until a failed daily charge is paid
        (`billing_paused`). Or its spend today (UTC) reached its daily limit
        (`daily_limit_reached`): an admin can raise the limit, or retry after
        midnight UTC.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InferenceError'
          examples:
            billingRequired:
              value:
                error:
                  code: billing_required
                  message: add a card under Settings, API billing to use the inference API
                  type: billing_error
            billingPaused:
              value:
                error:
                  code: billing_paused
                  message: inference is paused until your failed charge is paid
                  type: billing_error
            dailyLimitReached:
              value:
                error:
                  code: daily_limit_reached
                  message: your organization reached its daily inference spend limit
                  type: billing_error
    InferenceRateLimited:
      description: >-
        Your key's rate limit was exceeded (`rate_limited`), your key already
        has as many completions in progress as its tier allows
        (`concurrency_limited`), or every GPU node serving the model is at
        capacity (`capacity_exhausted`); retry after `Retry-After`.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: string
        X-RateLimit-Limit:
          description: Your bucket size (burst capacity); absent on `capacity_exhausted`.
          schema:
            type: string
        X-RateLimit-Remaining:
          description: Tokens currently available in your bucket.
          schema:
            type: string
        X-RateLimit-Reset:
          description: Unix timestamp of the estimated next-token time.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InferenceError'
    InferenceUnavailable:
      description: >-
        No GPU node is serving the model right now (`no_healthy_node`), the
        model router is still loading its catalogue (`warming_up`, with
        `Retry-After`), or API-key validation is temporarily unavailable
        (`service_unavailable`).
      headers:
        Retry-After:
          description: Seconds to wait before retrying; present for `warming_up`.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InferenceError'
    InferenceBadGateway:
      description: The model router could not be reached or answered outside its contract (`upstream_error`); retry shortly.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InferenceError'
  schemas:
    CommitmentCurve:
      type: object
      additionalProperties: false
      required:
        [
          index_id,
          methodology_version,
          units,
          as_of,
          calculated_at,
          window_start,
          window_end,
          eligible_regions,
          oldest_date,
          newest_date,
          deal_count,
          bucket_count,
          distinct_tenors,
          gpu_count,
          excluded_invalid,
          state,
          reason,
          sparse,
          buckets,
          fit,
        ]
      properties:
        index_id: { type: string }
        methodology_version: { type: string, enum: [commitment-v1] }
        units: { type: string, enum: [usd_per_gpu_per_hr] }
        as_of: { type: string, format: date }
        calculated_at: { type: string, format: date-time }
        window_start: { type: string, format: date }
        window_end: { type: string, format: date }
        eligible_regions:
          type: array
          items: { type: string, enum: [us, global+us, apac+us] }
        oldest_date: { type: [string, 'null'], format: date }
        newest_date: { type: [string, 'null'], format: date }
        deal_count: { type: integer, minimum: 0 }
        bucket_count: { type: integer, minimum: 0 }
        distinct_tenors: { type: integer, minimum: 0 }
        gpu_count: { type: integer, minimum: 0 }
        excluded_invalid: { type: integer, minimum: 0 }
        state: { type: string, enum: [fitted, insufficient_data, invalid_fit] }
        reason: { type: string, enum: ['', fewer_than_three_tenors, degenerate_or_nonpositive_fit] }
        sparse: { type: boolean }
        buckets:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              [
                term_days_min,
                term_days_max,
                midpoint_days,
                deal_count,
                gpu_count,
                average,
                low,
                high,
                fitted,
              ]
            properties:
              term_days_min: { type: integer, minimum: 1 }
              term_days_max: { type: integer, minimum: 1 }
              midpoint_days: { type: number, exclusiveMinimum: 0 }
              deal_count: { type: integer, minimum: 1 }
              gpu_count: { type: integer, minimum: 1 }
              average: { type: number, exclusiveMinimum: 0 }
              low: { type: number, exclusiveMinimum: 0 }
              high: { type: number, exclusiveMinimum: 0 }
              fitted: { type: [number, 'null'], exclusiveMinimum: 0 }
        fit:
          type: [object, 'null']
          additionalProperties: false
          required: [a, b, min_days, max_days]
          properties:
            a: { type: number }
            b: { type: number }
            min_days: { type: number, exclusiveMinimum: 0 }
            max_days: { type: number, exclusiveMinimum: 0 }

    HealthResponse:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [ok]
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
            message:
              type: string
    InferenceError:
      type: object
      description: >-
        OpenAI's error object. Errors from the gateway and the model router
        carry a string `code` such as `model_not_found`; an error from a
        model's engine is passed through as the engine wrote it.
      required: [error]
      properties:
        error:
          type: object
          required: [message, type]
          properties:
            code:
              type: [string, integer, 'null']
            message:
              type: string
            type:
              type: string
              description: OpenAI's error class, such as `invalid_request_error` or `rate_limit_error`.
    ChatCompletionRequest:
      type: object
      description: >-
        An OpenAI chat completions request. Fields not listed here, such as
        `max_tokens`, `temperature` or `tools`, are passed to the model's engine
        unchanged.
      required: [model, messages]
      properties:
        model:
          type: string
          description: A model id from `GET /v1/models`.
        messages:
          type: array
          minItems: 1
          items:
            type: object
            required: [role]
            properties:
              role:
                type: string
              content:
                description: The message text, or an array of content parts.
        stream:
          type: boolean
          default: false
        stream_options:
          type: object
          properties:
            include_usage:
              type: boolean
              description: Send a final chunk with the request's token usage.
        session_id:
          type: string
          description: Conversation id; requests sharing one stay on one GPU node.
        prompt_cache_key:
          type: string
          description: Keeps requests on one GPU node when no session id is sent.
    ChatCompletion:
      type: object
      description: An OpenAI chat completion as the model's engine produced it.
      required: [id, object, model, choices]
      properties:
        id:
          type: string
        object:
          type: string
          enum: [chat.completion]
        created:
          type: integer
        model:
          type: string
        choices:
          type: array
          items:
            type: object
        usage:
          type: object
          properties:
            prompt_tokens:
              type: integer
            completion_tokens:
              type: integer
            total_tokens:
              type: integer
    ModelList:
      type: object
      required: [object, data]
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            $ref: '#/components/schemas/Model'
    Model:
      type: object
      required: [id, object, created, owned_by]
      properties:
        id:
          type: string
        object:
          type: string
          enum: [model]
        created:
          type: integer
        owned_by:
          type: string
        context_length:
          type: integer
          minimum: 1
          description: The longest prompt plus completion the model accepts, in tokens.
        max_output_tokens:
          type: integer
          minimum: 1
          description: The longest completion the model returns, in tokens.
        pricing:
          type: object
          description: The model's current prices in USD per token, as decimal strings.
          properties:
            prompt:
              $ref: '#/components/schemas/TokenPrice'
            completion:
              $ref: '#/components/schemas/TokenPrice'
            input_cache_read:
              $ref: '#/components/schemas/TokenPrice'
    TokenPrice:
      type: string
      pattern: '^[0-9]+(\.[0-9]+)?$'
      description: USD per token, as a decimal string such as `0.00000015`.
    CustomIndexPoint:
      type: object
      required:
        - timestamp
        - index_id
        - index_name
        - gpu_family
        - gpu_model
        - region_group
        - contract_type
        - vendor_category
        - index_value
        - blended_value
        - index_units
        - p5
        - p95
      properties:
        timestamp:
          type: string
          description: UTC interval start.
          example: '2026-06-03T13:00:00+00:00'
        index_id:
          type: string
          example: h100-us
        index_name:
          type: string
          example: h100
        gpu_family:
          type: string
          example: hopper
        gpu_model:
          type: string
          example: h100
        region_group:
          type: string
          example: US
        contract_type:
          type: string
          example: combined
        vendor_category:
          type: string
          example: neocloud
        index_value:
          type: number
          example: 2.5795
        blended_value:
          type: number
          example: 2.532
        index_units:
          type: string
          example: usd_per_gpu_per_hr
        p5:
          type: number
          example: 1.5328
        p95:
          type: number
          example: 4.1658
    CustomIndexPointResponse:
      type: object
      required: [data, total_count, filters]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/CustomIndexPoint'
        total_count:
          type: integer
          example: 1
        filters:
          type: object
          additionalProperties: true
    CustomIndexHistoryResponse:
      allOf:
        - $ref: '#/components/schemas/CustomIndexPointResponse'
        - type: object
          properties:
            index_name:
              # The series' display name from the live catalog; null when the
              # catalog carries no name for the requested id.
              type:
                - string
                - 'null'
            resolution:
              type: string
              example: 30m
            returned_count:
              type: integer
            limit:
              type: integer
            offset:
              type: integer
            has_more:
              type: boolean
            next_offset:
              type:
                - integer
                - 'null'
    CustomIndexReferenceResponse:
      type: object
      required: [index_ids, index_units, endpoints]
      properties:
        index_ids:
          type: array
          description: The custom index identifiers your key is authorized to read.
          items:
            type: string
        default_index_id:
          type: string
          description: The index used when index_id is omitted.
        index_units:
          type: string
          example: usd_per_gpu_per_hr
        endpoints:
          type: array
          items:
            type: object
            required: [path, summary, parameters]
            properties:
              path:
                type: string
              summary:
                type: string
              description:
                type: string
              parameters:
                type: array
                items:
                  type: object
                  additionalProperties: true
    CustomIndexAvailableResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        total:
          type: integer
    TransactionIndexPoint:
      type: object
      required: [date, index_value, p5, p95, txn_count]
      properties:
        date:
          type: string
          format: date
          example: '2026-06-01'
        gpu_family:
          type: string
          example: hopper
        gpu_group:
          # Null when the row is a family series rather than a gpu_group series.
          type:
            - string
            - 'null'
        region_group:
          type: string
          example: US
        contract_type:
          type: string
          example: combined
        index_type:
          type: string
          example: blended_sigmoid
        index_value:
          type: number
          example: 2.4857
        p5:
          description: 5th percentile; null on blended_sigmoid rows, which publish no band.
          type:
            - number
            - 'null'
          example: 1.5256
        p95:
          description: 95th percentile; null on blended_sigmoid rows, which publish no band.
          type:
            - number
            - 'null'
          example: 4.3432
        txn_count:
          description: Transactions behind the value; null on blended_sigmoid rows.
          type:
            - integer
            - 'null'
          example: 15
    TransactionIndexCurrentResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TransactionIndexPoint'
        total_count:
          type: integer
        filters:
          type: object
          additionalProperties: true
    TransactionIndexHistoryResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TransactionIndexPoint'
        total_days:
          type: integer
        gpu_family:
          # Null when the requested gpu_group has no family mapping — the
          # upstream echoes gpu_family null rather than guessing, and the
          # gateway forwards it verbatim.
          type:
            - string
            - 'null'
        gpu_group:
          # Present-but-null when no gpu_group was requested; the upstream
          # envelope carries the key and the gateway forwards it verbatim.
          type:
            - string
            - 'null'
        index_type:
          type: string
        filters:
          type: object
          additionalProperties: true
    InformationalIndexPoint:
      type: object
      required:
        - date
        - index_name
        - region_group
        - contract_type
        - vendor_category
        - index_value
        - index_units
        - p5
        - p95
      properties:
        date:
          type: string
          format: date
          example: '2026-06-03'
        index_name:
          type: string
          example: hopper
        region_group:
          type: string
          example: US
        contract_type:
          type: string
          example: combined
        vendor_category:
          type: string
          example: neocloud
        index_value:
          type: number
          example: 2.6476
        index_units:
          type: string
          example: usd_per_gpu_per_hr
        p5:
          type: number
          example: 1.5804
        p95:
          type: number
          example: 4.1719
    InformationalIndexResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/InformationalIndexPoint'
        total_count:
          type: integer
        filters:
          type: object
          additionalProperties: true
    InformationalIndexHistoryResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/InformationalIndexPoint'
        total_days:
          type: integer
        index_name:
          # Echo of the requested index_name; null when absent.
          type:
            - string
            - 'null'
        filters:
          type: object
          additionalProperties: true
    InformationalIndexAvailableResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        total:
          type: integer
    GPUPriceSource:
      type: object
      description: Customer-safe outcome for one vendor in the selected scrape.
      required: [source, outcome, rows]
      properties:
        source:
          type: string
          minLength: 1
          example: lambda
        outcome:
          type: string
          enum: [parsed, skipped, failed, not_attempted]
        rows:
          type: integer
          minimum: 0
          description: >-
            Ledger rows this source persisted in the served scrape, every
            status counted. Not the number of comparable prices in `data`; a
            `parsed` source can report 0 when its rows were refused.
    GPUPriceRun:
      type: object
      description: The completed scrape shared by every observation in the response.
      required:
        - run_id
        - started_at
        - finished_at
        - status
        - sources_attempted
        - sources_parsed
        - sources_skipped
        - sources_failed
        - sources_not_attempted
        - observations_written
        - sources
      properties:
        run_id:
          type: string
          pattern: '^[0-9A-Za-z._-]{1,64}$'
        started_at:
          type: string
          format: date-time
        finished_at:
          type: string
          format: date-time
        status:
          type: string
          enum: [succeeded, partial]
        sources_attempted:
          type: integer
          minimum: 0
        sources_parsed:
          type: integer
          minimum: 0
        sources_skipped:
          type: integer
          minimum: 0
          description: Sources whose pricing page refused the fetch (robots.txt); attempted = parsed + skipped + failed.
        sources_failed:
          type: integer
          minimum: 0
        sources_not_attempted:
          type: integer
          minimum: 0
        observations_written:
          type: integer
          minimum: 0
          description: All ledger rows written, including rows that are not comparable prices.
        sources:
          type: array
          items:
            $ref: '#/components/schemas/GPUPriceSource'
    GPUPriceObservation:
      type: object
      description: >-
        One vendor offer normalized to USD per GPU-hour. Raw labels remain
        beside normalized values so clients can display what the vendor
        actually published. Nullable region fields mean the vendor did not
        publish a region; they are not inferred from company location.
      required:
        - provider
        - provider_category
        - supply_model
        - gpu_model
        - gpu_model_raw
        - provider_sku
        - vram_gb
        - accelerator_count
        - minimum_quantity
        - price_kind
        - commitment_term
        - observed_amount
        - observed_literal
        - price_qualifier
        - observed_unit
        - usd_per_gpu_hour
        - currency
        - region_raw
        - region_canonical
        - configuration
        - availability
        - price_basis
        - source_url
        - observed_at
      properties:
        provider:
          type: string
          minLength: 1
          example: lambda
        provider_category:
          type: string
          enum: [neocloud, general_purpose, hyperscaler, mixed]
        supply_model:
          type: string
          enum: [direct, marketplace, aggregator]
        gpu_model:
          type: string
          enum: [a100, h100, h200, b200, b300]
          example: h100
        gpu_model_raw:
          type: string
          minLength: 1
          description: GPU label exactly as published by the vendor.
        provider_sku:
          type: string
          minLength: 1
        vram_gb:
          type:
            - integer
            - 'null'
          minimum: 1
        accelerator_count:
          type:
            - integer
            - 'null'
          minimum: 1
          description: GPU count stated by the vendor; null when not published.
        minimum_quantity:
          type:
            - integer
            - 'null'
          minimum: 1
          description: Minimum volume required for the rate; not an instance GPU count.
        price_kind:
          type: string
          enum: [on_demand, reserved, spot]
        commitment_term:
          type:
            - string
            - 'null'
        observed_amount:
          type:
            - number
            - 'null'
          minimum: 0
          description: Published numeric amount before any per-GPU derivation; null for a discount-derived price.
        observed_literal:
          type: string
          minLength: 1
          description: Price or discount text exactly as published by the vendor.
        price_qualifier:
          type:
            - string
            - 'null'
          description: Vendor qualifier such as "from" or "starting at".
        observed_unit:
          type:
            - string
            - 'null'
          enum: [usd_per_gpu_hour, usd_per_instance_hour, usd_per_rack_hour, null]
        usd_per_gpu_hour:
          type: number
          minimum: 0
          description: Comparable normalized rate in USD per GPU-hour.
        currency:
          type:
            - string
            - 'null'
          enum: [USD, null]
        region_raw:
          type:
            - string
            - 'null'
          description: Region text exactly as published; null when the vendor publishes none.
        region_canonical:
          type:
            - string
            - 'null'
          description: Normalized form of region_raw; null when no source-backed normalization exists.
        configuration:
          type:
            - string
            - 'null'
          description: Vendor configuration qualifiers that distinguish otherwise similar offers.
        availability:
          type: string
          enum: [purchasable, waitlist, quote_only]
        price_basis:
          type:
            - string
            - 'null'
          enum: [observed, derived_from_published_discount, null]
          description: >-
            Whether the rate was printed or derived from a vendor-published
            discount; null when the source does not state a basis.
        source_url:
          type: string
          format: uri
        observed_at:
          type: string
          format: date-time
    GPUPricesResponse:
      type: object
      required: [data, total_count, run]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/GPUPriceObservation'
        total_count:
          type: integer
          minimum: 0
          description: Number of comparable observations in data.
        run:
          $ref: '#/components/schemas/GPUPriceRun'
    # ── history lane: GPU price history, change log and freshness schemas ──
    GPUPriceOffer:
      type: object
      description: >-
        One vendor offer, identified by the fields the vendor publishes; no
        two offers of one GPU model share all of them. Field meanings match
        `/v1/gpu-prices/current`. `region` is the normalized region where one
        exists, else the region text as published, and null when the vendor
        publishes none.
      required:
        - provider
        - provider_name
        - price_kind
        - commitment_term
        - region
        - supply_model
        - provider_sku
        - vram_gb
        - accelerator_count
        - minimum_quantity
        - configuration
      properties:
        provider:
          type: string
          minLength: 1
          example: nebius
        provider_name:
          type: string
          minLength: 1
          description: The vendor's display name.
          example: Nebius
        price_kind:
          type: string
          enum: [on_demand, reserved, spot]
        commitment_term:
          type:
            - string
            - 'null'
        region:
          type:
            - string
            - 'null'
        supply_model:
          type: string
          enum: [direct, marketplace, aggregator]
        provider_sku:
          type: string
          minLength: 1
        vram_gb:
          type:
            - integer
            - 'null'
          minimum: 1
        accelerator_count:
          type:
            - integer
            - 'null'
          minimum: 1
        minimum_quantity:
          type:
            - integer
            - 'null'
          minimum: 1
        configuration:
          type:
            - string
            - 'null'
    GPUPriceHistoryPoint:
      type: object
      required: [date, usd_per_gpu_hour]
      properties:
        date:
          type: string
          format: date
        usd_per_gpu_hour:
          type: number
          minimum: 0
    GPUPriceHistoryOffer:
      allOf:
        - $ref: '#/components/schemas/GPUPriceOffer'
        - type: object
          required: [points]
          properties:
            points:
              type: array
              minItems: 1
              description: One point per listed day, in date order.
              items:
                $ref: '#/components/schemas/GPUPriceHistoryPoint'
    GPUPriceHistoryResponse:
      type: object
      required: [gpu_model, start, end, offers]
      properties:
        gpu_model:
          type: string
          enum: [a100, h100, h200, b200, b300]
        start:
          type: string
          format: date
        end:
          type: string
          format: date
        offers:
          type: array
          items:
            $ref: '#/components/schemas/GPUPriceHistoryOffer'
    GPUPriceChange:
      allOf:
        - $ref: '#/components/schemas/GPUPriceOffer'
        - type: object
          required: [observed_at, change, previous_usd_per_gpu_hour, usd_per_gpu_hour]
          properties:
            observed_at:
              type: string
              format: date-time
              description: Start of the scrape that revealed the change.
            change:
              type: string
              enum: [price_changed, listed, delisted]
            previous_usd_per_gpu_hour:
              type:
                - number
                - 'null'
              minimum: 0
              description: Price before the change; null for `listed`.
            usd_per_gpu_hour:
              type:
                - number
                - 'null'
              minimum: 0
              description: Price after the change; null for `delisted`.
    GPUPriceChangesResponse:
      type: object
      required: [gpu_model, since, changes]
      properties:
        gpu_model:
          type: string
          enum: [a100, h100, h200, b200, b300]
        since:
          type: string
          format: date
        changes:
          type: array
          items:
            $ref: '#/components/schemas/GPUPriceChange'
    DatasetFreshness:
      type:
        - object
        - 'null'
      description: A dataset's last successful update and when the next is due; null when it has never updated.
      required: [last_success_at, next_expected_at]
      properties:
        last_success_at:
          type: string
          format: date-time
        next_expected_at:
          type: string
          format: date-time
    FreshnessResponse:
      type: object
      required: [custom_index, gpu_prices, series]
      properties:
        custom_index:
          $ref: '#/components/schemas/DatasetFreshness'
        gpu_prices:
          $ref: '#/components/schemas/DatasetFreshness'
        series:
          $ref: '#/components/schemas/DatasetFreshness'
    # ── end history lane ──
    PrivateTransaction:
      type: object
      description: >-
        One private GPU compute transaction. Terms are day counts
        canonicalized per unit (week = 7, month = 30, year = 365 — so "12
        months" is 360 days while "2 years" is 730); ranged terms carry
        distinct min and max. term_days_min/term_days_max are null for
        on-demand and spot deals. discount_pct is the percentage off list
        price and is null when the discount was not shown for the deal
        (null is not 0).
      required:
        - gpu_model
        - contract_type
        - term_days_min
        - term_days_max
        - price_usd_gpu_hr
        - discount_pct
        - gpu_count
        - region
        - date
        - is_rolling
      properties:
        gpu_model:
          type: string
          enum: [a100, h100, h200, b200, b300]
          example: h100
        contract_type:
          type: string
          enum: [reserved, on-demand, spot]
          example: reserved
        term_days_min:
          description: Minimum contracted term in days; null for on-demand and spot deals.
          type:
            - integer
            - 'null'
          example: 360
        term_days_max:
          description: Maximum contracted term in days; equals term_days_min for single-term deals, null for on-demand and spot deals.
          type:
            - integer
            - 'null'
          example: 360
        price_usd_gpu_hr:
          type: number
          description: Volume-weighted average price in USD per GPU-hour.
          example: 1.8
        discount_pct:
          description: Percentage off list price; null when the discount was not shown for the deal (null is not 0).
          type:
            - integer
            - 'null'
          minimum: 0
          maximum: 100
          example: 69
        gpu_count:
          type: integer
          minimum: 1
          example: 64
        region:
          type: string
          enum: [us, eu, apac, global, global+us, apac+us]
          example: us
        date:
          type: string
          format: date
          example: '2026-07-01'
        is_rolling:
          type: boolean
          description: True when the deal rolls (auto-renews) from the stated date.
          example: false
    PrivateTransactionsResponse:
      type: object
      required: [data, total_count, filters]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PrivateTransaction'
        total_count:
          type: integer
          example: 1
        filters:
          type: object
          description: Echoes only the query parameters present on the request.
          properties:
            gpu_model:
              type: string
            contract_type:
              type: string
            region:
              type: string
            start:
              type: string
            end:
              type: string
    # ── series lane: reference series ──
    ReferenceSeries:
      type: object
      additionalProperties: false
      required: [id, name, kind, unit, currency, source, attribution, model, side, latest]
      properties:
        id:
          type: string
          pattern: '^tokens(-[a-z0-9]+)+$'
          example: tokens-open-frontier-input
        name:
          type: string
          example: Open-weights frontier models, input tokens
        kind:
          type: string
          enum: [tokens]
        unit:
          type: string
          enum: [USD/Mtok]
          description: Price unit; `USD/Mtok` is US dollars per million tokens.
        currency:
          type: string
          pattern: '^[A-Z]{3}$'
          example: USD
        source:
          type: string
          example: LiteLLM
        attribution:
          type: string
          description: Text to show beside any value from this series.
          example: 'Source: LiteLLM'
        model:
          description: The token model a per-model series prices; null on every other series.
          oneOf:
            - $ref: '#/components/schemas/TokenModel'
            - type: 'null'
        side:
          description: Which tokens a per-model series prices; null on every other series.
          type: [string, 'null']
          enum: [input, output, null]
        latest:
          description: The most recent period's value; null until the series has one.
          type: [object, 'null']
          additionalProperties: false
          required: [period_start, value]
          properties:
            period_start:
              type: string
              format: date
            value:
              type: number
    TokenModel:
      type: object
      additionalProperties: false
      required: [id, name, lab, class, open_weights, position, succeeds]
      properties:
        id:
          type: string
          pattern: '^[a-z0-9]+(-[a-z0-9]+)*$'
          maxLength: 48
          example: deepseek-v4-pro
        name:
          type: string
          example: DeepSeek V4 Pro
        lab:
          type: string
          description: The lab that publishes the model.
          example: DeepSeek
        class:
          type: string
          enum: [premium, flagship, mid, small, open]
          description: |
            Price class. A closed model's class is its place in its lab's own
            lineup, priced at the lab's list price; `open` is an open-weights
            model, priced as the median across inference providers.
        open_weights:
          type: boolean
        position:
          type: integer
          description: Display order across all token models, ascending.
        succeeds:
          description: |
            Id of the model this one replaced in the same lab and price class;
            null when it replaced none. A model that no other model succeeds is
            current: the ladder shows current models, and a class's price
            history follows each one back along this chain.
          type: [string, 'null']
          pattern: '^[a-z0-9]+(-[a-z0-9]+)*$'
          maxLength: 48
          example: claude-opus-5
    ReferenceSeriesCatalogResponse:
      type: object
      additionalProperties: false
      required: [series]
      properties:
        series:
          type: array
          items:
            $ref: '#/components/schemas/ReferenceSeries'
    ReferenceSeriesPoint:
      type: object
      additionalProperties: false
      required: [period_start, period_end, value]
      properties:
        period_start:
          type: string
          format: date
        period_end:
          type: string
          format: date
          description: Exclusive end of the period.
        value:
          type: number
          description: The period's value in the token series' unit.
    ReferenceSeriesHistoryResponse:
      type: object
      additionalProperties: false
      required: [id, points]
      properties:
        id:
          type: string
          pattern: '^tokens(-[a-z0-9]+)+$'
        points:
          type: array
          items:
            $ref: '#/components/schemas/ReferenceSeriesPoint'
    TokenOffer:
      type: object
      additionalProperties: false
      required: [company, input, output, cached_input]
      properties:
        company:
          type: string
          pattern: '^[a-z0-9]+(-[a-z0-9]+)*$'
          maxLength: 48
          description: The inference provider.
          example: deepinfra
        input:
          type: number
          exclusiveMinimum: 0
          description: List price per million input tokens, USD.
        output:
          type: number
          exclusiveMinimum: 0
          description: List price per million output tokens, USD.
        cached_input:
          type: [number, 'null']
          minimum: 0
          description: List price per million cached input tokens, USD; null when the provider lists none.
    ReferenceSeriesOffersResponse:
      type: object
      additionalProperties: false
      required: [model, period_start, offers]
      properties:
        model:
          $ref: '#/components/schemas/TokenModel'
        period_start:
          description: The UTC day the prices were read; null when there are no offers.
          type: [string, 'null']
          format: date
        offers:
          type: array
          items:
            $ref: '#/components/schemas/TokenOffer'
    ReferenceSeriesCorrelationResponse:
      type: object
      additionalProperties: false
      required: [a, b, window_days, overlap_days, coefficient]
      properties:
        a:
          type: string
        b:
          type: string
        window_days:
          type: integer
          minimum: 30
          maximum: 365
        overlap_days:
          type: integer
          minimum: 0
          description: Dates in the window on which both series had a daily log change.
        coefficient:
          type: [number, 'null']
          minimum: -1
          maximum: 1
          description: Pearson correlation of daily log changes; null below 30 overlapping days or when either series did not move.
    # ── end series lane ──
