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

# Hyperliquid hourly market panel: open interest, funding, volume

> GET /v1/markets/hourly returns one row per Hyperliquid market and hour: open interest, mark, oracle, funding, OHLC, volume, taker flow and liquidations.

One row per market and UTC hour, ready to use: open interest, mark and oracle price at the end of the hour, the funding rate charged for the hour, open, high, low and close, volume in coins and USD, taker buy and sell USD, liquidation USD and trade count. In SQL the same rows are `hl.market_hourly`.

```bash theme={null}
# BTC, last 7 days, one row per hour
curl "https://api.ironflow.sh/v1/markets/hourly?market=BTC-PERP&from=-7d" \
  -H "Authorization: Bearer $IRONFLOW_KEY"

# Every market for one day, page by page
curl "https://api.ironflow.sh/v1/markets/hourly?from=2026-11-13&to=2026-11-14&limit=1000" \
  -H "Authorization: Bearer $IRONFLOW_KEY"
```

**History by plan:**

| Plan | History |
| - | - |
| No key | 24 hours |
| Free key, or one call paid with x402 | 14 days |
| Explorer | 90 days |
| Builder | 1 year |
| Enterprise | Everything since May 2025 |

A `from` before your history is moved up, with a `notice`. A range that ends before it answers `HISTORY_OUT_OF_RANGE` with `allowed_from`, the earliest `from` that works.

**Paging:** without `market` you get every market, at most one day of hours per page. Pass `pagination.next_cursor` as `cursor` while `has_more` is true; a page can hold fewer than `limit` rows and still have more after it.

**Values:**

* `funding_rate` is the rate charged for the hour, settled when the hour ends. Where the settlement is missing, it is the last rate seen during the hour.
* `open_interest` is in the base asset; `open_interest_usd` is it times `mark_price`.
* Null means no value: `open` to `close` in an hour without trades; open interest, mark, oracle and funding on spot and outcome markets.
* Only complete hours are served. `complete_until` is the end of the newest one; an hour appears a few minutes after it ends.


## OpenAPI

````yaml GET /v1/markets/hourly
openapi: 3.1.0
info:
  title: Ironflow API
  version: 1.0.0
  description: >
    Hyperliquid market data from Ironflow's own nodes: tick-level fills and

    trades, candles, funding rates, liquidations, mark prices, open interest,

    markets and wallet analytics over REST, live Server-Sent Events streams and

    read-only SQL. One schema across native HL perps, HIP-3 builder markets,

    HIP-4 outcome contracts and HL spot.


    The same data is a hosted MCP server for AI agents at

    `https://mcp.ironflow.sh/mcp`, and agents with a USDC wallet on Base can pay

    per call with x402, no key: the data GET endpoints are also served at

    `/x402/v1/...` (discovery: `https://api.ironflow.sh/openapi.json` and

    `https://api.ironflow.sh/.well-known/x402`).


    **Base URL:** `https://api.ironflow.sh`


    **Authentication:** Read endpoints work without a key (keyless access) or

    with a Bearer token in the `Authorization` header. Writes always need a key.

    ```

    Authorization: Bearer if_your_api_key

    ```

    Get a free key at https://ironflow.sh/key.


    **Access levels:**


    | Access | Requests per minute | Credits | History | Max range per request |

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

    | No key | 30 per IP | 100 a day per IP (300 per app) | 24 hours | 1 hour |

    | Free key | 60 | 1,000 a day, 3,000 a month | 14 days | 6 hours |

    | Explorer | 100 | 25,000 a day | 90 days | 24 hours |

    | Builder | 1,000 | 250,000 a day | 1 year | 7 days |

    | Enterprise | 10,000 | no cap | everything stored | unlimited |


    No key, free keys, Explorer and calls paid with x402 are for personal and

    non-commercial use; Builder and Enterprise include commercial use (Terms
    15.2,

    https://ironflow.sh/terms).


    **Credits:** every call costs credits by what it does. A read (trades,

    fills, liquidations, candles, funding, open interest, prices, markets) costs

    1, analytics (the liquidation map, summary and heatmap, positions at a past

    time, wallet analytics, top traders, open interest changes, the hourly
    market

    panel) 5, SQL (`/v1/query`, also as `/v1/run_query` and `/v1/sql`, and the

    export) 10, and a live stream 1 to open and 1 a minute while it is open.

    Status, getting a key, plans and payment and the SQL schema cost nothing,
    and

    so does a call paid with x402. Any other endpoint costs 1. The full table:

    https://docs.ironflow.sh/concepts/rate-limits#credits


    Without a market filter, high-volume endpoints allow at most 4 hours per
    request.

    `/v1/trades` filtered to one perp or spot market allows 24 hours per request

    without a key and on a free key.

    Each caller can also read a set number of rows per hour: 2 billion without a
    key,

    10 billion on a free key, 100 billion on Explorer, 500 billion on Builder,
    none set

    on Enterprise. REST and SQL count separately. Over it, REST answers 429

    `READ_BUDGET_EXCEEDED` until the next hour.

    A header that is present but malformed returns 401; keyless access applies
    only

    when no `Authorization` header is sent. The keyless 100 credits a day are a

    rolling 24 hours per IP: past them, `KEYLESS_DAILY_LIMIT` (429, or 402 with
    x402

    payment terms) carries a `Retry-After` of the seconds until the next call
    fits.

    An app that names itself in its `User-Agent` (not an HTTP library, a browser

    or curl) also shares 300 keyless credits per rolling 24 hours across all its
    IPs,

    with the same error past them; a free key lifts both.

    A key's credits count per account across all its keys over the UTC day, and
    on

    a free key also over the UTC month (free keys from one address share one
    free

    quota). Keyed responses carry `X-Credits-Cost` (this call),
    `X-Credits-Daily-Limit`,

    `X-Credits-Daily-Remaining` and `X-Credits-Daily-Reset` (the next 00:00 UTC
    in Unix

    seconds), on a free key also `X-Credits-Monthly-Limit` and

    `X-Credits-Monthly-Remaining`; `X-RateLimit-Daily-Limit`,
    `X-RateLimit-Daily-Remaining`

    and `X-RateLimit-Daily-Reset` repeat the daily values. From 80% a notice
    says so.

    A call that does not fit in what is left gets `429 DAILY_QUOTA_EXCEEDED`
    with

    `Retry-After` until 00:00 UTC, or on a free key past the month

    `429 MONTHLY_QUOTA_EXCEEDED` with `Retry-After` until the 1st at 00:00 UTC,
    both

    with an `upgrade` object (Explorer for a free key, Builder for Explorer).

    Calls paid with x402 cost no credits and keep working.


    **Data availability:** fills and trades start on 25 May 2025; other

    datasets start later (funding 2025-09-27, open interest and mark prices

    2026-04-29). Bulk history is free to download from

    https://huggingface.co/ironflow.


    **Timestamps:** All timestamps are Unix milliseconds (int64).


    **Pagination:** Paginated endpoints return a `cursor` in
    `pagination.next_cursor`.

    Pass it as the `cursor` query parameter to fetch the next page.

    When `pagination.has_more` is `false`, you have reached the last page.


    **Notices:** a successful JSON object may start with `notice`, the

    `X-Ironflow-Notice` header in the body: what was served when it differs from

    what was asked (a keyless sample, a shortened history, a guessed path) and

    how to get the rest.


    **Guessed paths:** a few paths callers guess are answered by the endpoint

    they mean, with a `notice` and the `X-Ironflow-Canonical` header naming it:

    `/v1/liquidations/aggregate`, `/v1/summary/liquidations` and similar by

    `/v1/liquidations/summary`, `/v1/liquidations/buckets` and `heatmap` by

    `/v1/analytics/liquidation-levels`, and `/v1/liquidations/map/BTC-PERP` by

    `/v1/liquidations/map?market=BTC-PERP`. A path with trailing `.`, `,`, `;`
    or

    `)` (`/v1/query.`, copied from a sentence) is answered by the endpoint
    without

    them, with the same method, query and body. OpenAI-style paths
    (`/v1/models`,

    `/v1/chat/completions`) answer 404: this is not an LLM API, and agents use
    the

    MCP server above.
  contact:
    name: Ironflow Support
    url: https://ironflow.sh
servers:
  - url: https://api.ironflow.sh
    description: Production
security:
  - bearerAuth: []
  - {}
tags:
  - name: Hyperliquid RPC
    description: Your API key as a Hyperliquid /info URL, on your plan's limits.
  - name: Market Data
    description: Historical tick-level market data stored in ClickHouse.
  - name: Analytics
    description: >-
      Aggregated analytics. Net flows, funding stats and the liquidation map
      markets work without a key; liquidation levels, the liquidation map and
      the vault leaderboard return a sample without one; order flow needs a key
      (a free key works).
  - name: Cohorts
    description: Predefined and custom address groups for filtering market data.
  - name: Alerts
    description: >-
      Liquidation alerts that Ironflow posts to your webhook, Discord channel,
      Slack channel or Telegram chat.
  - name: Ironbox
    description: >-
      Secure, sandboxed environments in Tokyo, next to Hyperliquid, where a
      trading agent runs with Ironflow's data, analytics, alerts and execution.
      Any API key, free included.
  - name: Query
    description: >-
      Read-only ClickHouse SQL over curated Hyperliquid tables (hl.*), limited
      by tier.
  - name: Billing
    description: >-
      Paid plans, paid in USDC on Base or Hyperliquid, or with x402 on Base. No
      card, no account page.
  - name: System
    description: Health checks and metadata endpoints.
  - name: TradingApp
    description: >-
      The trading terminal's own endpoints (terminal.ironflow.sh),
      wallet-session auth. Not part of the public data API; left out of the docs
      copy.
paths:
  /v1/markets/hourly:
    get:
      tags:
        - Market Data
      summary: Hourly market panel (open interest, mark, oracle, funding, volume)
      description: >
        One row per market and UTC hour: open interest, mark and oracle price at
        the end of

        the hour, the funding rate charged for the hour, open, high, low and
        close, volume in

        coins and USD, taker buy and sell USD, liquidation USD and trade count.
        The same rows

        as SQL's `hl.market_hourly`. Oldest hour first, then by `market_id`.


        `market` selects one market. Without it you get every market, at most
        one day of

        hours per page: keep paging with `cursor` while `pagination.has_more` is
        true (a page

        can hold fewer than `limit` rows and still have more after it). The
        hours served start

        at or after `from` and before `to`; without `from`, the last 24 hours.
        Only complete

        hours are served: `complete_until` is the end of the newest one, and an
        hour appears a

        few minutes after it ends.


        History follows your plan: 24 hours without a key, 14 days on a free key
        and on calls

        paid with x402, 90 days on Explorer, a year on Builder, everything since
        May 2025 on

        Enterprise. A `from` before your history is moved up
        (`X-Ironflow-History-Clamped`);

        a range that ends before it answers `HISTORY_OUT_OF_RANGE` with
        `allowed_from`.


        Null means there is no value: `open` to `close` are null in an hour
        without trades,

        and open interest, mark, oracle and funding are null for spot and
        outcome markets.
      operationId: listMarketHourly
      parameters:
        - name: market
          in: query
          description: >-
            One market (BTC-PERP, xyz:NVDA-PERP, HYPE/USDC) or Hyperliquid coin
            name (BTC). Omit it for every market.
          schema:
            type: string
            example: BTC-PERP
        - name: from
          in: query
          description: >-
            Start as Unix milliseconds, Unix seconds or ISO 8601. Defaults to 24
            hours before `to`; moved up to your plan's history.
          schema:
            type: integer
            format: int64
            example: 1794528000000
        - name: to
          in: query
          description: >-
            End (exclusive) as Unix milliseconds, Unix seconds or ISO 8601.
            Defaults to now; hours that are not complete yet are left out.
          schema:
            type: integer
            format: int64
            example: 1794614400000
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/cursor'
      responses:
        '200':
          description: One page of market hours.
          headers:
            Cache-Control:
              description: Complete hours do not change; `private, max-age=60`.
              schema:
                type: string
                example: private, max-age=60
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketHourlyPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          description: >-
            `HISTORY_OUT_OF_RANGE` for a keyless caller, when x402 is on: the
            range ends before your history. `allowed_from` is the earliest
            `from` that works; the `PAYMENT-REQUIRED` header has the terms for
            one paid call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            `HISTORY_OUT_OF_RANGE` (with `allowed_from`, as on the 402) when one
            paid call is not on offer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '504':
          $ref: '#/components/responses/QueryTimeout'
components:
  parameters:
    limit:
      name: limit
      in: query
      description: Maximum number of results per page. Default 100, max 1000.
      schema:
        type: integer
        default: 100
        maximum: 1000
        example: 100
    cursor:
      name: cursor
      in: query
      description: >-
        Opaque pagination cursor from `pagination.next_cursor` in a previous
        response.
      schema:
        type: string
        example: MTcxODQwMDAwMDAwMC00Mg==
  schemas:
    MarketHourlyPage:
      type: object
      required:
        - data
        - pagination
        - complete_until
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/MarketHour'
        pagination:
          $ref: '#/components/schemas/Pagination'
        complete_until:
          type: integer
          format: int64
          example: 1794657600000
          description: End of the newest complete hour (Unix ms)
        notice:
          type: string
          description: >-
            Present when the range was moved up to your history or the market is
            unknown
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code.
              example: RATE_LIMIT_EXCEEDED
            message:
              type: string
              description: Human-readable error description.
              example: Rate limit exceeded. Retry after 60 seconds.
            upgrade:
              type: object
              description: >-
                On a limit a higher plan lifts, the next plan, its price, what
                it lifts and the call that gets it.
              properties:
                plan:
                  type: string
                  example: explorer
                price_usdc:
                  type: number
                  example: 49
                days:
                  type: integer
                  example: 30
                unlocks:
                  type: string
                  description: >-
                    What the plan adds, starting with SQL export to Parquet or
                    CSV
                limit:
                  type: string
                  description: >-
                    The limit this error hit: rate, history, window, sql_budget,
                    read_budget, streams, alerts, keyless_day or tier
                  example: rate
                lifts:
                  type: string
                  description: What the plan changes about that limit
                  example: 60 requests a minute now, 100 on Explorer
                url:
                  type: string
                  format: uri
                  description: Where to upgrade; the link carries the plan and the limit
                how:
                  type: string
                  description: The API call that opens the upgrade request
                buy_url:
                  type: string
                  format: uri
                  description: Buy this plan in one x402 call (USDC on Base)
                  when x402 is on: null
                pay_per_call:
                  type: object
                  description: On limits one paid call lifts
                  how to pay for this call with x402: null
            get_key:
              type: object
              description: >-
                On every keyless 429 (daily cap, per-minute rate, SQL and read
                budgets, stream cap) and keyless history refusal, the one call
                that gets a free key. `page` is the key page tagged with the
                refusal (utm_campaign).
              properties:
                method:
                  type: string
                  example: POST
                url:
                  type: string
                  format: uri
                  example: https://api.ironflow.sh/v1/keys
                body:
                  type: object
                  additionalProperties:
                    type: string
                  example:
                    email: you@example.com
                page:
                  type: string
                  format: uri
            dataset:
              type: object
              description: >-
                On `HISTORY_OUT_OF_RANGE`, and on a rate limit for a closed past
                range, the free download that holds the requested rows, when one
                does. The message repeats it with its dates.
              properties:
                name:
                  type: string
                  example: Hyperliquid Liquidations
                url:
                  type: string
                  format: uri
                  example: >-
                    https://huggingface.co/datasets/ironflow/hyperliquid-liquidations?utm_source=api
                holds:
                  type: string
                  example: every liquidation
                from:
                  type: string
                  format: date
                  example: '2025-05-25'
                through:
                  type: string
                  format: date
                  example: '2026-10-31'
            ironbox:
              type: object
              description: >-
                On a rate limit, the keyless credits, the daily and monthly
                credits, the stream limit, the SQL budget and the read budget,
                how to run the agent in an Ironbox instead.
              properties:
                why:
                  type: string
                  example: >-
                    No rate limits inside an Ironbox: your agent runs next to
                    Hyperliquid with the data on the box.
                create:
                  type: string
                  example: POST https://api.ironflow.sh/v1/ironbox
                url:
                  type: string
                  format: uri
                  example: https://ironflow.sh/ironbox?utm_source=api
            stream:
              type: string
              description: >-
                On the keyless daily credits, a per-minute rate limit, the read
                budget and a keyless history refusal of `/v1/liquidations` and
                `/v1/trades`, the live stream that replaces polling.
              example: >-
                GET https://api.ironflow.sh/v1/liquidations/stream (1 credit a
                minute)
            allowed_from:
              type: integer
              format: int64
              description: >-
                On `HISTORY_OUT_OF_RANGE`, the earliest `from` (Unix ms; `at` on
                `/v1/positions`) the caller's history reaches; a request from
                there is served.
              example: 1794394800000
            did_you_mean:
              type: array
              items:
                type: string
              description: >-
                On an unknown market (the liquidation map, the order book), the
                markets the name most likely meant. The message repeats them.
              example:
                - xyz:GOLD-PERP
                - PAXG-PERP
    MarketHour:
      type: object
      required:
        - timestamp
        - market
        - market_id
        - market_class
        - dex
        - open
        - high
        - low
        - close
        - volume
        - volume_usd
        - taker_buy_usd
        - taker_sell_usd
        - liquidation_usd
        - trades
        - open_interest
        - open_interest_usd
        - mark_price
        - oracle_price
        - funding_rate
      properties:
        timestamp:
          type: integer
          format: int64
          example: 1794528000000
          description: Start of the UTC hour (Unix ms)
        market:
          type: string
          example: BTC-PERP
        market_id:
          type: integer
          example: 0
        market_class:
          type: string
          enum:
            - perp
            - spot
            - prediction
            - option
        dex:
          type: string
          example: ''
          description: HIP-3 deployer (xyz
          flx); empty for native markets: null
        open:
          type:
            - number
            - 'null'
          example: 71448
          description: First trade price of the hour; null without trades
        high:
          type:
            - number
            - 'null'
          example: 71507
        low:
          type:
            - number
            - 'null'
          example: 70624
        close:
          type:
            - number
            - 'null'
          example: 71109
          description: Last trade price of the hour
        volume:
          type: number
          example: 3463.12
          description: Volume in the base asset
          each trade once: null
        volume_usd:
          type: number
          example: 246018390.33
        taker_buy_usd:
          type: number
          example: 94154048.84
          description: USD volume where the buyer was the taker
        taker_sell_usd:
          type: number
          example: 151864341.49
          description: USD volume where the seller was the taker
        liquidation_usd:
          type: number
          example: 19530228.46
          description: USD volume of trades that were part of a liquidation
        trades:
          type: integer
          example: 27256
        open_interest:
          type:
            - number
            - 'null'
          example: 39517.2047
          description: Open interest at the end of the hour
          in the base asset. Perps only: null
        open_interest_usd:
          type:
            - number
            - 'null'
          example: 2811119583.86
          description: open_interest times mark_price
        mark_price:
          type:
            - number
            - 'null'
          example: 71136.6
          description: Mark price at the end of the hour. Perps only
        oracle_price:
          type:
            - number
            - 'null'
          example: 71162
          description: Oracle price at the end of the hour. Perps only
        funding_rate:
          type:
            - number
            - 'null'
          example: 0.0000125
          description: >-
            Funding rate charged for the hour (settled when it ends), a fraction
            per hour; where the settlement is missing, the last rate seen in the
            hour
    Pagination:
      type: object
      required:
        - next_cursor
        - has_more
      properties:
        next_cursor:
          type: string
          nullable: true
          description: >-
            Cursor to pass as `cursor` query parameter to fetch the next page.
            Null when no more pages.
          example: MTcxODQwMDAwMDAwMC00Mg==
        has_more:
          type: boolean
          description: Whether more pages are available.
          example: true
  responses:
    BadRequest:
      description: Invalid request parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: BAD_REQUEST
              message: market is required (e.g. BTC-PERP)
    RateLimited:
      description: >-
        Rate limit exceeded: `RATE_LIMIT_EXCEEDED` (per minute),
        `KEYLESS_DAILY_LIMIT` (keyless credits per IP or per app),
        `DAILY_QUOTA_EXCEEDED` (the call does not fit in the account's credits
        left for the UTC day; `Retry-After` runs to 00:00 UTC) or
        `MONTHLY_QUOTA_EXCEEDED` (a free key's credits for the UTC month;
        `Retry-After` runs to the 1st at 00:00 UTC). On the credit refusals
        `upgrade` names the plan: Explorer for a free key, Builder for Explorer.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            example: 60
        X-Credits-Cost:
          description: What the refused call would cost in credits.
          schema:
            type: integer
            example: 10
        X-Credits-Daily-Remaining:
          description: The credits left today (keyless, over the rolling 24 hours).
          schema:
            type: integer
            example: 5
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Rate limit exceeded. Retry after 60 seconds.
    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INTERNAL_ERROR
              message: An unexpected error occurred
    QueryTimeout:
      description: >-
        Query exceeded the 15-second execution limit. Narrow your time range,
        add a market filter, or reduce the limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: QUERY_TIMEOUT
              message: >-
                query took too long — narrow your time range, add a market
                filter, or reduce the limit
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Ironflow API key, format `if_xxxx`. Free keys at
        https://ironflow.sh/key. Optional on read endpoints.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.