Arcus API Guide: Markets, Funding and Fee-Tier Endpoints with Working Examples
Table of Contents
The short version
The public Arcus API lives at https://api.arcus.xyz. Market data needs no key: /v1/markets, /v1/feetiers, /v1/candles and /v1/fundingRates all answer a plain GET. Watch the units. Fees are in ppm (225 = 0.0225%), volume thresholds are in 1e-9 USD, and candle and funding timestamps are in microseconds. Candles came back newest-first in testing, even though the docs say oldest-first. Every response below is real, captured on October 2, 2026.
Arcus runs perpetual futures and Stock Tokens on Robinhood Chain, and its perps exchange has a full REST and WebSocket API. The official docs are detailed, down to a full OpenAPI spec. They are also long, and the unit conventions are spread across dozens of pages. This page pulls together the four endpoints you need for a dashboard, a backtest or a funding tracker, shows what each one returned on a real call, and spells out the conversions. If you just want to trade, the Arcus app is the easier route.

The GET /v1/candles page in the Arcus API reference. Its response panel is a static sample; the live API is what this guide quotes.
api.arcus.xyz returns markets, fees, candles and funding as JSON. The units are where scripts go wrong, so the table below lists each one.What the public Arcus API covers
Arcus's introduction page says the REST and WebSocket APIs "cover Arcus perpetuals." Mainnet REST is https://api.arcus.xyz and the WebSocket is wss://api.arcus.xyz/v1/ws. A testnet with identical paths runs at https://api.testnet.arcus.xyz, and Arcus notes that new features reach testnet first. A GET to the root returned {"docs":"https://docs.arcus.xyz","status":"running","version":"production-v0.5.0-1"} when I checked.
The endpoints this guide uses, plus a few related ones, with the request weight the rate-limit page assigns to each:
| Endpoint | What it returns | Key? | Weight |
|---|---|---|---|
GET /v1/markets | Every perp market: prices, 24h stats, open interest, funding, margin fractions | No | 20 |
GET /v1/feetiers | The exchange-wide maker/taker fee table by tier | No | 2 |
GET /v1/candles | OHLCV bars for one market and timeframe | No | 20 + rows/60 |
GET /v1/fundingRates | Historical hourly funding rates for one market | No | 20 + rows/20 |
GET /v1/commissionrates | The referral commission schedule in basis points | No | not listed |
GET /v1/bbo/BTC-USD, /v1/mids, /v1/prices | Best bid/offer for one market, mids for all, oracle and mark prices | No | 2 to 20 |
POST /v1/placeOrder and other writes | Orders, cancels, leverage, withdrawals (signed) | Yes | 0 to 125 |
Account reads such as /v1/positions and /v1/account take an address query parameter, and their docs pages say no authentication header is required. The pToken vaults have their own host, https://api.vaults.arcus.xyz/v1/vaults, which returned 16 vaults with TVL, NAV and fee fields on October 2. That host is not in the API reference index, so treat its shape as unofficial. Stock Token spot trading goes through a separate system, the Spot Router, with its own integration docs. For background on the vaults, see Arcus pTokens explained.
Units and gotchas
The API uses at least five different units across its fields. I checked every row below against a live response on October 2, 2026.
| Field | Unit | Live example | Convert |
|---|---|---|---|
maker_fee_ppm, taker_fee_ppm | Parts per million of notional | 225 | ÷ 10,000 = 0.0225% |
volume_threshold (fee tiers) | USD × 1e9 | 5000000000000000 | ÷ 1e9 = $5,000,000 |
openTime (candles), time (funding) | Unix microseconds | 1790953200000000 | ÷ 1,000 for JS Date |
to / from query params | Unix microseconds | 1790956638949693 | Date.now() * 1000 |
nextFundingAt, addedTimestamp (markets) | Unix seconds | 1790956800 | × 1,000 for JS Date |
fundingRate | Hourly rate as a fraction | "0.0000125" | × 100 = 0.00125% per hour |
priceChange24h | Fraction | "0.0133" | × 100 = +1.33% |
openInterest | Base-asset units | "138.52231637" | × markPrice = USD |
initialMarginFraction | Fraction | "0.025" | 1 ÷ value = 40x max leverage |
timeNs (/v1/time), X-Timestamp header | Unix nanoseconds | 1790956663172516503 | ÷ 1e6 for JS Date |
Two more things to know. Prices, sizes and rates arrive as strings ("85245.5", not 85245.5), which keeps decimal precision intact but means you have to parse them before doing math. And the time units don't match across endpoints: nextFundingAt on /v1/markets is in seconds, while candle and funding times are in microseconds.
Candles came back newest-first
The candles docs say "Bars are returned oldest-first; the last entry is the in-progress bar." On October 2, 2026 the live API returned them newest-first, with the in-progress bar (isFinal: false) at index 0. That held for both the countback and the from forms. Sort by openTime yourself and your code works either way.
Example 1: list markets with GET /v1/markets
With no parameters you get every market, sorted by marketId. Pass market (a display name like BTC-USD, case-insensitive, or a numeric ID) to get just one.
curl "https://api.arcus.xyz/v1/markets?market=BTC-USD"
Response, captured 2026-10-02 15:57 UTC (trimmed to the most useful fields):
{
"markets": [
{
"marketDisplayName": "BTC-USD",
"marketId": 1,
"status": "ONLINE",
"tickSize": "0.1",
"stepSize": "0.00000001",
"minOrderNotional": "5",
"oraclePrice": "85266.1",
"markPrice": "85245.5",
"fundingRate": "0.0000125",
"nextFundingRate": "0.0000125",
"nextFundingAt": 1790956800,
"priceChange24h": "0.0133",
"volume24hNotional": "269836888.8",
"openInterest": "138.52231637",
"initialMarginFraction": "0.025",
"regularTradingHours": null,
"isOutsideRth": false,
"category": "CRYPTO",
"pythId": "1"
}
]
}
That reads as: BTC marked at $85,245.50, funding of 0.00125% for the hour with the next payment at 16:00 UTC, about $11.8M of open interest (138.52 BTC × the mark), and 40x max leverage. Equity, index and commodity markets also fill regularTradingHours and flip isOutsideRth when the underlying is closed. The full list returned 67 markets that day, 60 of them ONLINE.
The same call in JavaScript, ranked by open interest:
const res = await fetch('https://api.arcus.xyz/v1/markets');
const { markets } = await res.json();
const rows = markets
.filter((m) => m.status === 'ONLINE')
.map((m) => ({
market: m.marketDisplayName,
mark: Number(m.markPrice),
maxLev: Math.round(1 / Number(m.initialMarginFraction)),
oiUsd: Number(m.openInterest) * Number(m.markPrice),
fundingPctHr: Number(m.fundingRate) * 100,
}))
.sort((a, b) => b.oiUsd - a.oiUsd);
console.table(rows.slice(0, 5));
At 15:58 UTC that printed BTC-USD (about $11.7M OI, 40x), ETH-USD (about $5.4M, 25x), SPY-USD (about $3.8M, 50x), SOL-USD and NVDA-USD. Our markets page and the open interest tracker are built on this same endpoint.
Example 2: read fee tiers with GET /v1/feetiers
curl https://api.arcus.xyz/v1/feetiers
Response, captured 2026-10-02 15:57 UTC (first two and last two tiers):
{
"tiers": [
{ "level": 0, "name": "Base", "volume_threshold": 0,
"maker_fee_ppm": 0, "taker_fee_ppm": 225 },
{ "level": 1, "name": "Bronze", "volume_threshold": 5000000000000000,
"maker_fee_ppm": 0, "taker_fee_ppm": 190 },
{ "level": 5, "name": "VIP", "volume_threshold": 1000000000000000000,
"maker_fee_ppm": -20, "taker_fee_ppm": 100 },
{ "level": 6, "name": "Max", "volume_threshold": 3000000000000000000,
"maker_fee_ppm": -30, "taker_fee_ppm": 95 }
]
}
The schema describes volume_threshold as the "30-day notional volume threshold (in USD×1e9)" and the fee fields as "parts-per-million of notional," where a negative maker value is a rebate. Converting and pricing a trade:
const { tiers } = await (
await fetch('https://api.arcus.xyz/v1/feetiers')
).json();
const pct = (ppm) => ppm / 10_000; // 225 -> 0.0225 (%)
const usd = (raw) => raw / 1e9; // 5e15 -> 5,000,000
const feeUsd = (notional, ppm) => (notional * ppm) / 1e6;
for (const t of tiers) {
console.log(t.name, usd(t.volume_threshold),
`maker ${pct(t.maker_fee_ppm)}%`, `taker ${pct(t.taker_fee_ppm)}%`);
}
feeUsd(10_000, tiers[0].taker_fee_ppm); // 2.25
A $10,000 taker fill at the Base tier costs $2.25, and a maker fill costs nothing. These are Arcus's beta rates. The help center lists higher standard rates for after the beta (0.045% taker and 0.015% maker at Base), so read the table live instead of hard-coding it. Arcus's fees concept page gives the same advice. Our fee calculator does this math for every tier, and Arcus fees explained covers how tiers are earned.
Traders who join with a referral code such as ARCUSGUIDE see "Receive 5% off on all trading fees" in Arcus's app, and Arcus's API changelog for July 22, 2026 lists a "5% referral fee discount for referred users." A code can only be added early in an account's life, and ArcusGuide earns a share of fees from accounts that use its code. The referral code guide has the details.
Trade Arcus for 5% less
Join with code ARCUSGUIDE and Arcus takes 5% off your trading fees, automatically. It costs nothing, and the code attaches at signup, so add it on day one.
Get 5% Off Arcus FeesExample 3: pull candles with GET /v1/candles
market, timeframe and to are required. to must be in Unix microseconds: the spec says seconds- or millisecond-scale values are rejected, and the server requires at least 1e14. Add either countback (up to 1,500 bars) or from, but not both. Timeframes run from 1m to 1w: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d, 3d and 1w.
TO=$(( $(date +%s) * 1000000 ))
curl "https://api.arcus.xyz/v1/candles?market=BTC-USD&timeframe=1h&to=$TO&countback=3"
Response, captured 2026-10-02 15:57 UTC (two of three bars, some fields trimmed):
{
"candles": [
{
"marketDisplayName": "BTC-USD",
"timeframe": "1h",
"openTime": 1790953200000000,
"open": "85657.5", "high": "85727.9",
"low": "85123.3", "close": "85252.9",
"volume": "272.42586708",
"notionalVolume": "23263333.211866306",
"tradeCount": 11456,
"isFinal": false
},
{
"marketDisplayName": "BTC-USD",
"timeframe": "1h",
"openTime": 1790949600000000,
"open": "86541.2", "high": "86802.9",
"low": "85364.2", "close": "85657.5",
"volume": "364.28365959",
"notionalVolume": "31317915.408049955",
"tradeCount": 14243,
"isFinal": true
}
],
"total": 3
}
The first bar is the open 15:00 UTC hour, so the order is newest-first. With countback=3 I got three rows, the in-progress bar included. Sending to in seconds returned HTTP 400 with {"error":"invalid or missing 'to' query parameter (unix microseconds)"}.
If you plan to backtest, note that the docs say bar prices "are derived from the market's oracle price feed, not from executed trades," while volume and trade counts come from actual fills. Empty buckets are gap-filled flat at the previous close. Arcus's help center names Pyth Pro as the primary source for its oracle prices.
async function getCandles(market, timeframe, countback = 100) {
const url = new URL('https://api.arcus.xyz/v1/candles');
url.search = new URLSearchParams({
market,
timeframe,
to: Date.now() * 1000, // microseconds
countback,
});
const res = await fetch(url);
if (!res.ok) throw new Error((await res.json()).error);
const { candles } = await res.json();
return candles
.map((c) => ({ ...c, date: new Date(c.openTime / 1000) }))
.sort((a, b) => a.openTime - b.openTime); // oldest first
}
const bars = await getCandles('ETH-USD', '4h', 6);
to in microseconds, convert openTime back with / 1000, and always sort candles yourself. Do that, and your code works whichever order the API uses.Example 4: read funding with GET /v1/fundingRates
This endpoint returns past hourly funding rates for one market, newest-first, up to limit=1000 rows per call. Optional from and to are microseconds and inclusive, matching the time field, so a time from one page works as the bound for the next.
curl "https://api.arcus.xyz/v1/fundingRates?market=SPY-USD&limit=3"
Response, captured 2026-10-02 15:57 UTC:
{
"fundingRates": [
{ "marketId": 23, "marketDisplayName": "SPY-USD",
"fundingRate": "-0.000029976454494208", "time": 1790953200000000 },
{ "marketId": 23, "marketDisplayName": "SPY-USD",
"fundingRate": "-0.000018348220132205", "time": 1790949600000000 },
{ "marketId": 23, "marketDisplayName": "SPY-USD",
"fundingRate": "0.00000505787037037", "time": 1790946000000000 }
],
"total": 3
}
Per Arcus's funding docs, a positive rate means longs pay shorts, and the hourly payment is funding_rate_hourly × position_size × oracle_price. The 15:00 UTC SPY rate was negative, so shorts paid longs that hour. On BTC, 0.0000125 per hour works out to 0.01% per 8 hours, which is the crypto base rate in the docs, or about 10.95% a year if it held constant. On a $10,000 BTC position that is $0.125 an hour. Equity, index and commodity perps lock funding to SOFR + 0.5% a year while the underlying is fully closed overnight and on weekends.
To build a longer history, page backwards:
async function fundingHistory(market, hours = 24 * 30) {
const out = [];
let to;
while (out.length < hours) {
const url = new URL('https://api.arcus.xyz/v1/fundingRates');
url.searchParams.set('market', market);
url.searchParams.set('limit', '1000');
if (to) url.searchParams.set('to', String(to));
const { fundingRates } = await (await fetch(url)).json();
if (!fundingRates.length) break;
out.push(...fundingRates);
to = fundingRates.at(-1).time - 1; // just below the oldest row
await new Promise((r) => setTimeout(r, 3000)); // a full page costs 70 weight
}
return out.slice(0, hours);
}
const rows = await fundingHistory('SPY-USD', 1500);
const avg = rows.reduce((s, r) => s + Number(r.fundingRate), 0) / rows.length;
console.log(`${(avg * 100).toFixed(5)}%/hr, ${(avg * 24 * 365 * 100).toFixed(1)}%/yr`);
Run on October 2, 2026, that returned 1,500 unique hourly rows for SPY-USD going back to August 1. The average was about 0.00096% per hour, roughly 8.4% a year paid by longs over that window. The same lookup in Python:
import requests
from datetime import datetime, timezone
r = requests.get(
"https://api.arcus.xyz/v1/fundingRates",
params={"market": "BTC-USD", "limit": 24},
timeout=10,
)
r.raise_for_status()
for row in r.json()["fundingRates"]:
t = datetime.fromtimestamp(row["time"] / 1_000_000, tz=timezone.utc)
print(t.isoformat(), f"{float(row['fundingRate']) * 100:.5f}%/hr")
In my capture, fundingRate on /v1/markets matched the newest row here, and nextFundingRate is the predicted rate for the next hour. For a live view across every market without writing code, use the funding rates tool.
Rate limits and CORS
The rate-limit page describes two independent layers. Every IP gets a bucket of 1,500 weight that refills at 1,500 per minute (25 per second). Each request deducts its weight first. Arcus says limits are enforced across all gateway nodes, "so spreading requests across connections or hitting different nodes does not raise your effective budget."

The rate limits page in Arcus's API reference.
The sustained rates that follow from a 25-per-second refill, using the docs' own figures:
| Weight | Sustained rate from one IP | Example endpoints |
|---|---|---|
| 2 | 12.5 requests/second | feeTiers, bbo, mids, positions |
| 20 | 1.25 requests/second | markets, candles, fundingRates, prices |
| 125 | 0.2 requests/second | setLeverage, withdraw, transfer |
Big pages cost extra. List endpoints add floor(items / N), where N is 20 for most lists and 60 for candles, so a full 1,500-bar candle page costs 45 and a 1,000-row funding page costs 70. Go over budget and you get HTTP 429 with a Retry-After header in whole seconds and {"error":"rate limited"} in the body. Arcus does not send X-RateLimit-* headers, so you have to track the cost yourself. Order writes cost 0 IP weight and draw on per-subaccount pools instead (20,000 orders and 40,000 cancels to start, growing with lifetime volume).
If you poll hard, the docs suggest a WebSocket subscription. The socket has channels for the order book, trades, BBO, candles, markets and predicted funding.
I couldn't find anything about CORS in the docs. In testing on October 2, api.arcus.xyz returned access-control-allow-origin: * on 200 responses, on 400 errors and on the OPTIONS preflight, and the vaults host did the same. Browser fetch calls worked directly with no proxy. Arcus hasn't documented that, so it could change.
Trade Arcus for 5% less
Join with code ARCUSGUIDE and Arcus takes 5% off your trading fees, automatically. It costs nothing, and the code attaches at signup, so add it on day one.
Get 5% Off Arcus FeesWhat needs an API key
Anything that changes account state needs a signed request. According to the authentication docs, an Arcus API key is the public half of an Ed25519 key pair. You register it with POST /v1/createApiKey, which your Ethereum wallet signs as EIP-712 typed data, or you let the Arcus web app generate and register one for you.
Each signed request then carries three headers: X-API-Key, X-Timestamp and X-Signature. The timestamp is in Unix nanoseconds and has to be within ±30 seconds of server time, not the microseconds the data endpoints use. Orders, cancels and modifies sign a compact, key-sorted payload with prices in ticks and sizes in quantums. Leverage changes, cancel-all and referral calls use an older scheme that signs timestamp + action + body.
Three rules are easy to miss. Order writes are asynchronous and usually return 202 Accepted; the docs say the final state arrives on the WebSocket orders and userFills channels. Every order needs a goodTilTime at least one month out, IOC and FOK included, because the field doubles as replay protection. And keys made through the public createApiKey are trade-only. The docs say a key that can withdraw is only granted through the operator's provisioning flow.
The docs include step-by-step guides for trading over REST and over WebSocket, plus a latency guide. Start on testnet. A new testnet key starts with no balance, and the docs point to a Testnet Deposit button that credits about $1,000 of paper collateral. Arcus also runs an MCP server so AI coding assistants can read its docs.
Read without a key
Prototype on the public GET endpoints first: markets, fee tiers, candles and funding.
Make a testnet key
Generate and register an Ed25519 key on testnet, then fund it with Testnet Deposit.
Sign one order
Follow the REST trading guide, and watch the orders channel for the result.
Move to mainnet
Same paths and signing, different host. Your account needs perps access first.
Two limits apply before mainnet. Arcus says it is not available in the United States, Canada, the United Kingdom or other restricted jurisdictions, and its terms bar those persons from perps; if that's you, the regional availability guide covers what that means. Elsewhere, perps trading needs access through an invite code or a waitlist slot, and an API key does not get around that. The invite code and waitlist guide explains how access works.
Where to go next
Arcus is built by dYdX Labs (the trade name of dYdX Trading Inc.), and its API is Arcus's own /v1 design. The dYdX v4 indexer path /v4/perpetualMarkets returns 404 on api.arcus.xyz, so code written for dYdX Chain needs rewriting. For more on the platform itself, start with what Arcus is and how it compares with dYdX Chain. For the official reference, bookmark the API introduction, the changelog and the machine-readable index at docs.arcus.xyz/llms.txt.
ArcusGuide is independent and not affiliated with Arcus. Responses quoted here were captured on October 2, 2026 and will have changed since. This is not financial advice.
Frequently Asked Questions
Not for market data. Markets, fee tiers, candles, funding rates, the order book and the referral commission schedule are all public GET endpoints with no key. Placing or canceling orders, setting leverage and withdrawing need an Ed25519 API key registered to your Ethereum address, and every one of those requests has to be signed.
Fees in /v1/feetiers are in parts per million, so 225 means 0.0225 percent. Volume thresholds are in billionths of a dollar, so 5000000000000000 means 5 million dollars. Candle and funding timestamps are Unix microseconds. Prices, sizes and funding rates arrive as decimal strings, and funding rates are hourly fractions.
Arcus's docs describe a per-IP bucket of 1,500 weight that refills at 1,500 per minute. Cheap reads such as feeTiers cost 2, standard reads such as markets, candles and fundingRates cost 20 plus a charge for large pages. Going over returns HTTP 429 with a Retry-After header. Order writes are governed by separate per-subaccount pools.
In testing on October 2, 2026, the public endpoints returned access-control-allow-origin set to a wildcard on normal responses, on errors and on the preflight OPTIONS request, so a browser fetch worked with no proxy. The docs do not describe a CORS policy, so treat that as observed behavior that could change.
No. Arcus says it is not available in the United States, Canada, the United Kingdom or other restricted jurisdictions, and its terms bar those persons from perps. Elsewhere, an API key lets you trade only once your account has perps access, which comes from an invite code or a waitlist slot.
Independent resource: ArcusGuide is an independent, third-party resource operated by Concept211. It is not affiliated with, produced by, reviewed by or endorsed by Arcus Labs Ltd, Arcus Labs Inc., Pocket Protector Labs Inc., dYdX Trading Inc. (dYdX Labs), the dYdX Foundation, Robinhood or any of their affiliates. "Arcus" and related names and marks belong to their respective owners and are used here only to identify the platform this site documents. Read the full disclaimer.
Availability: Arcus perpetual contracts and derivatives trading features are not available to persons in the United States, Canada, the United Kingdom or other restricted or sanctioned jurisdictions, and Arcus restricts spot trading there too. This site is not intended for persons in those jurisdictions. Do not use a VPN or any other tool to get around a regional restriction; Arcus prohibits it.
Not advice: Nothing on this site is legal, tax, financial or investment advice. Descriptions of regulatory status, tax treatment and market availability are general information that varies by jurisdiction and changes over time. Confirm anything that matters to you with a qualified professional and against primary sources. Trading perpetual futures with leverage carries a substantial risk of loss, and past performance does not indicate future results.
Disclosure: this site contains referral links. Joining Arcus with code ARCUSGUIDE takes 5% off your trading fees and earns us a share of the fees Arcus already charges, at no extra cost to you.
Trade Arcus for 5% less
Join with code ARCUSGUIDE and Arcus takes 5% off your trading fees, automatically. It costs nothing, and the code attaches at signup, so add it on day one.
Get 5% Off Arcus Fees