API Reference
Trades
Create

Create Trade

POST /api/v1/trades

Scope: trades:write

Submit a new spread trade. Returns immediately with a trade_id; execution happens asynchronously.

Request

{
  "market": "HYPE",
  "long_exchange": "lighter",
  "long_wallet": "0xYourWalletAddressHere",
  "short_exchange": "extended",
  "short_wallet": "0xYourWalletAddressHere",
  "target_spread": "0",
  "spread_condition": "gte",
  "target_size": "2",
  "execution_type": "maker_taker",
  "maker_leg": "long",
  "max_slippage": "0.1",
  "size_type": "base",
  "spread_type": "absolute"
}

Required fields

FieldTypeDescription
marketstringTrading pair symbol (e.g. HYPE, BTC, ETH)
long_exchangestringExchange for the long leg
long_walletstringWallet address for the long leg
short_exchangestringExchange for the short leg (must differ from long_exchange)
short_walletstringWallet address for the short leg
target_spreadstringTarget spread value (decimal string, in units of spread_type)
spread_conditionstringOne of gte, gt, lte, lt, eq
target_sizestringTotal size to fill (positive decimal)
execution_typestringmaker_taker
maker_legstringlong or short
max_slippagestringMaximum allowed slippage (non-negative decimal)

Optional fields

FieldTypeDefaultDescription
size_typestringbasebase (token amount) or notional (USD amount)
spread_typestringabsoluteabsolute (raw USD price difference), percentage (% of hedge reference price), bps (basis points of hedge reference price), or ticks (multiples of the coarser leg's tick size)
idempotency_keystringSee idempotency below — required via header or body

Idempotency (required)

Every create request must carry an idempotency key — either the X-Idempotency-Key header (preferred; takes precedence) or the idempotency_key body field. Missing key → 400. Keys must match ^[a-zA-Z0-9_-]{1,128}$ (a UUID works).

Replaying a key returns 200 OK with the original trade and "idempotent": true — no new trade is created:

{
  "trade_id": "00000000-0000-0000-0000-000000000000",
  "status": "active",
  "rate_budget": 20,
  "idempotent": true
}

Response

202 Accepted

{
  "trade_id": "00000000-0000-0000-0000-000000000000",
  "status": "pending",
  "rate_budget": 20
}

The rate_budget is the per-second request budget Spreadr has allocated to this trade against the maker exchange's per-account rate limit.

When the hedge exchange enforces a per-account limit that other legs of yours share, the response also carries hedge_rate_budget: the per-second budget allocated to this trade's hedge leg. Both budgets are re-divided live as your trades on those accounts start and finish.

Aggressive quoting and the fast-move guard

quote_mode: "aggressive" (maker-taker only) pegs the maker up to, or across, the touch to fill now, bounded by worst_spread (same units as target_spread; omitted = peg to the touch). Because an aggressive order rests inside the spread, a fast move of the hedge reference can run it over before a re-quote lands. The fast-move guard watches for that:

FieldValuesDefault
fast_move_guardshadow (evaluate and record only), on (cancel the resting order when the hedge reference moves against it by the threshold inside the window, then hold quoting until the book settles), offshadow for aggressive trades
fast_move_guard_bpsexplicit trigger threshold, 0 < x <= 1000the resting order's distance to the hedge reference (its quote cushion), floored at 10 bps
fast_move_window_ms505000250
fast_move_settle_ms5010000500 (a hold is capped at 3 s while the book stays fast)

Every trigger appears in GET /trades/{id}/events as a fast_move_guard event (move, threshold, references, action), hedge_outcome events carry guard_fill_class (after_trigger: a fill inside the settle window of a trigger; adverse: a fill whose move met the threshold with no trigger), and fast_move_guard_stats is written on the trade when it ends.

Validations

The server enforces these before accepting the trade:

  • Both exchanges must have active credentials linked for the specified wallets, and the wallets must belong to your account (403 otherwise).
  • System and both exchanges must not be in maintenance mode (503).
  • The global kill switch must not be active (503).
  • long_exchange and short_exchange must differ.
  • market must be recognized on both exchanges — not tradeable on one or both, or ambiguous between the two, returns 400 (use the canonical symbol from GET /api/v1/markets).
  • Concurrent maker-trade cap per (wallet, maker exchange), counting only trades where the wallet is on the maker leg. The cap varies by venue (currently: Lighter 1; Hyperliquid, Orderly, Aster 2; Extended 3; trade[xyz] shares Hyperliquid's cap — all subject to change). Exceeding it returns 409 ("max N concurrent maker trades on {exchange} — already has M active").
  • Sufficient account-level rate budget on the maker exchange (429).

Example

curl -X POST https://api.spreadr.xyz/api/v1/trades \
  -H "X-API-Key: sprdr_..." \
  -H "X-Idempotency-Key: my-bot-001" \
  -H "Content-Type: application/json" \
  -d '{
    "market": "HYPE",
    "long_exchange": "lighter",
    "long_wallet": "0xYourWalletAddressHere",
    "short_exchange": "extended",
    "short_wallet": "0xYourWalletAddressHere",
    "target_spread": "0",
    "spread_condition": "gte",
    "target_size": "2",
    "execution_type": "maker_taker",
    "maker_leg": "long",
    "max_slippage": "0.1"
  }'