curl --request POST \
--url https://api.arcus.xyz/v1/batchModifyOrders \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--header 'X-Signature: <api-key>' \
--header 'X-Timestamp: <api-key>' \
--data '
{
"modifies": [
{
"address": "<string>",
"accountIndex": 4,
"marketId": 32767,
"goodTilTime": "<string>",
"quantity": "<string>",
"price": "<string>",
"reduceOnly": true,
"orderId": "<string>",
"clientId": "<string>",
"clientTime": "<string>",
"signature": "<string>"
}
]
}
'import requests
url = "https://api.arcus.xyz/v1/batchModifyOrders"
payload = { "modifies": [
{
"address": "<string>",
"accountIndex": 4,
"marketId": 32767,
"goodTilTime": "<string>",
"quantity": "<string>",
"price": "<string>",
"reduceOnly": True,
"orderId": "<string>",
"clientId": "<string>",
"clientTime": "<string>",
"signature": "<string>"
}
] }
headers = {
"X-API-Key": "<api-key>",
"X-Timestamp": "<api-key>",
"X-Signature": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-API-Key': '<api-key>',
'X-Timestamp': '<api-key>',
'X-Signature': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
modifies: [
{
address: '<string>',
accountIndex: 4,
marketId: 32767,
goodTilTime: '<string>',
quantity: '<string>',
price: '<string>',
reduceOnly: true,
orderId: '<string>',
clientId: '<string>',
clientTime: '<string>',
signature: '<string>'
}
]
})
};
fetch('https://api.arcus.xyz/v1/batchModifyOrders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcus.xyz/v1/batchModifyOrders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'modifies' => [
[
'address' => '<string>',
'accountIndex' => 4,
'marketId' => 32767,
'goodTilTime' => '<string>',
'quantity' => '<string>',
'price' => '<string>',
'reduceOnly' => true,
'orderId' => '<string>',
'clientId' => '<string>',
'clientTime' => '<string>',
'signature' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>",
"X-Signature: <api-key>",
"X-Timestamp: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/batchModifyOrders"
payload := strings.NewReader("{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("X-Timestamp", "<api-key>")
req.Header.Add("X-Signature", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcus.xyz/v1/batchModifyOrders")
.header("X-API-Key", "<api-key>")
.header("X-Timestamp", "<api-key>")
.header("X-Signature", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/batchModifyOrders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["X-Timestamp"] = '<api-key>'
request["X-Signature"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"responses": [
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"accountIndex": 4,
"clientTime": 123,
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"status": "ACK",
"updateTime": 123,
"clientId": "<string>",
"timeInForce": "GTT",
"goodTilTime": "<string>",
"remainingSize": "<string>",
"filledSize": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"error": "<string>",
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}
],
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}{
"responses": [
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"accountIndex": 4,
"clientTime": 123,
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"status": "ACK",
"updateTime": 123,
"clientId": "<string>",
"timeInForce": "GTT",
"goodTilTime": "<string>",
"remainingSize": "<string>",
"filledSize": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"error": "<string>",
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}
],
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "rate limited",
"reason": "account_empty",
"retryAfterMs": 850,
"clientId": "my-order-42"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}Batch modify orders
Modify up to 100 open orders in a single request.
curl --request POST \
--url https://api.arcus.xyz/v1/batchModifyOrders \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--header 'X-Signature: <api-key>' \
--header 'X-Timestamp: <api-key>' \
--data '
{
"modifies": [
{
"address": "<string>",
"accountIndex": 4,
"marketId": 32767,
"goodTilTime": "<string>",
"quantity": "<string>",
"price": "<string>",
"reduceOnly": true,
"orderId": "<string>",
"clientId": "<string>",
"clientTime": "<string>",
"signature": "<string>"
}
]
}
'import requests
url = "https://api.arcus.xyz/v1/batchModifyOrders"
payload = { "modifies": [
{
"address": "<string>",
"accountIndex": 4,
"marketId": 32767,
"goodTilTime": "<string>",
"quantity": "<string>",
"price": "<string>",
"reduceOnly": True,
"orderId": "<string>",
"clientId": "<string>",
"clientTime": "<string>",
"signature": "<string>"
}
] }
headers = {
"X-API-Key": "<api-key>",
"X-Timestamp": "<api-key>",
"X-Signature": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-API-Key': '<api-key>',
'X-Timestamp': '<api-key>',
'X-Signature': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
modifies: [
{
address: '<string>',
accountIndex: 4,
marketId: 32767,
goodTilTime: '<string>',
quantity: '<string>',
price: '<string>',
reduceOnly: true,
orderId: '<string>',
clientId: '<string>',
clientTime: '<string>',
signature: '<string>'
}
]
})
};
fetch('https://api.arcus.xyz/v1/batchModifyOrders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.arcus.xyz/v1/batchModifyOrders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'modifies' => [
[
'address' => '<string>',
'accountIndex' => 4,
'marketId' => 32767,
'goodTilTime' => '<string>',
'quantity' => '<string>',
'price' => '<string>',
'reduceOnly' => true,
'orderId' => '<string>',
'clientId' => '<string>',
'clientTime' => '<string>',
'signature' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>",
"X-Signature: <api-key>",
"X-Timestamp: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/batchModifyOrders"
payload := strings.NewReader("{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("X-Timestamp", "<api-key>")
req.Header.Add("X-Signature", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.arcus.xyz/v1/batchModifyOrders")
.header("X-API-Key", "<api-key>")
.header("X-Timestamp", "<api-key>")
.header("X-Signature", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/batchModifyOrders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["X-Timestamp"] = '<api-key>'
request["X-Signature"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"modifies\": [\n {\n \"address\": \"<string>\",\n \"accountIndex\": 4,\n \"marketId\": 32767,\n \"goodTilTime\": \"<string>\",\n \"quantity\": \"<string>\",\n \"price\": \"<string>\",\n \"reduceOnly\": true,\n \"orderId\": \"<string>\",\n \"clientId\": \"<string>\",\n \"clientTime\": \"<string>\",\n \"signature\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"responses": [
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"accountIndex": 4,
"clientTime": 123,
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"status": "ACK",
"updateTime": 123,
"clientId": "<string>",
"timeInForce": "GTT",
"goodTilTime": "<string>",
"remainingSize": "<string>",
"filledSize": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"error": "<string>",
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}
],
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}{
"responses": [
{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"accountIndex": 4,
"clientTime": 123,
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"status": "ACK",
"updateTime": 123,
"clientId": "<string>",
"timeInForce": "GTT",
"goodTilTime": "<string>",
"remainingSize": "<string>",
"filledSize": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"error": "<string>",
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}
],
"rateLimit": {
"pool": "order",
"remaining": 9958
}
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "rate limited",
"reason": "account_empty",
"retryAfterMs": 850,
"clientId": "my-order-42"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}{
"error": "Invalid request body",
"code": "GEO_RESTRICTED",
"errorSource": "Order",
"errorType": "Tick",
"rejectionReason": "POST_ONLY_WOULD_CROSS"
}(address, accountIndex) — batch modifies are scoped to one subaccount. Requires address query parameter (or a uniform per-row address) matching the master Ethereum address for X-API-Key.
Signed per modify. This endpoint requires the X-Signature header to be present (non-empty); its value is not verified — set it to any one element’s signature. Omitting it rejects every element with invalid order signature. Each element of modifies carries its own signature over the same typed canonical modify payload as a standalone modifyOrder, so the signed bytes are identical whether the modify is submitted alone or in a batch; the shared X-API-Key + X-Timestamp headers authenticate the batch.
Failures are per-item. A row that fails validation or signature verification is returned with status: ERROR and an error message; the remaining rows still reach the matching engine. The whole batch is rejected only for envelope-level problems (empty/oversized array, mixed (address, accountIndex), bad auth).
Speed-bump semantics match batchPlaceOrders: the batch is forwarded to the engine as one unit. If every modify targets a post-only (ALO) resting order the batch skips the taker speed bump entirely; if ANY modify targets a non-post-only order the whole batch is subject to it.
All-ALO batches are prioritized like cancels. An all-ALO batch can only shrink or reprice maker orders — it can never take liquidity — so it is routed on the high-priority path ahead of order placements, the same as cancels. Like a cancel, it may therefore be processed ahead of earlier not-yet-sequenced placements (including its own targets, which would then reject as ORDER_NOT_FOUND_FOR_MODIFY). Under cancel-replace modify semantics such a rejected modify is also buffered briefly and applied when the target placement arrives, mirroring buffered-cancel behavior — see POST /v1/modifyOrder.
Response behavior
Asynchronous. Returns either202 Accepted (the common case) or 200 OK (the gateway already had definitive per-row state). Each row echoes orderId / clientId so clients can correlate orders WebSocket channel events.Authorizations
Hex-encoded Ed25519 public key (64 chars). The public key IS the API key — register it via POST /createApiKey. Required on every authenticated request, both read-only and signed.
Unix time in nanoseconds as a decimal string (e.g. "1713825891591000000"). Millisecond or second epochs are rejected with 401 Unauthorized. Must be within ±30,000 ms (MaxTimestampDriftMs, the drift window stays configured in milliseconds) of server wall-clock, or the request is rejected with 401 Unauthorized. Required on all mutating / credential-creating endpoints. This same value must appear as the ct field in the ordersign typed canonical payload (single-order endpoints) or in each element's ct field (batch endpoints).
Lowercase hex-encoded Ed25519 signature (128 chars).
Single-order endpoints (placeOrder, cancelOrder, modifyOrder, and other non-batch mutating routes) sign over the ordersign typed canonical payload — a compact, key-sorted JSON object built from parsed request fields using engine-native integer values:
placeOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,"g":N,"m":N,"op":1,"p":N,"q":N,"r":0|1,"s":N,"t":N,"v":1}
cancelOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,[,"id":"…"],"m":N,"op":2,"v":1}
modifyOrder: {"ad":"0x…","ai":N,[,"c":"…"],"ct":N,"g":N,[,"id":"…"],"m":N,"op":3,"p":N,"q":N,"r":0|1,"s":N,"t":N,"v":1} (exactly one of id / c)
ct must equal the X-Timestamp header value. Keys in brackets are conditional (omitted when empty). op values: 1=place, 2=cancel, 3=modify. See the ordersign package for field definitions and reference signing code.
Other signed routes (e.g. createApiKey, tokens, userPreferences) still use the legacy scheme: signing_message = X-Timestamp + ACTION + canonicalJSON(body), where ACTION is the camelCase final path segment.
Batch endpoints (batchPlaceOrders, batchCancelOrders, batchModifyOrders) do NOT use this header. They authenticate with per-element typed ordersign signatures embedded in the request body (see the global auth description and the per-field signature descriptions on OrderRequest / CancelOrderRequest / ModifyOrderRequest).
Read endpoints are authenticated by ?address= (and optionally X-API-Key) only — no signature is required. The one exception is GET /v1/affiliate/inviteCodes, which returns bearer secrets and therefore requires the full header triple; with no body its signing message is X-Timestamp + ACTION. canonicalJSON(body) is the JSON body with object keys sorted lexicographically at every level and no whitespace; the server canonicalizes the received body before verifying, so only the bytes signed over must be canonical. Required on all mutating / credential-creating endpoints.
Query Parameters
Master Ethereum address for this API key (must match address from POST /createApiKey for the same key). Required on REST for account-scoped reads and for place/cancel. Invalid hex → 400; mismatch with key → 403.
20-byte EVM address as hex: optional 0x or 0X prefix and exactly 40 hexadecimal digits. API responses normalize to lowercase a–f after 0x.
^(0x|0X)?[0-9a-fA-F]{40}$Body
Array of 1-100 modify requests. Every row must share the same (address, accountIndex) — batch modifies are scoped to a single subaccount — and every row must carry its own signature field (see ModifyOrderRequest.signature).
1 - 100 elementsShow child attributes
Show child attributes
Response
Batch modify processed and the gateway already has definitive state for the rows. Per-row status reflects that state. Treat as best-effort enrichment of the 202 path; the orders WebSocket channel is still the source of truth.
One row per input modify, in request order. Failures are per-item: a row that fails validation or signature verification is returned with status: ERROR and an error message while the remaining rows still reach the matching engine.
Show child attributes
Show child attributes
Per-subaccount order-pool rate-limit snapshot after charging the whole batch (one snapshot for the request, not per row). Omitted when rate limiting is not configured.
Show child attributes
Show child attributes
Was this page helpful?