REST is Polyester's HTTP and JSON interface, supported for production trading. Use this page when you integrate with plain HTTP: it covers request signing, pagination, and error bodies. Payloads are readable and work with any HTTP client, which makes REST a quick way to start and to debug.
Send REST requests to the API host of your environment, https://api.testnet.polyester.com on
Testnet. See Environments and hosts.
Polyester's custom contract-generation tooling derives the REST models, conversions, validation, and OpenAPI schemas from the same public Protobuf contracts used by ConnectRPC. This keeps shared API semantics aligned while allowing REST-specific representations such as decimal strings and text identifiers. Learn more in Protobuf contracts.
For trading orders, review Order sizing, Fee Assets, and Preview order. These pages show the REST field names alongside the canonical contract semantics.
When to use REST
Choose REST when you want:
- simple HTTP integration with broad ecosystem support
- human-readable request/response payloads
- easier debugging with existing API tools
- fast onboarding for teams that already know exchange REST patterns
Compare REST with typed and streaming interfaces in the Connectivity matrix.
Signing private requests
- no API-key signature required
- ideal for market data and discovery flows
Private endpoints use Ed25519 API key signing.
Common required headers:
X-API-KEY-IDX-API-TIMESTAMPX-API-SIGNATURE
X-API-NONCE is optional and is not part of the canonical signing string or an operation
idempotency key.
Use the full signing guide here: Ed25519 API keys. Retry and replay rules are in API key replay protection.
Pagination patterns
REST list endpoints page with an opaque token, narrow results with a time range, or resume after a known trade ID. The REST API reference lists the parameters each endpoint accepts.
| Pattern | Send | Read back |
|---|---|---|
| Opaque page token | limit, pageToken | nextPageToken |
| Time range | startTime, endTime | nextPageToken |
| Trade-ID cursor | afterTradeId, afterMatchId | nextAfterTradeId, completeThroughTradeId |
Opaque page token. Pass nextPageToken back unchanged as pageToken. Continue while it is
non-empty. A page can hold fewer rows than limit, or none, before the last page.
Time range. Order history, account trades, public trades, and candles take RFC 3339 timestamps. Ledger transfers use timestampMin and timestampMax in
microseconds since the Unix epoch instead.
Trade-ID cursor. On public trades, afterTradeId walks a symbol's trades in ascending order
without gaps. Start from 0, then send each response's nextAfterTradeId. Every trade up to completeThroughTradeId is available to this walk. When nextAfterTradeId equals the afterTradeId you
sent, nothing newer is available yet: retry later with backoff. On a Pair without trades nextAfterTradeId is absent; treat it as 0. nextPageToken stays empty in this
mode, and afterTradeId cannot be combined with pageToken, side, startTime, or endTime. On account trades, afterMatchId returns your fills after a known match
ID and requires symbol.
General guidance:
- keep page sizes bounded and predictable
- always use the server-provided
nextPageTokenornextAfterTradeIdinstead of constructing your own - in token mode, stop paginating when
nextPageTokenis empty
Error responses
REST errors returned by the API use an RFC 9457 application/problem+json body with type, title, status, detail, and a stable code. Branch on code; detail is human-readable text and can change. type is the same for a code on every endpoint: https://polyester.dev/problems/ followed by the
code in lowercase with hyphens for underscores, such as https://polyester.dev/problems/auth-session-kind-not-allowed. Some errors add endpoint-specific
fields, such as existingOrderId on a duplicate client order ID.
A body over the 256 KiB size limit of an order or trigger write route answers 413 with REQUEST_TOO_LARGE, whether the request is signed with an API key or uses a session. Send a smaller
body or a smaller batch. A body or query value that cannot be parsed answers 400 with BAD_REQUEST. Credentials are checked first, so an unsigned request answers 401 whatever its body.
Order write errors add an optional violations array, the field violations ConnectRPC returns for
the same request. Each item has fieldPath, ruleId, and message. fieldPath is the REST request
field you sent, such as order.baseQty or items[2].limitGtc.price; when no choice of a one-of
group was sent, it names the group, such as order.execution. Branch on code, then on ruleId.
Zeros beyond a decimal's scale do not change its value and are accepted: a baseQty of "0.000100000" at base scale 8 is the quantity "0.0001", and a price of "70000.0000000000" is "70000".
A baseQty with a nonzero digit beyond the Pair's base scale is off the Pair's step, and a limit
price, or a tick-checked attached take-profit, stop-loss, or trailing-stop price, with a nonzero
digit beyond 9 fractional digits is off its tick. Order writes answer them as ConnectRPC answers an
off-step quantity or off-tick price on the same route:
POST /v1/ordersanswers400withQTY_STEP_SIZEorPRICE_TICK_SIZEand the field's violation, such asorder.baseQty.POST /v1/orders/batchrejects only that item, with the same code and a violation such asitems[0].baseQty. The other items are admitted or rejected on their own.POST /v1/orders/previewanswers200with"admissible": falseand arejectioncarrying the code and the violation.POST /v1/orders/modifyandPOST /v1/orders/replace-batchanswer400withPRICE_TICK_SIZEand the violation, such asnewPriceoritems[1].newPrice.
Other decimals, such as maxQuoteDebit, clientRefPrice, and trailingDistance, answer INVALID_ARGUMENT for a nonzero digit beyond their scale.
A request refused for its credentials gets the same problem body, for example 401 with "code": "UNAUTHENTICATED" and "type": "https://polyester.dev/problems/unauthenticated". The per-IP
limit on order and trigger writes answers a plain 429 with no Polyester body. Read the HTTP status
first, then parse the body according to its content type. See Error responses for the full model and Error codes for every code.
Enum values
REST sends and returns enum values without their protobuf type prefix: a trigger's status is CANCELED, an event's eventType is CANCELED, and its cancelReason is USER_REQUEST.
ConnectRPC JSON and the WebSocket JSON channels use the
protobuf enum name with its prefix, such as STATUS_CANCELED, EVENT_CANCELED, and TRIGGER_CANCEL_REASON_USER_REQUEST. The same trigger event, read over REST and received on the
trigger events channel, carries the same value in these two forms. A few REST enums use short
labels instead, such as candle timeframes (1m, 1h).
To map a ConnectRPC or WebSocket name to its REST value, drop the prefix the protobuf enum names
for its values, such as STATUS_, EVENT_, or TRIGGER_CANCEL_REASON_. The REST reference lists
each field's accepted values.
Query filters take only these REST values. A protobuf name such as STATUS_CANCELED, another
spelling such as canceled, or UNSPECIFIED answers 400. This covers side on open orders, order
history, and your trades, status on order history, and status, triggerType, and eventType on trigger reads. An ID filter
that is present must also name a real ID: a subaccountId of zero (1 in base58) answers 400 instead of reading the Main Account. Leave a filter out to read without it.
Common REST integration patterns
- Readable payloads: decimal strings and text identifiers are common on REST surfaces for developer ergonomics.
- Idempotent writes: for endpoints that support request IDs, use stable request IDs to make retries safe.
- Retry discipline: retry transient failures with bounded backoff and jitter; do not blindly retry validation or auth errors.
- Transport hygiene: reuse HTTP connections and keep request/response handling lightweight.
Yes. REST is production-supported. Use the Connectivity matrix to compare it with ConnectRPC and streaming interfaces.
No. Cursor/token fields are opaque by contract. Store and replay them exactly as returned.
Continue with WebSocket JSON when your REST integration also needs live market or account updates.