PerpsKit

PerpsKit is a unified layer for interacting with on-chain perpetual futures. It provides normalized market data, trading actions, account state, and transaction construction across multiple perpetual DEX protocols and networks.

What PerpsKit Does

  • Unified trading actions

    A single action model works across all supported perpetual exchanges. Currently exposed action types:

    ActionDescription
    openOpen a new long/short position (market or limit)
    closeClose (full or partial) an existing position
    updateLeverageChange leverage for a market
    updateMarginAdd or remove isolated margin from a position
    stopLoss / takeProfitPlace a single TP or SL trigger order
    setTpAndSlBundled action that creates/updates both TP and SL
    editOrderModify an existing open order
    cancelOrderCancel one or many open orders
    fund / withdrawDeposit or withdraw collateral (cross-chain capable)
    approveAgentApprove an agent wallet for delegated trading
    approveBuilderFeeApprove a builder-code fee recipient (Hyperliquid)
  • Cross-protocol, cross-chain coverage

    • Live:
      • Hyperliquid (provider id hyperliquid)
    • In progress / planned:
      • GMX
      • Lighter
      • Drift
      • dYdX v4
  • Consistent data structures PerpsKit exposes normalized DTOs:

    • MarketDto — metadata, prices, fees, leverage range, funding
    • ActionDto — high-level trading intent, current status, transaction list
    • TransactionDto — unsigned EIP-712 / EVM / protocol-specific payloads to sign
    • PositionDto / OrderDto / BalanceDto — normalized portfolio state
    • EventDto — async venue outcomes (fills, liquidations, TP/SL triggers)
  • Portfolio & balances Unified endpoints for:

    • Open positions
    • Open orders
    • Collateral balance, margin usage, account value, unrealized PnL

Authentication

All endpoints (except /v1/health) require an x-api-key header. Keys are rate-limited per category (trial / standard / pro).


Core Endpoints

Discovery & Metadata

List providers

GET /v1/providers

Returns the available perp providers, their network, supported actions, argument schemas, and metadata.

Get provider details

GET /v1/providers/{providerId}

List markets (paginated)

GET /v1/markets?providerId=hyperliquid&sortBy=volume24h&order=desc&limit=50&offset=0

Query params:

ParamTypeNotes
providerIdstringOptional filter
sortByvolume24h | markPrice | priceChangePercent24hOptional
orderasc | descDefault desc
limit, offsetnumberStandard pagination

Each item in items[] is a MarketDto:

{
  "id": "hyperliquid-eth-usdc",
  "providerId": "hyperliquid",
  "baseAsset": {
    "symbol": "ETH",
    "name": "Ethereum",
    "network": "hyperliquid",
    "decimals": 18
  },
  "quoteAsset": {
    "symbol": "USDC",
    "network": "hyperliquid",
    "decimals": 6
  },
  "leverageRange": [1, 50],
  "supportedMarginModes": ["isolated", "cross"],
  "markPrice": 3950.42,
  "oraclePrice": 3949.87,
  "priceChange24h": -1.23,
  "priceChangePercent24h": -0.031,
  "volume24h": 3110000000,
  "openInterest": 18234.56,
  "makerFee": "0.0002",
  "takerFee": "0.0004",
  "fundingRate": "0.00012",
  "fundingRateIntervalHours": 8,
  "minSize": 10.22,
  "metadata": {
    "name": "ETH-USDC Perpetual",
    "logoURI": "https://assets.stakek.it/markets/hyperliquid-eth-usdc.svg",
    "url": "https://app.hyperliquid.xyz/trade/ETH"
  }
}

Get a single market (with chart config)

GET /v1/markets/{marketId}

Same shape as a list item, plus a chartConfig block:

{
  "id": "hyperliquid-eth-usdc",
  "providerId": "hyperliquid",
  "...": "all MarketDto fields above",
  "chartConfig": {
    "pricescale": 100,
    "minmov": 1,
    "supportedResolutions": ["1", "5", "15", "60", "240", "1D"],
    "hasIntraday": true,
    "hasDaily": true
  }
}

Get historical candles

GET /v1/markets/{marketId}/candles?interval=1h&from=1733000000000&to=1733600000000

OHLCV data, max 5,000 candles per request. Supported intervals are exposed via the chart config on GET /v1/markets/{marketId}.

{
  "marketId": "hyperliquid-eth-usdc",
  "interval": "1h",
  "candles": [
    {
      "openTime": 1704067200000,
      "closeTime": 1704070799999,
      "open": "42150.5",
      "high": "42500.0",
      "low": "42000.0",
      "close": "42350.25",
      "volume": "1234.56",
      "trades": 482
    }
  ]
}

Actions

Execute an action

POST /v1/actions
{
  "providerId": "hyperliquid",
  "address": "0x1234...",
  "action": "open",
  "args": {
    "marketId": "hyperliquid-eth-usdc",
    "side": "short",
    "amount": "43",
    "leverage": 20,
    "marginMode": "isolated"
  }
}

Returns an ActionDto containing one or more unsigned transactions[] to sign and submit. If args.limitPrice is set, the order is placed as a limit order; otherwise it executes as a market order.

Notes:

  • The request body uses args (not arguments).
  • Argument shapes per action are defined as Zod schemas inside @stakekit/chains and exposed via GET /v1/providers as JSON Schema (with label, placeholder, and optionsRef UI hints).
  • For cross-chain funding, pass args.fromToken ({ network, address?, symbol? }). PerpsKit will compute the required swap/bridge/deposit transactions and prepend them to transactions[].

Response (ActionDto):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "providerId": "hyperliquid",
  "action": "open",
  "status": "CREATED",
  "summary": {
    "type": "Open Position",
    "asset": "ETH",
    "direction": "short",
    "orderType": "market",
    "leverage": 20,
    "size": "0.215",
    "collateral": "43.00",
    "fee": "0.0004",
    "orderValue": 859.25
  },
  "signedMetadata": "01012b02010181d0010081d1040000000b240345544815...",
  "transactions": [
    {
      "id": "11111111-2222-3333-4444-555555555555",
      "network": "hyperliquid",
      "chainId": "1337",
      "type": "OPEN_POSITION",
      "status": "CREATED",
      "address": "0x1234...",
      "signingFormat": "EIP712_TYPED_DATA",
      "signablePayload": {
        "domain": { "name": "Exchange", "version": "1", "chainId": 1337 },
        "types": { "Agent": [/* ... */] },
        "message": { "source": "a", "connectionId": "0x..." }
      },
      "rawPayload": {
        "type": "order",
        "orders": [{ "a": 4, "b": false, "p": "0", "s": "0.215", "r": false, "t": { "limit": { "tif": "Ioc" } } }],
        "grouping": "na",
        "nonce": 1733332000000
      }
    }
  ],
  "createdAt": "2026-02-19T10:23:45.000Z",
  "completedAt": null
}

status follows ActionStatus: CREATED, PROCESSING, WAITING_FOR_NEXT, SUCCESS, FAILED, CANCELED, STALE.

Each transaction's status follows PerpTransactionStatus: CREATED, QUEUED, BROADCASTED, CONFIRMED, FAILED, NOT_FOUND.

List user actions (paginated)

GET /v1/actions?address=0x...&providerId=hyperliquid&limit=20&offset=0

Optional filters: status, statuses, type (action type), marketId. Ordered by most recent first. Each item is an ActionDto (same shape as above).

Get an action

GET /v1/actions/{actionId}

Use this to:

  • Poll for action progress (status lifecycle: CREATEDPROCESSINGSUCCESS / FAILED / CANCELED / STALE)
  • Read the latest per-transaction states attached to the action

Returns the same ActionDto shape as POST /v1/actions.


Transactions

Submit a signed transaction

POST /v1/transactions/{transactionId}/submit

transactionId is the UUID returned inside ActionDto.transactions[].id.

Body — provide either signedPayload (we broadcast for you) or transactionHash (you've already broadcast it):

{
  "signedPayload": "0x..."
}
{
  "transactionHash": "0xabc..."
}

Response:

{
  "transactionHash": "0x8a3fb2c1d4e5f67890abcdef12345678",
  "link": "https://app.hyperliquid.xyz/explorer/tx/0x8a3fb2c1d4e5f67890abcdef12345678",
  "status": "CONFIRMED",
  "details": { "orderStatus": "filled", "fillPrice": 3975 }
}

status follows PerpTransactionStatus: CREATED, QUEUED, BROADCASTED, CONFIRMED, FAILED, NOT_FOUND.

Typical usage:

  1. POST /v1/actions — receive ActionDto with transactions[].
  2. For each transaction in order, sign according to its signingFormat (EIP712_TYPED_DATA, EVM_TRANSACTION, etc.) using signablePayload.
  3. POST /v1/transactions/{transactionId}/submit for each.
  4. Poll GET /v1/actions/{actionId} until a final state.

Portfolio

Positions

POST /v1/positions

Request:

{ "address": "0x1234...", "providerId": "hyperliquid" }

Response — array of PositionDto:

[
  {
    "marketId": "hyperliquid-eth-usdc",
    "side": "short",
    "size": "0.215",
    "entryPrice": 4000,
    "markPrice": 3975,
    "leverage": 20,
    "marginMode": "isolated",
    "margin": 43,
    "unrealizedPnl": 5.38,
    "funding": -1.24,
    "liquidationPrice": 4200,
    "pendingActions": [
      {
        "type": "close",
        "label": "Close Position",
        "args": { "marketId": "hyperliquid-eth-usdc" }
      }
    ]
  }
]

Orders

POST /v1/orders

Request:

{ "address": "0x1234...", "providerId": "hyperliquid" }

Response — array of OrderDto:

[
  {
    "marketId": "hyperliquid-eth-usdc",
    "side": "long",
    "type": "limit",
    "size": "0.5",
    "limitPrice": 3900,
    "leverage": 10,
    "margin": 195.0,
    "reduceOnly": false,
    "createdAt": 1733332000,
    "pendingActions": [
      {
        "type": "cancelOrder",
        "label": "Cancel Order",
        "args": { "orderId": "abc123" }
      }
    ]
  }
]

Account balance

POST /v1/balances

Request:

{ "address": "0x1234...", "providerId": "hyperliquid" }

Response (BalanceDto):

{
  "providerId": "hyperliquid",
  "collateral": {
    "symbol": "USDC",
    "network": "hyperliquid",
    "decimals": 6
  },
  "accountValue": 1280.44,
  "usedMargin": 320.13,
  "availableBalance": 960.31,
  "unrealizedPnl": 12.8
}

Events & Activity

These endpoints surface async venue outcomes that aren't directly tied to a single user-initiated action — fills, liquidations, and TP/SL triggers — plus a unified chronological feed.

List perp events

GET /v1/events?address=0x...&providerId=hyperliquid&limit=20&offset=0

Optional filters: eventType / eventTypes (order_filled, liquidation, stop_loss_triggered, take_profit_triggered), marketId, perpActionId, providerOrderId, fromDate, toDate (ISO 8601).

Each item in items[] is an EventDto:

{
  "id": "8c1e7a6b-4d2c-4f95-9c0e-2b9d9c8e1234",
  "eventType": "order_filled",
  "providerId": "hyperliquid",
  "occurredAt": "2026-04-23T07:52:05.187Z",
  "marketId": "hyperliquid-eth-usdc",
  "perpActionId": "550e8400-e29b-41d4-a716-446655440000",
  "providerOrderId": "394438950581",
  "order": {
    "orderId": "394438950581",
    "marketId": "hyperliquid-eth-usdc",
    "asset": "ETH",
    "side": "buy",
    "type": "market",
    "originalSizeBase": "0.0063",
    "remainingSizeBase": "0.0",
    "limitPrice": 2395.2,
    "timeInForce": "ioc",
    "reduceOnly": false,
    "isPositionLevel": false,
    "clientOrderId": null,
    "childOrderIds": [],
    "createdAt": "2026-04-23T07:52:05.187Z"
  }
}

Get a single event

GET /v1/events/{eventId}

Returns the same EventDto shape.

Unified activity feed

GET /v1/activity?address=0x...&providerId=hyperliquid&limit=20&offset=0

Paginated, chronological mix of timeline events (occurredAt) and user actions (createdAt), newest first. Each item is either an EventDto or an ActionDto.


Example Flow

  1. Discover marketsGET /v1/markets?providerId=hyperliquid
  2. Open a positionPOST /v1/actions (action: "open")
  3. Sign each transaction in ActionDto.transactions[] using its signingFormat and signablePayload
  4. SubmitPOST /v1/transactions/{transactionId}/submit for each
  5. PollGET /v1/actions/{actionId} until status is SUCCESS / FAILED
  6. Read updated statePOST /v1/positions, POST /v1/balances

Cross-Chain Orchestration

For providers and actions that support it (e.g. fund on Hyperliquid):

  • Accepts arbitrary input assets via args.fromToken
  • Computes required swap, bridge, or deposit operations
  • Returns all unsigned transactions in the correct sequence inside ActionDto.transactions[]

For Hyperliquid specifically, args.fundingMethod selects the route:

ValueBehavior
bridge2Direct Hyperliquid Bridge2 (Arbitrum USDC only)
lifiLiFi bridging (any token / network)
omittedbridge2 for Arbitrum USDC, lifi otherwise

args.skipApproval: true skips the ERC-20 approval transaction when the spender is already approved.

Some providers may have restricted funding support; consult GET /v1/providers/{providerId} for the actions and arguments each provider supports.


Integration Options

Direct API Integration

Endpoint summary:

GET    /v1/providers
GET    /v1/providers/{providerId}
GET    /v1/markets
GET    /v1/markets/{marketId}
GET    /v1/markets/{marketId}/candles
POST   /v1/positions
POST   /v1/orders
POST   /v1/balances
POST   /v1/actions
GET    /v1/actions
GET    /v1/actions/{id}
POST   /v1/transactions/{transactionId}/submit
GET    /v1/events
GET    /v1/events/{id}
GET    /v1/activity
GET    /v1/health

PerpsKit Widget Integration

The Yield.xyz Perps Widget is a drop-in perpetual futures trading frontend module you can embed in any application to provide users a self-custodial perps trading experience. It's built with React and designed to feel native inside wallets, exchanges, and fintech apps.

Under the hood, the widget uses the PerpsKit API to power the full lifecycle:

  • Discovers available providers and supported actions
  • Retrieves markets, live pricing data, candles, and the user's portfolio state
  • Constructs intent-based actions (open/close/manage positions, update orders, leverage adjustments, TP/SL)
  • Returns unsigned transaction payloads for the user to sign
  • Submits signed transactions for broadcast and execution
  • Streams async venue outcomes via the events / activity feed

Example deployments

Embedded Trading Widget — A lightweight widget that can be embedded directly inside an existing product interface (wallets, exchanges, fintech apps). Provides users with a seamless trading experience without leaving the host application.

Full Trading Terminal Dashboard — A full-featured trading dashboard built to mirror the Hyperliquid terminal experience. Includes richer position management, portfolio analytics, and a professional layout optimized for desktop traders.


Monetization via Builder Codes

PerpsKit supports builder code integration on supported perps protocols, allowing clients to earn a configurable facilitation fee per trade. The markup is transparently included in displayed fees and paid out via each protocol's builder-code mechanism.

Hyperliquid

  • Fees can be set up to 0.1% (10 bp) on perps.
  • Builder fees are authorized via the approveBuilderFee action by the user on first interaction.
  • Once authorized, subsequent order actions sent on behalf of the user may include the builder parameter {"b": address, "f": number}:
    • b — fee recipient address
    • f — builder fee in tenths of basis points (e.g. f: 10 = 1 bp = 0.01%)

How builder fees are calculated

Key facts:

  • Charged per fill — applies to opens, closes, and partial executions.
  • Leverage does not affect the fee. It is calculated on executed notional, not user margin.
  • Collected in the collateral asset (USDC). Multiple fills are charged independently.

Example. A user deposits $1,000 in collateral and opens a perp at 10x leverage = $10,000 notional. With a 1 bp builder fee filled in a single fill:

$10,000 × 0.01% = $1.00

If the user later closes at $12,000 notional, an additional $1.20 builder fee is charged on the close. A partial close of $4,000 notional charges $0.40 for that fill.