API Reference
Trades
List

List Trades

GET /api/v1/trades

Scope: trades:read

Returns your 50 most recent trades, all statuses. Trades are scoped to the authenticated user — you'll never see another user's trades.

Response

200 OK

[
  {
    "trade_id": "00000000-0000-0000-0000-000000000000",
    "user_id": "usr_YOUR_USER_ID",
    "market": "HYPE",
    "long_exchange": "lighter",
    "long_wallet": "0x...",
    "short_exchange": "extended",
    "short_wallet": "0x...",
    "target_spread": "0",
    "spread_condition": "gte",
    "target_size": "2",
    "filled_size": "2",
    "execution_type": "maker_taker",
    "maker_leg": "long",
    "max_slippage": "0.005",
    "status": "completed",
    "rate_budget": 20,
    "created_at": "2026-03-12T13:06:58Z",
    "updated_at": "2026-03-12T13:08:30Z",
    "completed_at": "2026-03-12T13:08:30Z",
    "size_type": "base",
    "spread_type": "absolute",
    "failure_reason": ""
  }
]

Trade object fields

The example above shows the base fields every trade carries. The fields below are also returned and are committed: they keep their name and meaning within /api/v1, and any change comes with a version bump. Fields marked absent until are omitted (not null) before that point.

FieldTypePresentMeaning
maker_filled_sizedecimal stringafter teardownGross size filled on the maker leg. filled_size keeps its meaning (gross maker fill); these four reconstruct the real position without a venue read
hedge_filled_sizedecimal stringafter teardownSize filled on the hedge leg
flattened_sizedecimal stringafter teardownMaker size closed reduce-only by a teardown corrective. On a trade with failure_code: "liquidation": what the flatten of the surviving leg executed, on whichever leg survived
net_exposuredecimal stringafter teardownmaker_filled_size − flattened_size − hedge_filled_size: > 0 unhedged maker exposure, < 0 over-hedged, 0 at a clean completion or when a maker fill was fully flattened. Non-zero on a terminal trade means a position is still open on a venue. On a trade with failure_code: "liquidation" the venue closed one leg, so this is what the survivor flatten left open on the surviving leg instead: > 0 on the maker leg, < 0 on the hedge leg. maker_filled_size and hedge_filled_size stay the fills before the liquidation
working_orderobjectGET /trades/{id} only, while a maker order is liveMetadata-only snapshot of the resting maker order, from the worker's last heartbeat: { "exchange", "market", "side": "buy"|"sell", "price", "size", "state": "pending"|"resting"|"filled"|"none" }. Carries no wallet or credentials. "none" means the marker was cleared. Lets a client tell "amend persisted" (price moved) from "quote re-priced"
fills_hedgedbooleanwhen trueEvery unit filled on the maker leg is covered by the hedge leg and no residual exposure remains: maker_filled_size > 0, flattened_size = 0, net_exposure = 0. Never on a trade with failure_code: "liquidation": the venue closed one leg, so it is not a hedged pair. The key is omitted when false, so it is an assertion and never a claim about a trade whose fill accounting was never written. Read it with status, not instead of it — see Reading a hedged partial
failure_codestringterminal tradesTyped terminal classification, derived at read time — see Failure codes
failure_detailstringterminal tradesHuman detail behind failure_code; empty when the code says everything (a clean cancel)
killed_by_switchbooleanterminal trades, when trueThe trade was halted by the user or global kill switch
long_market_nativestringwhen it differs from marketThe venue-native symbol the long leg trades (e.g. a venue that lists kBONK for BONK). Empty = same as market
short_market_nativestringwhen it differs from marketSame for the short leg
opened_atRFC3339 timestampafter the first maker fillPosition open time, stamped once by the worker on the first maker fill (created_at is when the trade was accepted)

Provisional fields — returned today, read them as optional and expect the shape to change without a version bump:

FieldTypeMeaning
funding_at_entryobjectBoth legs' live funding as the trade was admitted: { "stamped_at", "long": { "exchange", "annualised_pct", "source": "indicative"|"settled"|"unknown", "sampled_at", "next_funding_at" }, "short": { … }, "net_apr_pct" }. net_apr_pct is short minus long (positive = the position collects funding) and is present only when both legs had a reading. Absent when the cache had no reading — never a fabricated zero
funding_at_exitobjectSame stamp taken as the trade went terminal
hl_action_countsobjectHyperliquid actions this trade consumed against the wallet's address budget: { "place", "modify", "cancel" } (integers). Hyperliquid's own nRequestsUsed increments the same way — one per order, one per modify, one per cancel, nothing for info requests — so these are the trade's real share of the address budget
hl_action_budgetintegerThe per-trade cap on Hyperliquid actions, for a trade whose maker leg is on Hyperliquid or trade[xyz]. While the trade runs it is what create asked for, if anything; when the trade ends it is the cap that applied (by default the trade's USD notional, at least 100). See Hyperliquid action cap
fast_move_guard_statsobjectWhat the aggressive fast-move guard saw and did, written when the trade ends. Alongside triggers / cancels / holds it carries re_places and re_place_modifies: the placement that replaced each guard-cancelled order and the first modify that brought it to the touch. A trigger with a resting order therefore costs three actions in hl_action_counts — cancels + re_places + re_place_modifies is the guard's share of the burn
unmatched_fillsarrayFills a worker saw on your account that it could not attribute to any order it placed: [{ "at", "reason": "unknown_cloid"|"tracked_mismatch", "exchange", "market", "cloid", "order_id", "side", "price", "size" }]. These are not this trade's fills — see Unclaimed fills on your account. Absent when there were none; capped at 20 per trade

Reading a hedged partial

A trade that filled part of its target, hedged all of it, and then could not quote the remainder ends failed — the venue rejected the last quote, so the execution did fail — even though the position it holds is sound. The example is a maker leg that filled 74% and left a $6 remainder under Hyperliquid's $10 minimum: status: "failed", failure_code: "below_min_size", maker_filled_size and hedge_filled_size equal, and net_exposure: "0".

Book from the fill fields, not from status: fills_hedged: true is the single discriminator for this case, because net_exposure: "0" alone cannot tell a hedged partial from a fill that a teardown corrective flattened away — both read zero.

There is deliberately no separate terminal status for it. Clients switch on status, so adding a value would be a breaking change dressed as an additive one.

Unclaimed fills on your account

unmatched_fills is the one field on a trade that is not about that trade.

A worker streams the whole account it trades on, so it sees every fill on that account — including fills belonging to another of your trades, to another tool signing on the same wallet, or to an order nobody is tracking any more. When a fill arrives that the worker cannot attribute to an order it placed itself, it drops it. Dropping is correct: acting on a fill that may belong to something else would be worse than ignoring it.

What changed is that the drop is now recorded. The trade that happened to be streaming the account when the fill landed carries it out as unmatched_fills, because you are the only person who can say whose that fill was.

Read one as: something filled on your account and no Spreadr order claimed it. That is worth investigating — an unclaimed fill is an unhedged position until proven otherwise. It is not evidence that the reporting trade misbehaved, and the sizes in it must not be booked against that trade.

reason distinguishes two shapes, both meaning the same thing to you:

  • unknown_cloid — the fill arrived with no order state at all.
  • tracked_mismatch — the fill arrived while a different order was being tracked.

The list is capped at 20 per trade. It tells you an unclaimed fill happened; it is not an exhaustive ledger.

Trade statuses

StatusDescription
pendingAccepted, execution not yet started
validatingPre-flight checks (credentials, margin) in progress
activeTrade is executing
pausedExecution paused (e.g. during maintenance); position held
recoveringExecution is being relaunched after a crash or disconnect
completedTarget size fully filled
canceledCanceled by user
rejectedFailed pre-flight validation (see failure_reason)
failedExecution failed (see failure_reason)

Example

curl -H "X-API-Key: sprdr_..." https://api.spreadr.xyz/api/v1/trades

Filter client-side by status if you only want active ones:

trades = requests.get(f"{API_URL}/trades", headers=headers).json()
active = [t for t in trades if t["status"] in ("pending", "validating", "active", "paused", "recovering")]