REST
The Perpl REST (Representational State Transfer) API is served over HTTPS and covers public market data, account and trading history, and API-key enrollment. Streaming data (order books, live prices, order/position updates, order placement) is handled by the WebSocket API, not REST.
All response types are generated from Go structs and shipped as TypeScript interfaces (via tygo), so the field names shown below match the wire format exactly.
Base URL
The REST base URL includes the /api suffix. Choose the base URL for your target network:
Mainnet (default)
https://app.perpl.xyz/api
143
Testnet
https://testnet.perpl.xyz/api
10143
The examples on this page read the base URL from the PERPL_API_URL environment variable, falling back to the mainnet default:
export PERPL_API_URL="https://app.perpl.xyz/api" # mainnet
# export PERPL_API_URL="https://testnet.perpl.xyz/api" # testnetMarket IDs differ per network. Mainnet: BTC=1, MON=10, ETH=20, SOL=31, HYPE=40, ZEC=50. Testnet: BTC=16, ETH=32, SOL=48, MON=64, ZEC=256. For the full list of network values (RPC URLs, contract addresses, collateral token) see Networks.
Authentication overview
Authentication uses API keys, which are Ed25519 key pairs enrolled once with a wallet signature. The server stores only your public key; the private key never leaves your client. There is no bearer token and no session cookie — every authenticated request is signed with four headers:
X-API-Key
The opaque token returned at enrollment
X-API-Timestamp
Request timestamp in milliseconds (must be within ±30 seconds of server time)
X-API-Nonce
Client-random, single-use base64url value (no padding)
X-API-Signature
base64url(ed25519_sign(privateKey, canonicalString)), no padding
Each endpoint below is labelled with one of three authentication requirements:
None — no signature needed.
Optional — works unauthenticated; providing an API-key signature personalizes the response.
API key — an API-key signature is required.
A key also carries a scope (read, trade, or both). REST history and profile reads require read; trade is used for order placement over WebSocket. Withdrawals and transfers-out are never permitted via API key, under any scope.
Enrolling an API key only authorizes API access — it does not create an exchange account. Trading additionally requires an on-chain account created via createAccount(uint256 amountCNS) on the Exchange contract. Endpoints that need an on-chain account may return 404 if none exists.
For the full canonical-string format and a signing helper, see Authentication. To obtain a key, see Authentication → Creating a key.
Public endpoints
GET /api/v1/pub/context
Returns global protocol configuration: chain, protocol instances, tokens, and markets.
Authentication: Optional (a signature personalizes the response)
Response:
Each ProtocolInstance carries operational limits such as min_account_open_amount, min_deposit_amount, min_withdraw_amount, and max_account_trigger_orders. Each Market carries price_decimals and size_decimals, which you use to scale integer prices and sizes into human-readable values (see Types).
Example:
GET /api/v1/market-data/:market_id/candles/:resolution/:from-:to
Returns OHLCV (open-high-low-close-volume) candlestick data.
Authentication: None
URL parameters:
market_id
number
Market ID (e.g. 1 for BTC on mainnet)
resolution
number
Candle resolution in seconds (see supported values)
from
number
Start timestamp (ms)
to
number
End timestamp (ms)
Limits: A maximum of 1024 candles per request.
Supported resolutions (seconds): 60 (1m), 300 (5m), 900 (15m), 1800 (30m), 3600 (1h), 7200 (2h), 14400 (4h), 28800 (8h), 43200 (12h), 86400 (1d).
Response:
Example:
GET /api/v1/profile/announcements
Returns active announcements.
Authentication: Optional (works unauthenticated for the public audience; a signature personalizes the returned announcements)
Response:
Example:
API-key enrollment endpoints
Enrollment is a one-time, wallet-authorized flow that turns a locally generated Ed25519 key pair into an X-API-Key token. Both endpoints below are authorized by a wallet signature, not an API-key signature, and are CORS (cross-origin resource sharing) enabled — the request Origin must be pre-whitelisted by Perpl.
For the full step-by-step flow (keypair generation, EIP-712 signing, proof-of-possession), see Authentication → Programmatic enrollment.
POST /api/v1/api-key/payload
Returns the EIP-712 typed data to sign for enrollment, plus an opaque mac that you echo back on enroll.
Authentication: Wallet signature
Purpose: Obtain the typed_data + mac used in the next step.
POST /api/v1/api-key/enroll
Enrolls the public key and returns ApiKeyInfo. The api_key.api_key field is the opaque X-API-Key token.
Authentication: Wallet signature
Request (echo typed_data + mac from the payload step, plus two signatures):
signature— wallet secp256k1 EIP-712 signature (proves account ownership)pop_signature— Ed25519 proof-of-possession over the enrollment digest
Store the returned X-API-Key token immediately — it is not re-derivable. Listing and revoking keys is done in the web UI (/apikeys), not via the API. A revoked public key cannot be re-enrolled; use a fresh keypair.
Enroll status codes:
404
Target profile not found
409
Public key already registered (revoked keys are not re-enrollable)
423
Per-profile key limit reached (maximum 16 active keys)
Profile endpoints
GET /api/v1/profile/ref-code
Returns your current referral code.
Authentication: API key
Response:
Returns 404 with an empty code if no referral code is assigned.
Example ($SIG, $TS, $NONCE are the signed values — see Authentication):
Trading history endpoints
All trading history endpoints require an API-key signature and support pagination. The response is always a page object:
Pagination query parameters:
page
string
–
Cursor from the previous response's np
count
number
50
Items per page (maximum 100)
Server-side filtering by market ID or date range is not currently supported. Filter results client-side if needed.
GET /api/v1/trading/account-history
Returns account events (deposits, withdrawals, settlements, funding, and more).
Authentication: API key
Response:
Account event types (et):
0
Unspecified
1
Deposit
2
Withdrawal
3
IncreasePositionCollateral
4
Settlement
5
Liquidation
6
TransferToProtocol
7
TransferFromProtocol
8
Funding
9
Deleveraging
10
Unwinding
11
PositionCollateralDecreased
12
LastForwardedDescIdReset
GET /api/v1/trading/fills
Returns order fill history.
Authentication: API key
Response:
GET /api/v1/trading/order-history
Returns historical order events.
Authentication: API key
Response:
See Types for the Order structure.
GET /api/v1/trading/position-history
Returns position history.
Authentication: API key
Response:
See Types for the Position structure.
Pagination example
Each request is signed with the API-key headers. signedRequest(method, target, body) is the helper defined in Authentication — note that the request-target (path + query string) must be signed exactly as sent.
Rate limits
Rate limits are approximate. Monitor for HTTP 429 responses and back off.
REST public
~100 req/min
/api/v1/pub/*, market data
REST authenticated
~60 req/min
profile, trading history
On a 429 Too Many Requests, retry with exponential backoff (for example 1s, then 2s, then 4s).
Errors
HTTP status codes:
200
Success
400
Bad Request
401
Unauthorized — bad or stale signature, replayed nonce, or a revoked/expired key
403
Forbidden — insufficient scope (for example a read key attempting a trade action)
404
Not Found — including no on-chain account for the caller
429
Too Many Requests
500
Internal Server Error
A request is rejected with 401 if the timestamp is outside the ±30-second window, the nonce has already been used within the validity window, the key is past its expires_at, or the caller IP is not in the key's ip_cidrs allow-list (CIDR = Classless Inter-Domain Routing; maximum 4 CIDRs) when one is set.
No JSON error-envelope schema is defined in the source documentation. Inspect the HTTP status code to classify failures.
Last updated