{
  "id": "surf.hyperliquid.fills",
  "object": "tool",
  "name": "Fills",
  "summary": "Returns a wallet's individual fills (fill-level executions), newest first by default, with realized PnL, fees, and direction (crossed=true marks a taker fill that took liquidity).",
  "description": "Returns a wallet's individual fills (fill-level executions), newest first by default, with realized PnL, fees, and direction (crossed=true marks a taker fill that took liquidity). For closed round-trip trades (one row per open→close position, with per-trade P&L incl. funding), use /hyperliquid/trades instead — funding is NOT attributable at fill level. Default order=desc is a recent-capped window (no offset): up to limit of the most-recent fills; page back by setting to to the oldest time you received (or to_ms+to_fill_id for millisecond-exact paging). For COMPLETE history with no cap, use order=asc with from and follow meta.next_cursor. symbol filters to one market (applied client-side, so with order=desc it only filters the recent window — pair with from/to to reach older history).\n\nRaw surf#hyperliquid/fills endpoint: input is { body, queryParams, pathParams } as the provider documents them (https://docs.asksurf.ai/data-api/hyperliquid/fills).",
  "category": "lookup",
  "hosted_by": "Surf",
  "runtime": "partner",
  "side_effects": false,
  "status": "live",
  "credits": 0,
  "price": null,
  "providers": [
    {
      "id": "surf",
      "name": "Surf",
      "price": null,
      "own_key_only": true
    }
  ],
  "mcp_name": "surf_hyperliquid_fills",
  "input_schema": {
    "type": "object",
    "properties": {
      "queryParams": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "minLength": 1,
            "description": "Wallet address: a 0x EVM address or an ENS name (e.g. vitalik.eth). Solana addresses are not supported."
          },
          "from": {
            "type": "string",
            "minLength": 1,
            "description": "Window start (Unix seconds or a date). With the default order=desc, results cover the window forward from this point and are capped at ~2000 of the earliest fills in range (narrow the range to see the most-recent fills). With order=asc this is the start of a complete-history walk: follow meta.next_cursor to page through every fill in the window with no cap. Example: 2026-03-01."
          },
          "to": {
            "type": "string",
            "minLength": 1,
            "description": "Window end (Unix seconds or a date), inclusive. Defaults to now. With order=desc, page back within the most-recent results by setting this to the oldest time you received (prefer to_ms for millisecond precision); with order=asc it bounds the walk. Example: 2026-03-01."
          },
          "to_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Window end in Unix milliseconds, for millisecond-precise desc paging: pass the time_ms of the oldest fill you received to fetch the next page. On its own the boundary is exclusive (fills AT to_ms are dropped), so when more than limit fills share one millisecond the remainder is skipped — pair it with to_fill_id to page losslessly through a boundary millisecond. Takes precedence over to (which is ignored when to_ms is set). Like to, it pages within the recent (~2000-fill) window. desc-only: rejected with order=asc (use cursor instead)."
          },
          "to_fill_id": {
            "type": "string",
            "minLength": 1,
            "description": "Cursor tiebreak for to_ms: pass the fill_id of the oldest fill you received alongside its time_ms as to_ms. The next page then resumes strictly after that (time_ms, fill_id) pair — fills at the boundary millisecond with a lower fill_id are included instead of skipped, so paging never loses fills that share a millisecond. Requires to_ms; desc-only. Equivalent to following meta.next_cursor, which encodes the same position. An empty page can mean the window is exhausted (set from to reach older history), not that no older fills exist."
          },
          "order": {
            "default": "desc",
            "type": "string",
            "enum": [
              "desc",
              "asc"
            ],
            "description": "desc (default): newest first — the recent feed, or the earliest-anchored ~2000-fill slice when from is set. asc: oldest first, a complete-history walk from from (required) up to to — each page links the next via meta.next_cursor with no result cap, the reliable mode for full trade-history or PnL reconstruction. Defaults to \"desc\"."
          },
          "cursor": {
            "type": "string",
            "minLength": 1,
            "description": "Opaque continuation token from a previous response's meta.next_cursor. It encodes the paging direction, position, and window, so pass it with only symbol and limit — combining it with from, to, to_ms, or to_fill_id is rejected with 400."
          },
          "symbol": {
            "type": "string",
            "minLength": 1,
            "description": "Filter to one market (exact match, e.g. xyz:GOLD). Applied client-side: with order=desc it filters after the recent cap (pair it with from/to so older fills are not missed); with order=asc the cursor walk scans past non-matching fills, so a page may return fewer than limit items (or none) while meta.next_cursor still advances."
          },
          "limit": {
            "default": 20,
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "Page size (1-100). Defaults to 20."
          }
        },
        "required": [
          "address"
        ],
        "additionalProperties": false
      }
    },
    "required": [
      "queryParams"
    ],
    "additionalProperties": false
  },
  "kind": "endpoint"
}