Synthetix API trading within request limits
Synthetix supports automated perpetual futures trading through signed API requests for an authorized trading subaccount. REST handles discrete requests, while WebSocket supports trading messages and streaming updates. Order placement uses EIP-712 signatures, and request pacing must respect the applicable IP and subaccount limits. Those limits use weighted budgets, so a batch containing more orders consumes more capacity. An accepted request also needs interpretation: an order may rest on the book, fill, or receive an individual rejection. The public getIsWhitelisted query reports whether a wallet has permission to place orders. Market rules and collateral-backed margin remain relevant even when the client has spare request capacity.
The short version: Synthetix API clients must budget weighted requests across shared IP capacity and subaccount limits, while tracking each order’s actual outcome.
Signed submissions and rate-limit recovery
An authorized placeOrders request submits an order array; a rate-limit rejection requires a delay before another attempt. For a successfully processed batch, the API reports an outcome for each submitted order, including resting or filled states. HTTP 429 with RATE_LIMIT_EXCEEDED calls for exponential backoff and reduced request pressure. A malformed payload or unauthorized signer needs correction, because waiting alone does not repair those conditions. A transport timeout leaves a different uncertainty: the missing response does not establish whether the requested operation completed.
Signing domains, nonces, and subaccount permissions
EIP-712 signing authenticates a structured message whose fields and domain must match the selected API action. The mainnet domain identifies Synthetix and Ethereum Mainnet, including the configured domain version and verifying-contract value. Order placement and cancellation have their own typed structures. For order placement, postOnly appears in the request payload but does not belong to the signed Order type. A cancellation that targets venue IDs uses a different primary type from one that targets client order IDs.
Order placement and cancellation require a positive, unique, increasing nonce within the applicable signer and subaccount context. A nonce distinguishes a new request from a replay of an earlier signed operation. Parallel workers using the same context need coordinated nonce generation and submission ordering. Generating unique values alone does not prevent a lower nonce from arriving after a higher one. A restart must not cause subsequent nonces to move backward.
Authenticated reads using SubAccountAction, including getOpenOrders and getPositions, require signatures but do not require a nonce.
Authorization follows the signing address and its permissions for the specified subaccount. A delegate needs the relevant permission before sending account actions. Possessing a subaccount identifier grants no authority over it. Private keys belong in the signing component, outside request logs and public client code.
The optional expiresAfter field bounds request validity when set to a nonzero expiration timestamp. Clock drift or time spent waiting in a submission queue can produce an expired request.
Weighted request budgets and live snapshots
Weighted token budgets measure API load by action cost, so request counts alone cannot describe available capacity.
A placeOrders batch consumes tokens in proportion to its number of orders.
Each bucket refills over time. Public info and status actions consume the IP budget, while trade actions also use the subaccount budget. Multiple clients or WebSocket connections behind the same IP compete for that shared capacity. The applicable fee tier determines the subaccount budget. Moving requests to another transport does not remove these limits.
Here, a token is an accounting unit inside the rate limiter.
Trade response envelopes can include rateLimit and ipRateLimit snapshots when the corresponding limiter runs. requestsCap identifies capacity, and remainingTokens reports available tokens. Successful responses normally reflect the debit for that request. On a 429 rejection, available snapshots can show pre-debit values, so a positive balance does not override the rejection. getRateLimits provides a usage query that consumes budget itself. The Python SDK’s last_rate_limits property reads cached response snapshots without making another request. An omitted snapshot leaves the SDK’s previous value intact, which makes its age relevant to scheduling.
Market eligibility and open-order constraints
Market settings, available margin from collateral, and open-order caps can prevent admission even when request tokens remain. getMarkets exposes price increments, quantity steps, minimum order sizes, and trading-status flags. These constraints apply to the selected market.
Each subaccount has separate per-market and total open-order caps.
Resting limit orders and conditional orders count toward these caps; reduce-only orders and engine liquidation orders do not. getTiers supplies their configurable values. An order-cap rejection requires a change in occupied slots or the applicable tier.
How can automation preserve the option to cancel an unfilled order?
A client can cancel the remaining quantity of an open or partially filled order with valid authorization. For automation that needs this flexibility, a funded, authorized subaccount and a compatible EIP-712 signer are prerequisites.
-
With valid market inputs, a
limitGtcorder withpostOnlyset totruerequests maker-only execution. The engine rejects it if it would take liquidity immediately. - Without signing permission for the subaccount, public market-data access remains available, while live account actions require authorization.
-
If private streaming is unavailable, signed REST queries through
getOpenOrdersprovide an alternative view of active orders. -
For an order that remains open,
cancelOrdersaccepts either venue IDs or client IDs, with the corresponding EIP-712 type. - After a timeout or an unclear cancellation response, order history and fills need reconciliation before a replacement instruction.
A per-order canceled status identifies the canceled order and confirms removal of its unfilled remainder. Executed quantities remain trades. Absence from getOpenOrders alone does not establish cancellation, because that query excludes filled, canceled, and rejected orders.
Streaming updates and REST reconciliation
WebSocket subscriptions deliver account and market updates, while REST queries provide snapshots and recorded executions for reconciliation. Private order and position subscriptions require authenticated access; public market subscriptions use the info connection. Trade WebSocket authentication establishes access to the connection, and individual trading actions still need their own signatures. Events can describe order updates, while getTrades returns executed fills for the requested subaccount. The execution price and filled quantity describe trades that actually occurred.
A heartbeat confirms that the connection responds. It supplies no confirmation of an order fill.
Connection management needs heartbeat monitoring, timeouts, and reconnection behavior. The trade connection needs authentication again after a reconnect. An active scheduleCancel dead-man switch cancels the subaccount’s open orders if the client does not reset its timer within the configured timeout. Historical queries and open-order queries answer different questions, and pagination matters when a response covers only part of a record set. A client that records identifiers and request outcomes can reconcile those records with later order and fill data. REST suits integrations that prefer individual responses and occasional snapshots. WebSocket suits continuous subscriptions, provided the integration also manages session interruptions and signed trading messages.
Synthetix: common questions
Do public market-data queries require a private key?
Public market-data queries do not require a private key or an EIP-712 signature. Account-specific order, position, and trade queries use authenticated access. A read-only market-data integration still consumes its applicable IP budget, so anonymous access does not provide unrestricted polling capacity.
Does expiresAfter remove an order that is already resting?
A nonzero expiresAfter value limits the validity of a signed request, not the lifetime of an accepted resting order. A limitGtd order uses the separate expiresAt field to define its order deadline. Request expiration therefore cannot replace an order cancellation or a supported order-expiration setting.
Why does an order batch show success when one item failed?
A successful request envelope means that the API processed the batch, while each order has its own outcome. The statuses array follows the request’s item order. An errorCode identifies an individual rejection, and successful items remain effective when another item fails. HTTP 200 alone cannot establish that every order succeeded.
What format does clientOrderId accept?
The optional clientOrderId field accepts strings of up to 255 characters using ASCII letters, digits, and.-/+_=. A 128-bit identifier written as 0x followed by 32 hexadecimal characters is one supported format. Leading or trailing whitespace causes an error. It identifies the client’s order and differs from the exchange’s venue identifier. Cancellation by client ID uses its corresponding signing type, and a duplicate client ID can produce IDEMPOTENCY_CONFLICT.
Is a User-Agent required for market-data requests?
REST requests and WebSocket connection handshakes require a descriptive User-Agent header, including for public market data. The value identifies the application making the connection or request. Public endpoints remove the need for trading authentication; they do not remove this header requirement.
Can a trading bot use a delegated key instead of the account owner’s key?
An authorized delegated signer can use its own private key for the account actions covered by its permissions. The account owner grants the delegation to the relevant subaccount. This separates the bot’s signing key from the owner’s key, while subaccount scope and granted permissions continue to determine its authority.
How can a client distinguish maintenance from a rate-limit response?
The public exchange-status query provides an operational check alongside the failed request’s error code. Rate-limit rejection uses HTTP 429 and RATE_LIMIT_EXCEEDED. The status endpoint normally remains available during maintenance, even when other public endpoints return HTTP 503. Extreme failures can affect that endpoint too, so a missing response alone does not establish normal operation.
Are prices and quantities JSON numbers in an API order?
Prices and quantities use decimal strings in order requests. This preserves the expected representation and avoids floating-point conversion errors. Subaccount IDs and venue order IDs also use strings in JSON. Boolean order flags remain booleans, so converting every field to a string produces an incorrect payload.