ADDR=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
# First page: newest 1000 (the default — no `limit` needed).
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}"
# Next page: the oldest createdAt from the response, sent verbatim.
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}&to=1785801600123456"
# Just the TPSLs waiting on a trigger, in one market.
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}&market=BTC-USD&status=UNTRIGGERED"
import requests
ADDR = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
URL = "https://api.arcus.xyz/v1/openOrders"
seen, to = {}, None
while True:
params = {"address": ADDR}
if to is not None:
params["to"] = to
page = requests.get(URL, params=params).json()["orders"]
for o in page:
seen[o["orderId"]] = o # dedupes the boundary overlap
if len(page) < 1000:
break
to = page[-1]["createdAt"] # same unit as the bound
print(len(seen))
const ADDR = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
const seen = new Map();
let to;
for (;;) {
const url = new URL("https://api.arcus.xyz/v1/openOrders");
url.searchParams.set("address", ADDR);
if (to !== undefined) url.searchParams.set("to", String(to));
const { orders } = await (await fetch(url)).json();
for (const o of orders) seen.set(o.orderId, o); // dedupe overlap
if (orders.length < 1000) break;
// createdAt and `to` are both microseconds — echo it back as-is.
to = orders[orders.length - 1].createdAt;
}
console.log(seen.size);
const options = {method: 'GET'};
fetch('https://api.arcus.xyz/v1/openOrders', 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/openOrders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/openOrders"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.arcus.xyz/v1/openOrders")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/openOrders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"orders": [
{
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"side": "BUY",
"status": "PENDING",
"price": "<string>",
"originalSize": "<string>",
"remainingSize": "<string>",
"updatedAt": 123,
"clientId": "<string>",
"type": "LIMIT",
"filledSize": "<string>",
"avgFillPrice": "<string>",
"timeInForce": "GTC",
"goodTilTime": "<string>",
"reduceOnly": true,
"triggerPrice": "<string>",
"tpslType": "STOP_LOSS",
"isPositionTPSL": true,
"parentOrderId": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"cancelReason": "MODIFY_CANCELED",
"state": "OPEN",
"positionEffect": "OPEN_LONG",
"createdAt": 123,
"sequenceNumber": 123
}
],
"total": 123
}{
"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"
}Get open orders
Returns the open orders for the requested account address, newest-first by placement time (createdAt).
ADDR=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
# First page: newest 1000 (the default — no `limit` needed).
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}"
# Next page: the oldest createdAt from the response, sent verbatim.
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}&to=1785801600123456"
# Just the TPSLs waiting on a trigger, in one market.
curl "https://api.arcus.xyz/v1/openOrders?address=${ADDR}&market=BTC-USD&status=UNTRIGGERED"
import requests
ADDR = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
URL = "https://api.arcus.xyz/v1/openOrders"
seen, to = {}, None
while True:
params = {"address": ADDR}
if to is not None:
params["to"] = to
page = requests.get(URL, params=params).json()["orders"]
for o in page:
seen[o["orderId"]] = o # dedupes the boundary overlap
if len(page) < 1000:
break
to = page[-1]["createdAt"] # same unit as the bound
print(len(seen))
const ADDR = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
const seen = new Map();
let to;
for (;;) {
const url = new URL("https://api.arcus.xyz/v1/openOrders");
url.searchParams.set("address", ADDR);
if (to !== undefined) url.searchParams.set("to", String(to));
const { orders } = await (await fetch(url)).json();
for (const o of orders) seen.set(o.orderId, o); // dedupe overlap
if (orders.length < 1000) break;
// createdAt and `to` are both microseconds — echo it back as-is.
to = orders[orders.length - 1].createdAt;
}
console.log(seen.size);
const options = {method: 'GET'};
fetch('https://api.arcus.xyz/v1/openOrders', 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/openOrders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.arcus.xyz/v1/openOrders"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.arcus.xyz/v1/openOrders")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.arcus.xyz/v1/openOrders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"orders": [
{
"orderId": "<string>",
"marketId": 32767,
"marketDisplayName": "BTC-USD",
"side": "BUY",
"status": "PENDING",
"price": "<string>",
"originalSize": "<string>",
"remainingSize": "<string>",
"updatedAt": 123,
"clientId": "<string>",
"type": "LIMIT",
"filledSize": "<string>",
"avgFillPrice": "<string>",
"timeInForce": "GTC",
"goodTilTime": "<string>",
"reduceOnly": true,
"triggerPrice": "<string>",
"tpslType": "STOP_LOSS",
"isPositionTPSL": true,
"parentOrderId": "<string>",
"rejectionReason": "POST_ONLY_WOULD_CROSS",
"cancelReason": "MODIFY_CANCELED",
"state": "OPEN",
"positionEffect": "OPEN_LONG",
"createdAt": 123,
"sequenceNumber": 123
}
],
"total": 123
}{
"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"
}createdAt). Optional from/to (epoch microseconds) bound the window on createdAt.
At most 1000 orders are returned per response, which is also the default. The matching engine allows up to 10,000 live clientIds per account (TOO_MANY_CLIENT_IDS), so an account can hold more open orders than fit in one page.
To page through them, walk backwards through createdAt windows:
- Request the first page with no
to. You get the newest 1000 orders. - Take the oldest
createdAtin the response and send it back verbatim astoon the next request. Bounds are microseconds, the same unitcreatedAtis reported in, so no conversion is needed. - Repeat until a response contains fewer than 1000 orders.
orderId as you go. to is inclusive and closes over the whole microsecond it names, so the last order(s) of one page reappear at the top of the next. That overlap is deliberate: orders placed in a single batch can share a createdAt, and a bound that excluded them would drop the ones you had not yet seen instead of repeating the ones you had.
The window keys off createdAt rather than the last-update time so that paging is stable — an order’s createdAt does not move when it partially fills, so each order stays in the same window as you walk.
Optional market restricts results to a single market, and optional status separates orders resting on the book (OPEN) from TPSLs parked on the untriggered book (UNTRIGGERED). Both are applied before the row cap, so a filtered page still carries up to limit orders. No authentication header is required.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}$Subaccount index (0–9) to scope the request to. Defaults to 0 (the primary account). Values above 9 → 400.
0 <= x <= 9Restrict results to a single market. Accepts either the display name (e.g. BTC-USD, case-insensitive) or the numeric market id (e.g. 1). Omit to return open orders across all markets. An unresolvable value → 400.
"BTC-USD"
Restrict results to one or more order statuses. Case-insensitive. Accepts repeated params (?status=OPEN&status=UNTRIGGERED) or a comma-separated list (?status=OPEN,UNTRIGGERED); the two are equivalent. Multiple values are a union.
On this endpoint only two of the values can ever match, and the distinction is the reason to use it: OPEN is an order resting on the matching book, UNTRIGGERED is a TPSL parked on the untriggered book waiting for its trigger. The terminal statuses are accepted but match nothing here — they belong to GET /v1/orders, so narrowing an existing query returns an empty 200, not a 400. An unrecognised value → 400.
OPEN, UNTRIGGERED, FILLED, CANCELED, REJECTED, LIQUIDATED, ADL ["UNTRIGGERED"]
Maximum number of open orders to return. Default and maximum are both 1000; requests above the maximum are silently clamped. Pass an explicit smaller value when you want fewer rows.
Filters are applied before this cap, so a filtered page still carries up to limit matching orders.
1 <= x <= 1000Start of the time window, filtering on createdAt (epoch microseconds, inclusive) — the same unit and field the response reports, so a createdAt read from one page is a valid bound for the next with no conversion.
Second- and millisecond-scale values are rejected with a 400 (the server requires at least 1e14). A millisecond bound read as microseconds would land in 1970 and quietly return nothing.
x >= 1000000000000001785801600123456
End of the time window, filtering on createdAt (epoch microseconds, inclusive) — the same unit and field the response reports, so a createdAt read from one page is a valid bound for the next with no conversion.
The bound closes over the whole microsecond it names, so page boundaries overlap by design — deduplicate by id when paging.
Second- and millisecond-scale values are rejected with a 400 (the server requires at least 1e14). A millisecond bound read as microseconds would land in 1970 and quietly return nothing.
x >= 1000000000000001785801699001200
Was this page helpful?