Skip to content

Place Orders

Place one or more orders on Synthetix's orderbook. This endpoint always accepts an array of orders for consistency, even when placing a single order.

Endpoint

POST https://papi.synthetix.io/v1/trade

Request

Request Format

{
  "params": {
    "action": "placeOrders",
    "subAccountId": "1867542890123456789",
    "orders": [
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "limitGtc",
        "price": "50000.00",
        "triggerPrice": "",
        "quantity": "0.1",
        "reduceOnly": false,
        "postOnly": false,
        "isTriggerMarket": false,
        "closePosition": false,
        "clientOrderId": "0x1234567890abcdef1234567890abcdef"
      }
    ],
    "grouping": "na",
    "source": "direct"
  },
  "nonce": 1704067200000,
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  },
  "expiresAfter": 1704067300
}

Request Parameters

Action Parameters

ParameterTypeRequiredDescription
paramsobjectYesParameters object containing method details
params.actionstringYesMust be "placeOrders"
params.subAccountIdstringYesSubaccount identifier
params.ordersarrayYesArray of order objects (minimum 1 order)
params.groupingstringNoOrder grouping: "na", "normalTpsl", "positionTpsl", "twap"
params.sourcestringNoRequest source identifier for tracking and rebates (max 100 characters). Use "direct" for generic integrations

Common Request Parameters

ParameterTypeRequiredDescription
nonceintegerYesPositive integer, incrementing nonce
signatureobjectYesEIP-712 signature object
expiresAfterintegerNoExpiration timestamp (5x rate limit on stale cancels)

Order Object

ParameterTypeRequiredDescription
symbolstringYesMarket symbol (e.g., "BTC-USDT", "ETH-USDT")
sidestringYes"buy" or "sell"
orderTypestringYesOne of: limitGtc, limitIoc, limitAlo, limitGtd, market, triggerSl, triggerTp, twap
pricestringYesPrice as string. Use empty string when not applicable (market orders and trigger market)
quantitystringYesOrder size as string
reduceOnlybooleanYesOnly reduce existing position
postOnlybooleanConditionalWhether order must be maker (no immediate match). Only used for limitGtc and limitGtd orders. Must be false for all other order types
triggerPricestringYesTrigger price as string. Required for triggerSl and triggerTp; empty string otherwise
isTriggerMarketbooleanYesExecution type for trigger orders. true for market-on-trigger, false for limit-on-trigger. Must be false for non-trigger types
closePositionbooleanYesClose entire position when triggered (TP/SL orders only). Must be false for non-trigger types
clientOrderIdstringNo128-bit hex string (0x + 32 hex chars)
triggerPriceTypestringConditionalPrice type used for trigger evaluation: "mark" or "last". Defaults to "mark" when omitted. Applies to triggerSl and triggerTp orders only
expiresAtintegerConditionalExpiration timestamp in Unix seconds. Required for limitGtd orders; must be 10 seconds to 24 hours in the future. Not valid for other order types
durationSecondsintegerConditionalTotal TWAP execution window in seconds. Required for twap orders only. Must be between 300 (5 minutes) and 86400 (24 hours)
intervalSecondsintegerNoInterval between TWAP child chunks in seconds. Applies to twap orders only. Must be one of 10, 30, 60, 120, 180, 300, 600. Omit or set to 0 to use the default of 30
intervalVarianceBpsintegerNoRandomization of child order timing intervals. Applies to twap orders only. Multiples of 100 bps, capped at 2000 bps (20%). Default 0 (no variance)
sizeVarianceBpsintegerNoRandomization of child order sizes. Applies to twap orders only. Multiples of 100 bps, capped at 2000 bps (20%). Default 0 (no variance)

Note on postOnly: The postOnly field is a request parameter but is not included in the EIP-712 signed Order type. It is processed server-side after signature verification. The EIP-712 Order type contains: symbol, side, quantity, orderType, price, triggerPrice, reduceOnly, isTriggerMarket, clientOrderId, closePosition.

Order Type Enum and Rules

  • limitGtc: price required; isTriggerMarket=false; triggerPrice=""
  • limitIoc: price required; isTriggerMarket=false; triggerPrice=""
  • limitAlo: price required; isTriggerMarket=false; triggerPrice=""
  • limitGtd: price required; isTriggerMarket=false; triggerPrice=""; expiresAt required (10s–24h in the future); postOnly accepted
  • market: price=""; isTriggerMarket=false; triggerPrice=""
  • triggerSl: triggerPrice required; isTriggerMarket determines execution; if true then price="", if false then price required; optional triggerPriceType ("mark" or "last", defaults to "mark")
  • triggerTp: same rules as triggerSl
  • twap: Time-Weighted Average Price order. triggerPrice=""; expiresAt must be absent; durationSeconds required (300–86400); optional intervalSeconds from the allowed set (10, 30, 60, 120, 180, 300, 600, default 30); optional price sets a limit price, omit for no limit; exactly one order per request; use grouping: "twap". See TWAP Orders for full details.

Examples

Limit GTC

{
  "orderType": "limitGtc",
  "price": "50000.00",
  "triggerPrice": "",
  "isTriggerMarket": false,
  "postOnly": false,
  "closePosition": false
}

Limit GTC (Post-Only)

{
  "orderType": "limitGtc",
  "price": "50000.00",
  "triggerPrice": "",
  "isTriggerMarket": false,
  "postOnly": true,
  "closePosition": false
}

Limit GTD (Good Till Date)

{
  "orderType": "limitGtd",
  "price": "50000.00",
  "triggerPrice": "",
  "isTriggerMarket": false,
  "postOnly": false,
  "closePosition": false,
  "expiresAt": 1704070800
}

Market

{
  "orderType": "market",
  "price": "",
  "triggerPrice": "",
  "isTriggerMarket": false,
  "closePosition": false
}

Trigger SL (Market)

{
  "orderType": "triggerSl",
  "price": "",
  "triggerPrice": "50000",
  "isTriggerMarket": true,
  "closePosition": true
}

Trigger TP (Limit)

{
  "orderType": "triggerTp",
  "price": "49950.00",
  "triggerPrice": "50000.00",
  "isTriggerMarket": false,
  "closePosition": true
}

TWAP

{
  "orderType": "twap",
  "symbol": "BTC-USDT",
  "side": "buy",
  "quantity": "1.5",
  "price": "",
  "triggerPrice": "",
  "isTriggerMarket": false,
  "reduceOnly": false,
  "closePosition": false,
  "durationSeconds": 3600,
  "intervalSeconds": 60
}

Time in Force (TIF) Options

ValueNameDescription
gtcGood Till CanceledOrder remains active until filled or canceled
gtdGood Till DateOrder active until filled, canceled, or expiresAt timestamp reached
iocImmediate or CancelFill immediately available quantity, cancel remainder
postOnlyPost-OnlySet postOnly: true on limitGtc orders to ensure maker-only execution

Response

Response Structure

FieldTypeDescription
statusstring"ok" for processed requests or "error" for request-level failures
responseobjectContains operation results (omitted when status is "error")
response.statusesarrayArray of status objects, one per order submitted (check each for success/error)
errorobjectError details (only present when status is "error")
requestIdstringRequest tracking identifier
traceIdstringTrace ID for debugging
timestampintegerUnix milliseconds timestamp

Order Status Types

Each order in the request receives one status response:

Status TypeDescriptionFields
restingOrder placed in orderbookorder (canonical), id (deprecated), expiresAt (optional, Unix ms, present for limitGtd orders)
filledOrder completely filledorder (canonical), id (deprecated), avgPrice, totalSize, expiresAt (optional, Unix ms)
canceledOrder was canceledorder (canonical), id (deprecated)
errorOrder rejectederror (message string), errorCode (machine-readable code)

Success Response Examples

Single Order - Resting

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "resting": {
          "order": {
            "venueId": "1948058938469519360",
            "clientId": "cli-1948058938469519360"
          },
          "id": "1948058938469519360"
        }
      }
    ]
  },
  "requestId": "5ccf215d37e3ae6d"
}

Single Order - Filled

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "filled": {
          "order": {
            "venueId": "1948058938469519361",
            "clientId": "cli-1948058938469519361"
          },
          "id": "1948058938469519361",
          "avgPrice": "50001.25",
          "totalSize": "0.1"
        }
      }
    ]
  },
  "requestId": "5ccf215d37e3ae6d"
}

Single Order - Canceled

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "canceled": {
          "order": {
            "venueId": "1948058938469519362",
            "clientId": "cli-1948058938469519362"
          },
          "id": "1948058938469519362"
        }
      }
    ]
  },
  "requestId": "5ccf215d37e3ae6d"
}

Multiple Orders - Mixed Results (Partial Success)

Batch requests can have mixed results where some orders succeed and others fail. The response still has status: "ok" because the request was processed successfully—check each item in the statuses array:

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "resting": {
          "order": {
            "venueId": "1948058938469519360",
            "clientId": "cli-1948058938469519360"
          },
          "id": "1948058938469519360"
        }
      },
      {
        "filled": {
          "order": {
            "venueId": "1948058938469519361",
            "clientId": "cli-1948058938469519361"
          },
          "id": "1948058938469519361",
          "avgPrice": "2999.50",
          "totalSize": "1.0"
        }
      },
      {
        "error": "insufficient margin: additional needed 500.00, available 100.12",
        "errorCode": "INSUFFICIENT_MARGIN",
        "order": { "venueId": null, "clientId": "cli-1948058938469519362" }
      }
    ]
  },
  "requestId": "5ccf215d37e3ae6d",
  "traceId": "abc123def456",
  "timestamp": 1704067200000
}

See Batch Request Error Handling for details on handling partial success scenarios.

Migration Note: Use *.order.venueId as canonical in each status payload. Legacy id remains for compatibility.

Error Response

Request-level errors return an error status. For per-item errors in batch requests, see Batch Request Error Handling.

{
  "status": "error",
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid signature",
    "category": "AUTH",
    "retryable": false
  },
  "requestId": "5ccf215d37e3ae6d",
  "traceId": "abc123def456",
  "timestamp": 1704067200000
}

Examples

Place Single Limit Order (GTC)

{
  "params": {
    "action": "placeOrders",
    "subAccountId": "1867542890123456789",
    "orders": [
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "limitGtc",
        "price": "50000.00",
        "triggerPrice": "",
        "quantity": "0.1",
        "reduceOnly": false,
        "postOnly": false,
        "isTriggerMarket": false,
        "closePosition": false
      }
    ]
  },
  "nonce": 1704067200000,
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  }
}

Place Single Limit GTD Order

{
  "params": {
    "action": "placeOrders",
    "subAccountId": "1867542890123456789",
    "orders": [
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "limitGtd",
        "price": "50000.00",
        "triggerPrice": "",
        "quantity": "0.1",
        "reduceOnly": false,
        "postOnly": false,
        "isTriggerMarket": false,
        "closePosition": false,
        "expiresAt": 1704070800
      }
    ]
  },
  "nonce": 1704067200000,
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  }
}

Place Multiple Orders (Batch) — Mixed types

{
  "params": {
    "action": "placeOrders",
    "subAccountId": "1867542890123456789",
    "orders": [
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "limitGtc",
        "price": "49000.00",
        "triggerPrice": "",
        "quantity": "0.05",
        "reduceOnly": false,
        "postOnly": false,
        "isTriggerMarket": false,
        "closePosition": false
      },
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "limitGtc",
        "price": "48000.00",
        "triggerPrice": "",
        "quantity": "0.1",
        "reduceOnly": false,
        "postOnly": false,
        "isTriggerMarket": false,
        "closePosition": false
      },
      {
        "symbol": "BTC-USDT",
        "side": "sell",
        "quantity": "0.15",
        "reduceOnly": true,
        "orderType": "triggerSl",
        "price": "",
        "triggerPrice": "45000",
        "isTriggerMarket": true,
        "closePosition": false
      }
    ],
    "grouping": "na"
  },
  "nonce": 1704067200000,
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  }
}

:::info Rate Limiting This batch contains 3 orders, so it consumes 12 tokens (3 × 4) from your per-subaccount bucket. See Rate Limits for details. :::

TWAP Orders

TWAP (Time-Weighted Average Price) orders split a large order into equal-sized child chunks that are submitted at fixed intervals over a chosen duration. Use TWAPs to reduce market impact when entering or exiting large positions.

A TWAP order is placed via the same placeOrders action with grouping: "twap" and orderType: "twap".

TWAP Request

{
  "params": {
    "action": "placeOrders",
    "subAccountId": "1867542890123456789",
    "grouping": "twap",
    "orders": [
      {
        "symbol": "BTC-USDT",
        "side": "buy",
        "orderType": "twap",
        "quantity": "1.5",
        "price": "",
        "triggerPrice": "",
        "isTriggerMarket": false,
        "reduceOnly": false,
        "closePosition": false,
        "durationSeconds": 3600,
        "intervalSeconds": 60,
        "sizeVarianceBps": 2000,
        "intervalVarianceBps": 1000
      }
    ]
  },
  "nonce": 1704067200000,
  "signature": {
    "v": 28,
    "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
  },
  "expiresAfter": 1704067300
}

TWAP Order Fields

All standard order fields apply. The TWAP-specific fields are:

FieldTypeRequiredDescription
orderTypestringYesMust be "twap"
durationSecondsintegerYesTotal execution window in seconds. Must be between 300 (5 minutes) and 86400 (24 hours)
intervalSecondsintegerNoInterval between chunks in seconds. Must be one of 10, 30, 60, 120, 180, 300, 600. Omit or set to 0 to use the default of 30
intervalVarianceBpsintegerNoRandomization of child order timing intervals. Multiples of 100 bps, capped at 2000 bps (20%). Default 0 (no variance)
sizeVarianceBpsintegerNoRandomization of child order sizes. Multiples of 100 bps, capped at 2000 bps (20%). Default 0 (no variance)
quantitystringYesTotal quantity across all chunks
pricestringNoOptional limit price; chunks are skipped for any interval where the chunk would fill outside this limit. Omit (empty string) for no limit
sidestringYes"buy" or "sell"
symbolstringYesMarket symbol
reduceOnlybooleanNoStandard reduce-only flag
clientOrderIdstringNoStandard client order ID

Fields that must not be set on a TWAP order (validation fails otherwise):

  • triggerPrice — must be empty
  • expiresAt — must be absent

Allowed intervalSeconds Values

10, 30, 60, 120, 180, 300, 600.

Any other value returns HTTP 400 with TWAP interval invalid, allowed=[10 30 60 120 180 300 600]. When intervalSeconds is 0 or omitted, the server defaults to 30.

TWAP Validation Rules

All rules are enforced server-side and return HTTP 400 on failure:

RuleError message
durationSeconds ≥ 300TWAP duration must be at least 5 minutes
durationSeconds ≤ 86400TWAP duration exceeds maximum of 24 hours
intervalSeconds in allowed setTWAP interval invalid, allowed=[10 30 60 120 180 300 600]
Division produces ≥ 2 chunksTWAP duration must produce at least two chunks
quantity > 0TWAP total quantity must be greater than zero
price limit non-negative if setTWAP price limit must be positive
Total notional ≥ $10,000 USDTWAP total order size must be at least $10,000 USD equivalent
Exactly one order in orders arrayHTTP 400
sizeVarianceBps / intervalVarianceBps multiples of 100HTTP 400
sizeVarianceBps / intervalVarianceBps ≤ 2000HTTP 400

The notional check uses the current mark price. If the mark price is temporarily unavailable, the request is rejected with mark price not available for TWAP min order size validation.

TWAP child orders are scheduled deterministically using the parent venueOrderId as a seed. When variance fields are set, child order sizes and/or timing intervals vary within the configured bounds.

TWAP Response

On success, the TWAP parent order is accepted and rests in the open orders table immediately. Child-chunk fills arrive asynchronously as the TWAP progresses through each interval. The parent's execution progress can be inspected via getOpenOrders (see the twapDetails object) while the TWAP is active, and via getOrdersHistory once it completes or is cancelled.

TWAP Signing

TWAP orders use the same PlaceOrders EIP-712 primary type as all other placeOrders requests — there is no separate TWAP action type or signing domain. In the signed payload, set grouping to "twap".

durationSeconds, intervalSeconds, sizeVarianceBps, and intervalVarianceBps are passed in the HTTP params layer and are not included in the EIP-712 signature. Only the base Order fields and the top-level grouping, subAccountId, nonce, and expiresAfter are signed.

Chunk Execution Behaviour

For reference when building monitoring or progress UIs:

  • The TWAP is divided into floor(durationSeconds / intervalSeconds) equal chunks
  • Each chunk's quantity is totalQuantity / numChunks, truncated to market quantity precision. The final chunk absorbs any remainder from truncation
  • Chunks are submitted as market orders at each interval. If a price limit is set, chunks that would fill outside the limit are skipped for that interval
  • If the minimum chunk size computes to zero (extreme precision edge case), the TWAP collapses to a single chunk covering the full quantity
  • Progress is reported in real time via the twapDetails object on Get Open Orders

Cancelling a TWAP

There is no separate cancel-TWAP endpoint. Use the existing cancelOrders action with the TWAP parent's venue order ID or client order ID, or use cancelAllOrders. Cancelling a TWAP parent automatically cancels any in-flight child chunk orders; the parent transitions to Cancelled status.

Implementation Notes

  • Order nonces must be positive integers, incrementing and unique per request
  • Client order IDs enable request tracking with 128-bit hex format (0x prefix + 32 hex characters)
  • Limit orders require price specification, trigger orders require trigger price
  • Batch operations process orders independently with individual status responses
  • Order grouping types: "na" (none), "normalTpsl" (stop-loss/take-profit), "positionTpsl" (position-based), "twap" (TWAP execution)
  • All price and quantity values must be provided as strings for precision preservation

Linked TP/SL sibling cancellation

When a take-profit or stop-loss order in a linked normalTpsl or multipleTpsl pair fires and publishes to matching, the platform automatically cancels the sibling order and creates an explicit Cancelled history entry for it:

  • The filled leg (TP or SL) appears in getOrderHistory with status: "Filled" as expected.
  • The auto-cancelled sibling now appears in getOrderHistory with status: "Cancelled" and a populated cancelledAt timestamp.

This applies to both intra-pair and cross-pair siblings within multipleTpsl groupings. Locally cancelled conditional orders (e.g., due to insufficient position size) continue to emit history only for the triggered order itself.

Error Handling

Common Errors

Error CodeDescriptionSolution
INSUFFICIENT_MARGINAccount lacks required marginAdd collateral or reduce order size
COLLATERAL_PRICE_STALECollateral price exceeds staleness threshold; order would increase exposureWait for fresh collateral prices or cancel the order attempt; do not retry immediately
REQUEST_EXPIREDexpiresAfter timestamp has passedUse longer expiration or remove
INVALID_VALUEMarket symbol not recognizedCheck supported markets
QUANTITY_TOO_SMALLBelow minimum order sizeCheck market minimums
POST_ONLY_WOULD_TRADEPost-only order would take liquidityAdjust price or use different TIF
MAX_ORDERS_EXCEEDEDMaximum order limit reachedCancel existing orders first
REDUCE_ONLY_NO_POSITIONNo position for reduce-only orderRemove reduce-only flag
MARKET_CLOSEDMarket is not open for tradingWait for market to open

Batch Order Errors

For batch orders, each order is validated and processed independently:

  • Request-level errors (invalid signature, rate limit) fail the entire batch with an error response
  • Per-item errors (insufficient margin, invalid price) return HTTP 200 with individual error statuses
  • The statuses array preserves the order of your request—index 0 corresponds to your first order
  • Successfully processed orders are not rolled back if other orders fail

See Batch Request Error Handling for complete details and code examples.

Rate Limiting

  • Each order in the batch consumes base cost - A request with 5 orders consumes 5 × 4 = 20 tokens from your per-subaccount bucket
  • Bucket size: 1,000 tokens per 10 seconds per subaccount (see Rate Limits)
  • Failed orders due to validation errors still count against rate limits
  • See Rate Limits for complete details

Next Steps