PearPear
API IntegrationTrade Activity

Execution

Track what an action actually did, and read the venue orders it placed.

An execution is one run of an action at the venue: an open, an adjust, a close, or a reverse. It holds the venue orders that run placed, one per leg. Because trading calls are asynchronous, the execution is where you find out what really happened.

An execution is not an open order. An open order is an instruction still waiting to fire; an execution is what exists once something fires. A trigger, a schedule, or a ladder rung produces an execution each time it runs, and a market order produces one with no instruction step at all. See Orders and Executions.

EndpointReturns
GET /executionsExecutions, newest first, with cursor pagination.
GET /executions/{id}One execution, including per-order status and error.

GET /executions/{id} is scoped to the trade account and its venue. An execution id belonging to another venue reads as absent, so you get a 404.

There is no separate orders endpoint. Orders live inside their execution, in the orders array.

Read one execution

Every trading call returns an execution id. Use it to reconcile:

curl "https://pro-gateway.pearprotocol.io/executions/$EXECUTION_ID" \
  -H "x-api-key: $PEAR_API_KEY" \
  -H "x-trade-account-id: $TRADE_ACCOUNT_ID"
{
  "execution": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "connector": "hyperliquid",
    "intent": "OPEN",
    "orderType": "MARKET",
    "status": "COMPLETED",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:01.310Z",
    "legs": [
      { "symbol": "0", "side": "BUY",  "mode": "USD", "amount": "500", "reduceOnly": false },
      { "symbol": "1", "side": "SELL", "mode": "USD", "amount": "500", "reduceOnly": false }
    ],
    "orders": [
      {
        "id": "8d2e5b91-0c44-4a7e-9f31-6b0d5e2a1c88", "type": "MARKET", "connector": "hyperliquid",
        "symbol": "0", "side": "BUY", "reduceOnly": false,
        "status": "FILLED", "quantity": "0.0052", "price": "96150.5",
        "filledQuantity": "0.0052", "filledPrice": "96150.5",
        "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:01.310Z"
      },
      {
        "id": "1c47a2f0-9db3-4e18-8c60-72a4f9e05b13", "type": "MARKET", "connector": "hyperliquid",
        "symbol": "1", "side": "SELL", "reduceOnly": false,
        "status": "FILLED", "quantity": "0.1620", "price": "3086.2",
        "filledQuantity": "0.1620", "filledPrice": "3086.2",
        "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:01.310Z"
      }
    ]
  }
}

legs is what you asked for. orders is what Pear sent to the venue. Comparing them tells you whether the basket landed as intended.

price is the price the order was sent at; filledPrice is what it actually filled at, and it is absent until something fills.

What an execution names

The shape depends on intent:

intentCarries legsCarries position
OPENYesNo
ADJUSTYesYes
CLOSENoYes
REVERSENoYes

position is { id, exposure }: the position the execution acted on, and the exposure it held. It is the only field that correlates an execution to a position.

An OPEN execution does not name the position it created. Nothing on the response links the two. Read GET /positions once the execution completes and match on createdAt and basket. GET /tca/executions/{executionId} does return the positionId in its executionContext, but it needs the pro role. See Transaction Cost Analysis.

orders is itself optional. It is absent until Pear has sent anything to the venue.

Execution status

statusMeaning
IDLEAccepted, not started. This is the status a trading call returns.
ACTIVEOrders are working at the venue.
COMPLETEDThe action finished.
FAILEDThe action failed. error carries the reason.
CANCELLEDThe action was cancelled before it finished.

intent is OPEN, CLOSE, ADJUST, or REVERSE, and orderType is MARKET.

Order status

statusMeaning
PENDINGSent, nothing filled yet.
PARTIALSome quantity filled. Compare filledQuantity with quantity.
FILLEDFully filled at filledPrice.
CANCELLEDCancelled before filling.
REJECTEDThe venue refused it. error and errorCode carry the venue's reason.

symbol is the instrument ID, not the ticker. See Instrument ID.

A partly filled basket

An execution can fail with some legs already on the book. That is the case to handle before you place anything else:

{
  "execution": {
    "status": "FAILED",
    "error": "Insufficient margin",
    "orders": [
      { "id": "8d2e…", "symbol": "0", "side": "BUY",  "status": "FILLED",   "filledQuantity": "0.0052" },
      { "id": "1c47…", "symbol": "1", "side": "SELL", "status": "REJECTED", "errorCode": "MARGIN" }
    ]
  }
}

The BTC leg filled and the ETH leg did not, so the account now holds a one-sided position. Read GET /positions and decide, rather than retrying the original call. See Error Handling.

List executions

curl "https://pro-gateway.pearprotocol.io/executions?status=COMPLETED&limit=50" \
  -H "x-api-key: $PEAR_API_KEY" \
  -H "x-trade-account-id: $TRADE_ACCOUNT_ID"

status accepts only ACTIVE, COMPLETED, and CANCELLED, as a comma-separated list. These are not the values the response reports. Any other value, FAILED included, is a 400.

Query status=Response status
(omitted)Every status, including IDLE and FAILED.
ACTIVEACTIVE
COMPLETEDCOMPLETED and FAILED
CANCELLEDCANCELLED

So a FAILED execution comes back under status=COMPLETED, and filtering the response on the value you queried finds nothing. To read only failures, ask for status=COMPLETED and filter the response on status === "FAILED" yourself.

Omitting status is the only way to select IDLE rows from the list.

connector is accepted but ignored. The venue comes from the trade account in x-trade-account-id.

The default list hides schedule and trigger runs

Every execution records what created it. A direct call leaves both empty; a TWAP slice names its schedule; a fired trigger names its trigger.

By default the list returns only executions with neither, which is to say only the actions you called directly. Two flags bring the rest back:

QueryReturns
(default)Direct calls only: your opens, adjusts, closes, and reverses.
includeSchedule=trueAlso the slices a TWAP schedule ran.
includeTriggers=trueAlso the runs a fired trigger produced, including ladder rungs.

This is a filter, not a join: the flags do not attach the schedule or trigger object, they stop excluding the executions those produced. The default exists because one twelve-slice TWAP would otherwise bury every other action on the account.

includeSchedule=false does not turn the flag off. Both flags read the string "false" as true, so passing it has the same effect as passing true. Omit the parameter to keep the default.

An execution you hold an id for stays readable by id whatever the list is filtering.

Live updates

Subscribe to user_events on the WebSocket for execution.completed and execution.failed, instead of polling. The fills an execution produced are at Fills; what it cost against the arrival price is at GET /tca/executions/{executionId}.

On this page