Pascal

Pascal#

Pascal is a noncustodial prediction market exchange on Solana.

Quickstart#

Web: visit app.pascal.trade and connect your wallet to start trading.

Python Quickstart: github.com/pascal-research-inc/pascal-python-quickstart includes examples for request signing and order placement, plus a simple market making script you can adapt for custom trading logic.

Architecture#

Traders post collateral in a Solana program (smart contract) and submit signed orders to the offchain matching engine. When a trade occurs, the matching engine broadcasts a transaction containing both signed orders to settle the trade onchain.

Only the matching engine can broadcast trades onchain, meaning all state transitions occur through the offchain API and trades are guaranteed to execute as soon as they are matched offchain. Practically, this means that after depositing, traders do not need to interact with the chain to trade on Pascal.

The program ensures that any trading or movement of funds requires signatures by the trader. For details, see Program.

Onboarding#

Open the web app, connect a Solana wallet (or sign up with email), and deposit USDC.

Deposit USDC directly from a wallet, or by withdrawing from Polymarket or another centralized exchange to your generated deposit address.

Fees#

Fees are charged per fill and configured per market.

Fields for taker_fee_rate and maker_rebate_share are returned in each market's MarketSpec.

Today they default to:

{
  "taker_fee_rate": "0.020000", // Takers pay 2% * price * (1 - price) * size
  "maker_rebate_share": "0.250000" // Makers receive 25% of the taker fee
}

Fees are denominated in USD and rounded to 6 fractional digits.

For a fill of size contracts at price price ($0.00–$1.00), the taker pays:

size × price × (1 - price) × taker_fee_rate

Maker Rebates#

For a fill of size contracts at price price ($0.00–$1.00), the maker is paid:

size × price × (1 - price) × taker_fee_rate × maker_rebate_share

Builder Codes#

A third-party integration — a frontend, terminal, or bot — can attach a builder fee to the orders it routes by setting builder_info on Place Order requests. Builder codes are permissionless: any registered account's wallet public key other than your own is a valid builder_pubkey, with no registration or approval step. The builder must hold at least $100.00 of free collateral at the time an order naming it is placed; see the placement rules below.

The builder public key and fee rate sit inside the signed order permit, so the order's signer consents to the exact fee on every order. Consent is per order, not a standing approval. To stop paying a builder on future fills, cancel any open orders that name it and omit builder_info from future orders.

For each fill of size contracts at price price, the order's owner pays the builder:

size × price × (1 - price) × builder_fee_rate

For example, a 1% builder fee rate on 100 contracts filled at $0.50 pays the builder $0.25. The fee is debited with the fill and credited to the builder's free collateral in the same settlement step; the builder withdraws the proceeds like any balance.

Placement rejects fee rates above the 10% cap (BUILDER_FEE_RATE_INVALID), unknown builders and orders naming their own account (BUILDER_INVALID), and builders holding less than $100.00 of free collateral (BUILDER_COLLATERAL_BELOW_MINIMUM); resting orders are unaffected. Fills expose builder_pubkey and builder_fee_paid_usd, and lifetime builder totals are served by Builder Stats.

Referral Program#

Pascal's referral program rewards you for inviting new traders to Pascal. You earn a share of the fees they generate, while they receive a fee discount.

Once your Pascal account is funded, you can create a referral code and share it or your referral link with a new trader. When they sign up through the link or enter your code during registration, they become your referral. Referral codes cannot be added or changed after registration.

You receive 10% of the exchange fees generated by each trader you refer, calculated after discounts and rebates, up to $10,000.00 in rewards per trader. Your share is credited to your Pascal balance automatically with each eligible trade and is available immediately. There is no separate claim process.

A trader who registers with a referral code receives a 5% discount on fees until the discount has saved them $250.00.

Contracts#

Pascal offers outcome contracts for trading.

Outcome contracts are bilateral, fully collateralized derivative contracts which settle to a price p in the interval [$0, $1]. Counterparties are either long (receive p at settlement) or short (receive 1 - p at settlement).

There is no notion of "yes" or "no" on Pascal. If you are long 5 contracts, you can sell 8 and you will be short 3.

Collateral#

Accounts hold USDC collateral in the Pascal program.

When entering positions, an account's Free Collateral is reduced by enough to cover the worst case loss on the position.

For example, if going long q contracts at 30c, free collateral is reduced by q * 0.30 USD. If going short q at 30c, free collateral is reduced by q * (1 - 0.30) USD.

Free collateral is automatically unlocked when a market settles, or after partially or completely trading out of a position.

Realized and Unrealized PnL#

Pascal reports realized and unrealized PnL on open positions for display and portfolio tracking.

Core exchange accounting is based on collateral locked and unlocked during trading, not on these display fields.

For display purposes, positions follow these rules:

  1. The lifecycle of a position resets when touching zero. Fields like average entry price accumulate while the position stays open in one direction (long or short) and reset on touching or crossing zero.
  2. Average entry price updates only when the magnitude of the position increases (ie longing when already long, shorting when already short).
  3. Unrealized PnL is size * (mark - entry), where size is signed.
  4. Realized PnL updates by size_traded * (price - entry) on every closing trade.
  5. Realized and unrealized PnL do not include fees. At the end of a position's lifecycle, the realized PnL is exactly equal to the account's USDC balance change attributable to that position, excluding fees.

Self-Trade Prevention#

The matching engine prevents any fill where the same account would be both maker and taker by cancelling the maker order and continuing to match the taker.

Matching Rounds And Priority#

The exchange batches incoming orders into rounds and processes them every 50ms.

Within a round, orders are processed in three groups, in this order:

  1. Cancels
  2. Post-only placements: order place or replace requests with post_only: true.
  3. Other orders: IOC, GTC limits without post_only, GTT, market.

Requests within the same group are processed first come, first served by the order they were received by the write API.

If a cancel references a client_order_id whose placement is accepted later in the same round, it is applied immediately after that placement. A non-post-only order may therefore match before its remaining resting size is canceled. If the placement is rejected or leaves no resting order, the cancel is rejected because the target no longer exists.

This is comparable to Hyperliquid's matching algorithm.

Order Book#

Each market has a single limit order book with bid and ask sides. Within a side, orders are sorted by price, then by order of arrival. Order book insertion happens in the order described in Matching Rounds and Priority.

Tick Sizes#

Each market is parameterized with a tick_sig_figs and tick_size_min.

Both tick_size_min and tick_sig_figs are returned in each market's MarketSpec and on List Markets. For a market with tick_size_min = 0.001 and tick_sig_figs = 2, the tick size at various prices is:

Price range (px)Tick sizeExample valid prices
[0.10, 0.90]0.01000.50, 0.51, 0.85
[0.010, 0.100) or (0.900, 0.990]0.00100.025, 0.043, 0.975
< 0.010 or > 0.9900.0010 (floor)0.001, 0.997

Specifically, for a given price p:

p' = min(p, 1 - p) # Symmetrical around 50c
p_sigs = floor(log10(p')) + 1
tick_size = max(10^(p_sigs - tick_sig_figs), tick_size_min)

The min(p, 1 - p) mirror keeps tick precision symmetric around 0.50, so a market priced near $0.01 has the same precision as one priced near $0.99.

The intuition is to use a fixed number of significant figures regardless of how many leading zeros the price has, and the min tick size adds a bound as the price approaches the endpoints.

Program#

The following details are provided for informational purposes. None of them are necessary to trade on the exchange. The offchain APIs are the exclusive source of truth for exchange state.

Guarantees and Non-Guarantees#

The program enforces the following guarantees:

  1. Trader funds cannot move without a trader signature.
  2. Trades can only occur at crossing prices and with trader signatures.
  3. Once a trading key is revoked, orders placed with it cannot match.

The program does not guarantee the following:

  1. The program does not enforce the state of the order book. The offchain matching engine enforces matching priority.
  2. The program does not provide onchain oracles for market resolution.

Upgrade authority#

The program upgrade authority is secured with a Fordefi multisig. Accordingly key material is sharded across hardware secure enclaves. No upgrade keys are stored in AWS. Unlike an onchain multisig (eg Squads) this appears as a standard on-curve wallet onchain.

In the future the Pascal program will also include timelocked upgrades.

State#

Most exchange state is stored onchain in these large program-owned accounts:

Additional per-trader information, including trading key records, is stored in trader auxiliary PDAs. For example, the trader auxiliary PDA for owner 5Hu8swhmKFK5NG7GWxYsisHfgKzic8WCF2QLGmrDs4Kk is 1CforH2A6eXwYpR8z7GFfdt2ikUNYZ5qFeaeNUD1Wc8.

Buffers#

To improve throughput, all state transitions are first buffered before they are applied to exchange state.

The buffer accounts are 1, 2, 3, 4, 5, 6, 7, and 8.

When a buffer is full, its transitions are applied to exchange state and the buffer is emptied. This is why trades initially appear in transactions targeting one of the buffers rather than the exchange state accounts.

Deposit Addresses#

Traders do not deposit directly to exchange state. Instead, they deposit to program-controlled PDAs. The program then moves the funds into exchange state.

For example, the deposit PDA for owner GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB is DtzBRq9hAREJZKj1fuHsgrWrtaeXmbYQdXsmMrd54arG.

To find your registered deposit address, use the Deposit Address endpoint. Note that the deposit address PDA itself is never created (and thus won't show up on block explorers). Only the associated token account that stores the USDC is created.

Source code#

For now, the Pascal program is not source-available. The program's source code will be publicly available once we conclude security audits.

API#

Public reference for integrating Pascal's REST and WebSocket APIs.

All write requests are signed. Most read requests are unauthenticated; invite-code and referral reads require a signed request payload, as documented per endpoint. The Write and Read APIs use distinct base URLs; see Base URLs.

General Information#

Base URLs#

Use the production URLs for live trading:

ServiceURL
Webhttps://app.pascal.trade
Write APIhttps://trade.pascal.trade
Order Entry WebSocketwss://trade.pascal.trade/ws
REST Read APIhttps://data.pascal.trade
Market Data WebSocketwss://data.pascal.trade/ws

Geography#

The Pascal matching engine is located in AWS ap-northeast-1.

Rate Limits#

Rate limits are provisional during private beta, and may change. Please program your clients to respect HTTP 429 error codes.

Initial rate limits: 100 requests / second (note you can batch up to 50 orders per request).

Authentication and Request Authorization#

Signing Keys#

Each Solana wallet address owns one Pascal account. Pascal separates account ownership from trading authorization, so certain operations (moving funds, approving trading keys) require wallet signatures, while order placement and cancellation are signed by separate trading keys.

This allows accounts to use higher security custody, like hardware or MPC wallets, without prohibiting the use of hot keys for trading.

KeyUseUsed by
Wallet keyCustody, withdrawals, and trading-key lifecycleCreate/revoke trading keys, withdraw collateral, register deposit addresses
Trading keyOrder placement and cancellationPlace and cancel orders

In every signed request, owner is the wallet public key, also used as the Solana address, and signer is the key that produced the Ed25519 signature. Wallet-signed requests require signer == owner.

Request Authorization#

Every signed request carries a RequestAuth object with replay and staleness protection fields client_ts_ms and recv_window_ms:

RequestAuth#

Request authentication metadata used to validate client requests.

FieldTypeDescription
client_ts_msu64 stringClient timestamp in milliseconds since the Unix epoch. Used with recv_window_ms for replay and staleness checks.
recv_window_msu32 stringMaximum age of the request in milliseconds. The server accepts the request when server_time_ms - client_ts_ms <= recv_window_ms.
ownerbase58 stringAccount owner's wallet public key (Solana address).
signerbase58 stringPublic key that signed this request. Wallet-signed requests require signer == owner; trading-key requests use a registered trading key.
signaturebase58 stringEd25519 signature over the signed payload bytes for this request.

recv_window_ms may be at most 60,000ms (1 minute).

See Request Signing for constructing the exact payloads to sign over.

Info

The easiest way to start signing requests is with github.com/pascal-research-inc/pascal-python-quickstart

Generate A Trading Key In The Browser#

For most users the easiest way to get a trading key is through the Pascal web app. The web app handles wallet prompts, permit construction, and key generation.

  1. Open the Pascal web app, for example https://app.pascal.trade, and log in with the wallet of your choice. To keep wallet keys off the computer, use a hardware wallet or MPC solution.
  2. Click your account image in the upper right, then Trading Keys > Create Key.
  3. Give the key a name and save the private key somewhere secure.

Alternatively, to generate and assign a trading key programmatically, call Create A Trading Key directly with an Ed25519 public key and a wallet-signed create_trading_key permit.

Response Envelopes#

All REST API responses are wrapped in:

FieldTypeDescription
statusstring enumsuccess or error
dataobjectEndpoint response payload on success, otherwise error object.
sequ64 string(Optional) Matching engine sequence number. Not present if the request is rejected before reaching the matching engine, for example due to invalid request data.
state_time_msu64 string(Optional) Matching engine timestamp as of the last matching round. Not present if the request is rejected before reaching the matching engine, for example due to invalid request data.
server_time_msu64 stringAPI server timestamp when this result was produced. Requests in a batch request can resolve at different times, so items in one response may carry different timestamps.
server_receive_time_msu64 string(Optional) API server timestamp when the HTTP request is received. Present on write endpoint responses, including rejections and timeouts. Not present on read endpoint responses or WebSocket messages.

Request-validation and rate-limit error responses may omit server_time_ms.

Error data is an object with fields:

FieldTypeDescription
codeApiErrorCodeStable, machine-readable error code.
messagestringHuman-readable error message. May evolve over time. For debugging and display only.

Batch endpoints normally return an array of envelopes, one per submitted request, in the same order as the request batch. Request-level errors, including rate limits, return one error envelope.

Batch endpoints return HTTP 200 if any request in the batch succeeds. Inspect each item's status to determine which requests succeeded or failed. Other endpoints return 200 only on {"status": "success"}.

Success envelope

{
  "status": "success",
  "data": {
    "server_time_ms": "1731536000123"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Error envelope

{
  "status": "error",
  "data": {
    "code": "INVALID_REQUEST",
    "message": "count_back must be positive"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Batch response envelope

[
  {
    "status": "success",
    "data": {
      "id": "987654321",
      "client_order_id": "42",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "side": "BID",
      "price": "0.550000",
      "size_original": "10",
      "size_remaining": "0",
      "size_filled": "3",
      "notional_filled": "1.650000",
      "tif": "GTC",
      "type": "LIMIT",
      "post_only": false,
      "reduce_only": false,
      "seq": "67880",
      "update_seq": "67891",
      "place_ts_ms": "1731536000050",
      "update_ts_ms": "1731536000050"
    },
    "seq": "67891",
    "state_time_ms": "1731536000050",
    "server_time_ms": "1731536000123"
  },
  {
    "status": "error",
    "data": {
      "code": "ORDER_NOT_FOUND",
      "message": "client_order_id 43 is not an open order"
    },
    "seq": "67891",
    "state_time_ms": "1731536000050",
    "server_time_ms": "1731536000123"
  }
]

Pagination#

Paginated endpoints accept an optional limit and at most one starting point: before_cursor or at_or_before_seq.

  • at_or_before_seq allows starting pagination at a sequence number. Continue paginating using the cursor.
  • before_cursor is the cursor returned from a previous page.
  • limit uses min 1, default 100, max 500.

Treat cursor values as opaque. The internal format may change without notice; clients should only round-trip next_cursor back into before_cursor.

Paginated responses wrap the page in the standard response envelope. Its data object contains:

FieldTypeDescription
itemsarrayResult page items. The item schema is shown under each paginated endpoint.
next_cursorstring(Optional) Cursor for the next page of results. Omitted on the last page.

The envelope seq and state_time_ms indicate the freshness of the historical data.

See the historical Trades query for an example.

JSON Conventions#

ShapeEncoding
Decimal prices, USD amounts, rates, and feesString with 6 fractional digits unless the field description says otherwise. Price fields stay within 0.000000 to 1.000000.
Contract sizes, sequence numbers, ids, timestamps, and cursorsSerialized as strings
TimestampsUnix milliseconds.
Solana addresses, public keys, and Ed25519 signaturesBase58 strings.
Market symbolsUppercase string identifiers.

Sequence Numbers#

Responses to API calls that read or update exchange state include a seq number.

The seq is a global counter that increments on every exchange state change. It only increases. Note: it is not a per-connection message counter.

You can compare seq values across REST, WebSocket, and historical APIs to tell which state is newer. For example:

  • Order responses include the seq at which the order was placed.
  • REST account and order book snapshots include the snapshot's seq in the response envelope.
  • WebSocket messages include the seq of the state that produced the update.
  • Historical data queries include the highest seq of indexed state. For example, the historical trades response is guaranteed to include every trade where trade.seq <= envelope.seq.

For example, after placing an order, suppose the order response has seq = o. Then a WebSocket book update arrives with seq = w:

  • If w < o, the update is from state before your order was placed.
  • If w = o, the update is from the same state transition as your order.
  • If w > o, the update is from state after your order was placed.

Individual WebSocket channels can carry non-contiguous seq values because multiple state changes occur in each matching round and not every global state change affects each specific WebSocket channel.

Write Endpoints#

Place And Replace Orders#

POST <write_base_url>/api/v1/orders

Places or amends one or more orders. Each order is independently signed and independently succeeds or fails. The maximum batch size is 50.

Request Body

Request bodies are JSON arrays. Every element is a JSON object:

PlaceOrderRequest#

Client sends this request to the API to place an order.

FieldTypeDescription
client_order_idu64 stringClient-assigned order identifier. Must be unique across open orders for the same account. Must be < 2^64 - 1.
replace_client_order_idu64 string(Optional) If present, the order with replace_client_order_id will be cancelled and replaced atomically with this new order. If the replacement only reduces remaining size, the order preserves the existing queue position.
symbolstring (Symbol)Symbol for the market to trade.
sidestring enum: "BID", "ASK"Whether to buy (BID) or sell (ASK).
pricedecimal string, 6 d.p.Limit price in dollars.
sizeu64 stringOrder size in contracts.
tifTimeInForce(Optional) The order's TimeInForce policy. Defaults to GTC.
post_onlyboolean(Optional) If true, the order can only match as a maker. If it would cross immediately, the order is rejected.
reduce_onlyboolean(Optional) If true, the order can only reduce exposure and never increase it.
allow_missing_replaceboolean(Optional) If true, the order is placed even if replace_client_order_id is not found. Requires replace_client_order_id. Incompatible with replace_with_open_size.
replace_with_open_sizeboolean(Optional) If true, the replacement does not alter the open order's size. Set the request size equal to the open size you think the order has and sign over it. When this request lands, the matching engine will inspect the size remaining for replace_client_order_id, and set the new order's size equal to that. Incompatible with allow_missing_replace.
expires_ts_msu64 string(Optional) Expiration timestamp, required for GTT orders and forbidden otherwise.
builder_infoBuilderInfo(Optional) Builder attribution and fee settings, signed into the order permit. A present value must name a registered builder other than the order's own account; omitting the field is the only no-builder form. Rejected with BUILDER_INVALID for an unknown or self-named builder, BUILDER_FEE_RATE_INVALID for a fee rate above BUILDER_FEE_RATE_CAP, and BUILDER_COLLATERAL_BELOW_MINIMUM when the builder does not meet the minimum free collateral.
authRequestAuthSigned request authentication metadata.

tif (time in force) values:

TimeInForce#

Time in force for an order.

ValueDescription
"GTC"Good 'til canceled: the order rests on the book until it is fully filled or explicitly canceled.
"GTT"Good 'til time: like GTC, but the order is automatically canceled once expires_ts_ms passes.
"IOC"Immediate or cancel: fills against resting orders immediately; any unfilled remainder is canceled instead of resting on the book.

builder_info, when present, is a JSON object (see Builder Codes):

BuilderInfo#

Builder public key and fee rate for one order, signed into the order permit.

FieldTypeDescription
builder_pubkeybase58 stringRegistered builder account credited for this order.
builder_fee_ratedecimal string, 6 d.p.Builder fee rate. Each fill pays the builder size × price × (1 − price) × builder_fee_rate, rounded toward zero.

JSON Response Payload

Batch responses are JSON arrays of responses. On success, each response's data object has these fields:

FieldTypeDescription
orderOrderMsgThe state of the order after placement, including any fills that occurred immediately.
fillsarray of FillMsgFills that occurred during the placement of the order.

order object schema:

OrderMsg#

Order message for API responses.

FieldTypeDescription
idu64 stringThe order's unique identifier, assigned by the exchange.
client_order_idu64 stringClient ID for the order.
replace_client_order_idu64 string(Optional) Present when this order was placed as a replacement.
symbolstring (Symbol)The symbol of the market being traded.
sidestring enum: "BID", "ASK"The side of the order.
pricedecimal string, 6 d.p.The limit price of the order in dollars.
size_originalu64 stringThe order size in contracts at placement time.
size_remainingu64 stringRemaining open quantity, matching FIX LeavesQty semantics. The order is closed when this is zero, whether it filled completely, was canceled, expired, or was resolved by the matching engine. Note that an order's size can be reduced by the matching engine due to reduce_only or self trade prevention. Use size_filled to determine filled size, not size_remaining.
size_filledu64 stringThe cumulative size of the order that has been filled.
notional_filleddecimal string, 6 d.p.The cumulative sum of price * size for fills to this order. Divide by size_filled to get the average fill price.
tifTimeInForceThe order's TimeInForce.
typestring enum: "LIMIT", "MARKET"Order type. LIMIT is a standard limit order. MARKET is for IOCs submitted via the front end market tab. For display purposes.
post_onlybooleanIf true, the order is only allowed to fill as a maker.
reduce_onlybooleanIf true, the order is only allowed to reduce existing exposure and never increase it.
expires_ts_msu64 string(Optional) The expiration timestamp of the order in milliseconds since the Unix epoch, omitted if the order is not GTT.
builder_pubkeybase58 string(Optional) Builder named by this order, omitted when the order has no builder.
builder_fee_ratedecimal string, 6 d.p.(Optional) Builder fee rate signed into this order, omitted when the order has no builder.
sequ64 stringThe sequence number when this order was placed.
update_sequ64 stringThe sequence number when this order last changed.
place_ts_msu64 stringThe timestamp the order was placed at.
update_ts_msu64 stringThe timestamp when this order last changed.

Every element in fills is a JSON object:

FillMsg#

Fill message for API responses.

FieldTypeDescription
trade_idu64 stringUnique ID for this trade.
symbolstring (Symbol)Symbol for the market being traded.
sidestring enum: "BID", "ASK"Side of the order that was filled.
fill_pricedecimal string, 6 d.p.Execution price in dollars.
fill_sizeu64 stringFilled quantity in contracts.
liquiditystring enum: "TAKER", "MAKER"Whether this fill removed liquidity (TAKER) or provided it (MAKER).
order_idu64 stringExchange ID of the order that was filled.
client_order_idu64 stringClient ID of the order that was filled.
fee_usdsigned decimal string, 6 d.p.Venue fee charged for this fill, excluding builder fees. Positive values are fees paid; negative values are rebates.
collateral_change_usdsigned decimal string, 6 d.p.This fill side's collateral change: position collateral change minus the side's fees. Credits the account receives from the same trade (builder fees received or a referral reward) are separate and not included.
position_size_previ64 stringSigned position size in this market immediately before the fill.
realized_pnl_usdsigned decimal string, 6 d.p.Realized PnL attributed to this fill, excluding fees.
position_open_sequ64 string(Optional) Opening sequence of the position instance associated with this fill. On a flip, this identifies the position closed by the fill rather than the residual position it opens.
position_open_ts_msu64 string(Optional) Opening timestamp, in milliseconds since the Unix epoch, of the position instance associated with this fill. On a flip, this identifies the position closed by the fill rather than the residual position it opens.
builder_pubkeybase58 string(Optional) Builder named by the filled order, omitted when the order has no builder.
builder_fee_paid_usddecimal string, 6 d.p.(Optional) Builder fee paid to the builder for this fill, included in collateral_change_usd; omitted when the order has no builder.
sequ64 stringMatching engine sequence number when this fill occurred.
trade_ts_msu64 stringTimestamp when this fill occurred, in milliseconds since the Unix epoch.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/orders \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
[
  {
    "client_order_id": "42",
    "symbol": "SIM_EVENT_1.MARKET_1",
    "side": "BID",
    "price": "0.550000",
    "size": "10",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "2rUctQ9bFbuQaUxbXDGRtGm4LMj3GdbYWAW6pjrznAsLvpdH8Eqyx3LgsFx3YzV9cvMfw1ND5T5nChUQZZJ7jEad"
    }
  }
]
EOF
Response
json
[
  {
    "status": "success",
    "data": {
      "order": {
        "id": "987654321",
        "client_order_id": "42",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "price": "0.550000",
        "size_original": "10",
        "size_remaining": "7",
        "size_filled": "3",
        "notional_filled": "1.650000",
        "tif": "GTC",
        "type": "LIMIT",
        "post_only": false,
        "reduce_only": false,
        "seq": "67880",
        "update_seq": "67891",
        "place_ts_ms": "1731536000050",
        "update_ts_ms": "1731536000050"
      },
      "fills": [
        {
          "trade_id": "555001",
          "symbol": "SIM_EVENT_1.MARKET_1",
          "side": "BID",
          "fill_price": "0.550000",
          "fill_size": "3",
          "liquidity": "TAKER",
          "order_id": "987654321",
          "client_order_id": "42",
          "fee_usd": "0.000742",
          "collateral_change_usd": "-1.650742",
          "position_size_prev": "0",
          "realized_pnl_usd": "0.000000",
          "seq": "67891",
          "trade_ts_ms": "1731536000050"
        }
      ]
    },
    "seq": "67891",
    "state_time_ms": "1731536000050",
    "server_time_ms": "1731536000123"
  }
]

Cancel Orders#

POST <write_base_url>/api/v1/cancels

Cancels one or more orders by client order id or exchange order id. Each cancel is independently signed and independently succeeds or fails. The maximum batch size is 50. Canceling by client order id and canceling by exchange order id have equivalent performance.

Request Body

Request bodies are JSON arrays. Every element is a JSON object:

CancelOrderRequest#

Client sends this request to the API to cancel an order.

FieldTypeDescription
client_order_idu64 string(One of) Client ID of the order to cancel.
order_idu64 string(One of) Exchange ID of the order to cancel.
authRequestAuthSigned request authentication metadata.

JSON Response Payload

Batch responses are JSON arrays of write envelopes. On success, each envelope's data object is an OrderMsg with the cancelled order's final state.

Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/cancels \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
[
  {
    "client_order_id": "42",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "591YziUjZzro24mamxgRrSDdYD71NpsDBmU4UGSyrBYbFiWzYaLjR9he1P5DP8djMKMJzHTsZzwRAagvLqgtiXnc"
    }
  },
  {
    "client_order_id": "43",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "3wFZKJWKA92W6iPszoKQccQmrVEfd6hU68HY6mqWYKEH8yFKQEFRJs1tKmrGDy1PMd3wWwcdcYSVoTgrW5KSnP3f"
    }
  }
]
EOF
Response
json
[
  {
    "status": "success",
    "data": {
      "id": "987654321",
      "client_order_id": "42",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "side": "BID",
      "price": "0.550000",
      "size_original": "10",
      "size_remaining": "0",
      "size_filled": "3",
      "notional_filled": "1.650000",
      "tif": "GTC",
      "type": "LIMIT",
      "post_only": false,
      "reduce_only": false,
      "seq": "67880",
      "update_seq": "67891",
      "place_ts_ms": "1731536000050",
      "update_ts_ms": "1731536000050"
    },
    "seq": "67891",
    "state_time_ms": "1731536000050",
    "server_time_ms": "1731536000123"
  },
  {
    "status": "success",
    "data": {
      "id": "987654322",
      "client_order_id": "43",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "side": "BID",
      "price": "0.550000",
      "size_original": "10",
      "size_remaining": "0",
      "size_filled": "3",
      "notional_filled": "1.650000",
      "tif": "GTC",
      "type": "LIMIT",
      "post_only": false,
      "reduce_only": false,
      "seq": "67880",
      "update_seq": "67891",
      "place_ts_ms": "1731536000050",
      "update_ts_ms": "1731536000050"
    },
    "seq": "67891",
    "state_time_ms": "1731536000050",
    "server_time_ms": "1731536000123"
  }
]

Create A Trading Key#

POST <write_base_url>/api/v1/trading-keys

Authorizes a trading key for an account. This request is wallet-signed.

Trading key names may be at most 16 bytes long, and their expiration timestamps may be no more than 31,536,000,000ms (365 days) in the future. An account may store at most 10 unrevoked trading keys at a time. Expired keys count against this limit until revoked.

Request Body

FieldTypeDescription
trading_keybase58 stringEd25519 public key to authorize for trading on this account.
namestringHuman-readable label for this trading key. This name is public onchain.
expiration_ts_msu64 stringTimestamp when this trading key expires, in milliseconds since the Unix epoch.
authRequestAuthSigned request authentication metadata.

JSON Response Payload

On success, the response envelope's data object has these fields:

TradingKeyMsg#

Trading key entry in API responses.

FieldTypeDescription
trading_keybase58 stringEd25519 public key authorized to sign trading requests for this account.
namestringHuman-readable label for this trading key. This name is public onchain.
expiration_ts_msu64 stringTimestamp when this trading key expires, in milliseconds since the Unix epoch.
open_order_countu64 stringNumber of this account's currently open orders placed using this trading key.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/trading-keys \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "name": "api-doc-key",
  "expiration_ts_ms": "1732140800000",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "kyc2KdqPTeEWqoK3pXR2rgPUiK4zXg9QdQffPUWk8eALpFy7uaHBEsASt7RQyVzDeubHVsPqQAHXrt8m8KZ78Ep"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "name": "api-doc-key",
    "expiration_ts_ms": "1732140800000",
    "open_order_count": "0"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Revoke A Trading Key#

POST <write_base_url>/api/v1/trading-key-revocations

Revokes a previously authorized trading key. This request is wallet-signed.

Request Body

FieldTypeDescription
trading_keybase58 stringEd25519 public key of the trading key to revoke.
authRequestAuthSigned request authentication metadata.

JSON Response Payload

On success, the response envelope's data object has these fields:

FieldTypeDescription
trading_keybase58 stringEd25519 public key of the revoked trading key.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/trading-key-revocations \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "3muKct6upyRNAr1CPdYcxQGCojpuB4gaEnWvbNwEBJ6rrKaThRZrKY56rpYbFEsGUZqJ4iCTJqYrPuxfGYVzXQX3"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Withdraw Collateral#

POST <write_base_url>/api/v1/withdrawals

Requests withdrawal of collateral to a destination token account. This request is wallet-signed.

Clients do not send a fee field. amount is the gross amount debited from the account; the exchange sends amount minus the current signed withdrawal fee to destination_token_account. Read the active fee from GET /api/v1/features (withdrawal_fee_usd).

Destination address

Derive the destination_token_account associated token account (ATA) with the standard Solana formula: PDA seeds [wallet, spl_token_program_id, usd_token_mint] under the Associated Token program.

Request Body

FieldTypeDescription
amountdecimal string, 6 d.p.Amount of collateral to withdraw in USD. The exchange debits this amount from the account and sends amount minus the withdrawal fee to the destination.
destination_authoritybase58 stringSolana wallet address that will receive the withdrawal. This is the address the user enters in a "withdraw to" field, not a token account address.
destination_token_accountbase58 stringUSD associated token account (ATA) for destination_authority. Must equal the ATA derived from destination_authority and the deployment's USD token mint. Clients derive this locally before signing because both fields are included in the signed withdrawal permit.
authRequestAuthSigned request authentication metadata.

JSON Response Payload

On success, the response envelope's data object has these fields:

FieldTypeDescription
ownerbase58 stringAccount owner's wallet public key (Solana address).
amountdecimal string, 6 d.p.Amount of collateral withdrawn in USD.
destination_authoritybase58 stringSolana wallet address that received the withdrawal.
destination_token_accountbase58 stringUSD associated token account (ATA) for destination_authority that received the withdrawal.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/withdrawals \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "amount": "25.000000",
  "destination_authority": "29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2",
  "destination_token_account": "3JF3sEqM796hk5WFqA6EtmEwJQ9quALszsfJyvXNQKy3",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "61YYUBk6BTXkfrksMbqDLUNxwo6jhMPqdP7fNnWjNF2FYvMDujTsaUxfCiRxXX72jxy4oLtt6AtLodCvjDi4XpDn"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "amount": "25.000000",
    "destination_authority": "29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2",
    "destination_token_account": "3JF3sEqM796hk5WFqA6EtmEwJQ9quALszsfJyvXNQKy3"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Register A Deposit Address#

POST <write_base_url>/api/v1/deposit-addresses

Registers the account's deposit address for balance scanning. This request is wallet-signed. Check Features for the attribution mechanism currently active: while invite_codes_enabled is true, a new account must include an invite_code; while referral_codes_enabled is true, a new account may include a referrer's referral_code to credit the referral. Submit at most one of the two codes; requests carrying both, or a referral_code while referral codes are not enabled, are rejected. A registration carrying a Referral Code succeeds only when attribution is created. If the code is unknown, revoked, self-referring, or otherwise cannot resolve, the request fails with REFERRAL_CODE_INVALID; the account remains unregistered and may retry with a corrected code or without one. The Reward Owner can observe a successfully created Referral through their Referrals list.

Info

Pascal uses USDC as its sole collateral token. Only send USDC to your deposit address.

Request Body

FieldTypeDescription
ownerbase58 stringAccount owner's wallet public key (Solana address).
invite_codestring(Optional) Invite or Promo Code submitted for registration. Required when invite_codes_enabled is true in GET /api/v1/features. Specify at most one of this field and referral_code; requests carrying both are rejected.
referral_codestring(Optional) Referral code to credit for the referral, exactly as shared by the referrer. Accepted only while referral_codes_enabled is true in GET /api/v1/features; rejected otherwise. Specify at most one of this field and invite_code; requests carrying both are rejected. When supplied, registration succeeds only if referral attribution is created. An unknown, revoked, self-referring, or otherwise invalid code rejects registration with REFERRAL_CODE_INVALID; the account remains unregistered and can retry with a corrected code or without one.
authRequestAuthSigned request authentication metadata.

JSON Response Payload

FieldTypeDescription
ownerbase58 stringThe account owner.
deposit_addressbase58 string(Optional) The deposit address PDA corresponding to the account owner. Present only after the account has registered a deposit address, and immutable once set.
registeredbooleanWhether the deposit address is registered.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/deposit-addresses \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "invite_code": "4PHPW-S4BM2-U9MS6-KP2JF",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "3y43tLaBWdQyxkj7BAbbSEmGfd6Fu3Xop5ATyWcLiC4XYFgNJSmoKXmyC8Qnu7oRUzAnEycmfSLzdyAV5L3cxecc"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "deposit_address": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "registered": true
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Generate A Referral Code#

POST <write_base_url>/api/v1/referral-codes

Creates the owner's reusable referral code. This request is signed by an active trading key. Each owner can generate one code, and generation requires a deposited account. Use Features to check whether referral codes are enabled (referral_codes_enabled), and Referrals for the owner's current code and the can_generate_referral_code flag.

A new user referred with this code submits it as referral_code in Register A Deposit Address, which creates a Referral under the Referral Program.

Request Body

FieldTypeDescription
authRequestAuthTrading-key-signed request authentication metadata.

JSON Response Payload

On success, the response envelope's data object has these fields:

FieldTypeDescription
generated_referral_codestringThe owner's reusable referral code.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/referral-codes \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "4UpZqQfCNDbj3NEnkkRUQsW5USTTcv7JekvPWyHsEC2ifcc3R8PwEVd5xVWZuSNacyNHrZg29ZqPeUME3GWGpnCN"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "generated_referral_code": "k7wq2m4x"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Claim A Custom Referral Code#

POST <write_base_url>/api/v1/referral-codes/custom

Claims the owner's one-time Custom Referral Code. This request is signed by an active trading key, requires a deposited account, and succeeds at most once per owner. Use Features for custom_referral_codes_enabled, and Referrals for the owner's current code and can_claim_custom_referral_code.

The code must contain 6–20 lowercase ASCII letters or digits and is not normalized by the server. Four- and five-character values fail with referral_code_too_short. Values shorter than four characters or otherwise outside the Referral Code format fail request parsing. Codes that are occupied or fail content policy fail with referral_code_unavailable.

Request Body

FieldTypeDescription
referral_codestringExact owner-chosen code: 6–20 lowercase ASCII letters or digits. The exchange does not normalize this value.
authRequestAuthTrading-key-signed request authentication metadata.

JSON Response Payload

On success, the response envelope's data object has these fields:

FieldTypeDescription
custom_referral_codestringThe owner's newly claimed Custom Referral Code.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/referral-codes/custom \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "referral_code": "trader1",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "26FzDQPEDR6u7rKhkaEEJL1YSyKpbUCCibQtQxkDfEfEjyAsJu17X5p8TtuW5iXQfCNbs9gjEsFecqPv4yffwsgS"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "custom_referral_code": "trader1"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Set A Username#

POST <write_base_url>/api/v1/usernames

Sets, renames, or clears the authenticated owner's Username. This request is signed by an active trading key and requires a deposited account.

Usernames contain 3 to 20 ASCII letters, digits, underscores, or hyphens. Submitted casing is preserved, but uniqueness is case-insensitive. Send an empty string to clear the current Username. An unavailable Username returns CONFLICT with the message That name is taken.

Request Body

FieldTypeDescription
usernamestringOwner-chosen Username, or an empty string to clear the current Username.
authRequestAuthTrading-key-signed request authentication metadata.

JSON Response Payload

FieldTypeDescription
usernameUsername(Optional) The now-active Username, or null after a clear.
Examples
Request
bash
curl -X POST https://trade.pascal.trade/api/v1/usernames \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "username": "Trader",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "2aEt4QQeG3iFTD6tAZvDnfKzna7cnVh18eq6cveGho9mdwfDGLoFjs1fsyYieXuZKKGdpFFCH8VgvVQJ2K1BBmjV"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "username": "Trader"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Order Entry WebSocket#

GET wss://trade.pascal.trade/ws upgrades to a WebSocket connection for placing and cancelling orders.

Submission via WebSocket may be useful to users who want many concurrent orders in flight and also want guaranteed submission ordering.

Note:

  • WebSocket messages sent on one connection enter matching in the order you sent them, so an order can replace or cancel an order whose acknowledgement has not arrived yet (see Matching Rounds And Priority)
  • Send up to 128 concurrent messages per connection. At the cap the server stops reading the socket until responses are delivered.
  • Rate limit of 1,000 requests per second (counting batch items and pings).
  • Batch and message size limits are the same as for REST: 50 requests per frame, 64 KiB per frame.
  • Send {"type":"ping"} messages to keep the connection open. Connections idle for 60 seconds are closed.
  • Responses do not necessarily arrive in the order requests were sent. Use the id field on the request envelope to identify responses.

Client Requests#

Send JSON place and cancel messages shaped like

{
  "id": "42",
  "type": "place|cancel|ping",
  "payload": "/* same place or cancel payload as REST */"
}

The payload matches the Place And Replace Orders array for place, or the Cancel Orders array for cancel exactly.

id is an optional field echoed back on the message's response, to help the client match responses to requests.

Ping keeps the connection alive and verifies the server end-to-end. The server responds with {"type":"pong"}.

Examples
Place Request
json
{
  "id": "1",
  "type": "place",
  "payload": [
    {
      "client_order_id": "42",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "side": "BID",
      "price": "0.550000",
      "size": "10",
      "auth": {
        "client_ts_ms": "1731536000000",
        "recv_window_ms": "5000",
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
        "signature": "2rUctQ9bFbuQaUxbXDGRtGm4LMj3GdbYWAW6pjrznAsLvpdH8Eqyx3LgsFx3YzV9cvMfw1ND5T5nChUQZZJ7jEad"
      }
    }
  ]
}
Request
json
{
  "id": "2",
  "type": "cancel",
  "payload": [
    {
      "client_order_id": "42",
      "auth": {
        "client_ts_ms": "1731536000000",
        "recv_window_ms": "5000",
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
        "signature": "591YziUjZzro24mamxgRrSDdYD71NpsDBmU4UGSyrBYbFiWzYaLjR9he1P5DP8djMKMJzHTsZzwRAagvLqgtiXnc"
      }
    }
  ]
}

Server Responses#

The server sends one response message per request message, but not necessarily in request order. Match request/response messages by the echoed id, or match individual payload entries by client_order_id.

Requests rejected before entering the write pipeline (over the rate limit, under backpressure, unparseable, or over the batch size limit) get an error frame instead, echoing the frame's id and the rejected client_order_ids (both absent when they could not be parsed). Unparseable frames carry the parse error in the error frame's message; repeated ones close the connection (see Close Codes).

Examples
Place Response
json
{
  "id": "1",
  "type": "place",
  "payload": [
    {
      "status": "success",
      "data": {
        "order": {
          "id": "987654321",
          "client_order_id": "42",
          "symbol": "SIM_EVENT_1.MARKET_1",
          "side": "BID",
          "price": "0.550000",
          "size_original": "10",
          "size_remaining": "7",
          "size_filled": "3",
          "notional_filled": "1.650000",
          "tif": "GTC",
          "type": "LIMIT",
          "post_only": false,
          "reduce_only": false,
          "seq": "67880",
          "update_seq": "67891",
          "place_ts_ms": "1731536000050",
          "update_ts_ms": "1731536000050"
        },
        "fills": [
          {
            "trade_id": "555001",
            "symbol": "SIM_EVENT_1.MARKET_1",
            "side": "BID",
            "fill_price": "0.550000",
            "fill_size": "3",
            "liquidity": "TAKER",
            "order_id": "987654321",
            "client_order_id": "42",
            "fee_usd": "0.000742",
            "collateral_change_usd": "-1.650742",
            "position_size_prev": "0",
            "realized_pnl_usd": "0.000000",
            "seq": "67891",
            "trade_ts_ms": "1731536000050"
          }
        ]
      },
      "seq": "67891",
      "state_time_ms": "1731536000050",
      "server_time_ms": "1731536000123"
    }
  ]
}
Response
json
{
  "id": "2",
  "type": "cancel",
  "payload": [
    {
      "status": "success",
      "data": {
        "id": "987654321",
        "client_order_id": "42",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "price": "0.550000",
        "size_original": "10",
        "size_remaining": "0",
        "size_filled": "3",
        "notional_filled": "1.650000",
        "tif": "GTC",
        "type": "LIMIT",
        "post_only": false,
        "reduce_only": false,
        "seq": "67880",
        "update_seq": "67891",
        "place_ts_ms": "1731536000050",
        "update_ts_ms": "1731536000050"
      },
      "seq": "67891",
      "state_time_ms": "1731536000050",
      "server_time_ms": "1731536000123"
    }
  ]
}
Error
json
{
  "id": "3",
  "type": "error",
  "payload": {
    "data": {
      "code": "RATE_LIMITED",
      "message": "rate limited; slow down before retrying"
    },
    "client_order_ids": [
      "42"
    ]
  }
}

Close Codes#

CodeReasonMeaning
1000idleNo inbound frames and no pending responses for the idle timeout.
1001going_awayServer restarting; reconnect.
1009Frame exceeded the 64 KiB message size limit.
4001slow_consumerClient stopped reading responses.
4002rate_limitedClient kept sending after repeated rate-limit errors.
4003malformed_frameClient kept sending invalid messages after repeated errors.

Clients closed with malformed_frame or rate_limited are rejected on subsequent upgrades with HTTP 429 for several minutes.

Market Data Endpoints#

Server Time#

GET <read_base_url>/api/v1/time

Returns the read API server's current wall-clock time in milliseconds.

JSON Response Payload

FieldTypeDescription
server_time_msu64 stringAPI server wall-clock time in milliseconds since the Unix epoch.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/time'
Response
json
{
  "status": "success",
  "data": {
    "server_time_ms": "1731536000123"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Features#

GET <read_base_url>/api/v1/features

Returns feature flags that clients may need before choosing a write flow.

JSON Response Payload

FieldTypeDescription
invite_codes_enabledbooleanWhether address registration currently requires a valid Invite Code.
referral_codes_enabledbooleanWhether Referral Codes are live: they can be generated, and registrations may carry a referral_code. Both are rejected while this is false.
custom_referral_codes_enabledbooleanWhether owner-chosen Custom Referral Codes can currently be claimed.
usernames_enabledbooleanWhether owner-chosen Usernames can currently be set.
withdrawal_fee_usddecimal string, 6 d.p.Fee charged for a signed withdrawal.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/features'
Response
json
{
  "status": "success",
  "data": {
    "invite_codes_enabled": true,
    "referral_codes_enabled": false,
    "custom_referral_codes_enabled": false,
    "usernames_enabled": false,
    "withdrawal_fee_usd": "0.500000"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Usernames#

GET <read_base_url>/api/v1/usernames?owners={owner_1},{owner_2}

Returns active Usernames for up to 100 account owners. Owners without a Username are omitted. The request rejects an empty owner list, duplicate owners, and lists over the limit.

Query Parameters

FieldTypeDescription
ownersarray of base58 stringComma-delimited list of account owners whose Usernames should be returned.

JSON Response Payload

FieldTypeDescription
usernamesobject mapping base58 string to UsernameActive Usernames keyed by owner. Requested owners without a Username are omitted.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/usernames?owners=GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB,J2xccRtuG43drESLYznHhLhQkLTdfepcKYbiQ9BsJVaf'
Response
json
{
  "status": "success",
  "data": {
    "usernames": {
      "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB": "Trader"
    }
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Referral Code#

GET <read_base_url>/api/v1/referral-codes/{referral_code}

Returns whether referral_code currently resolves to a Reward Owner, plus the taker discount terms a referred user receives at registration. Revoked Custom Referral Codes return exists: false. See the Referral Program for how both sides of a Referral work.

Codes are 4 to 20 characters of lowercase a-z0-9; other values are rejected with invalid_request.

JSON Response Payload

FieldTypeDescription
existsbooleanWhether this Referral Code currently resolves to a Reward Owner.
taker_fee_discount_sharedecimal string, 6 d.p.Fraction of each taker fee discounted for a user referred by this code.
fee_savings_limit_usddecimal string, 6 d.p.Maximum taker fee savings for a user referred by this code. The discount stops after cumulative savings cross this limit.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/referral-codes/k7wq2m4x'
Response
json
{
  "status": "success",
  "data": {
    "exists": true,
    "taker_fee_discount_share": "0.050000",
    "fee_savings_limit_usd": "250.000000"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

List Markets#

GET <read_base_url>/api/v1/markets

Returns unresolved markets and their current state, ordered by symbol.

JSON Response Payload

Returns a JSON array of market objects. Every element in the array is a JSON object:

MarketStateMsg#

Contains the market spec, display attributes, and current state exposed by the Read API.

FieldTypeDescription
symbolstring (Symbol)Human-readable identifier for a market. Globally Unique. Eg "NYCMAYOR_25NOV04.MAMDANI".
taker_fee_ratedecimal string, 6 d.p.Base fee rate paid by takers. Decimal string, e.g. "0.0010" for 10 bps. For a trade of sz at px, the taker fee is sz * px * (1 - px) * taker_fee_rate.
maker_rebate_sharedecimal string, 6 d.p.Share of taker fee rebated to makers. Decimal string, e.g. "0.50" for 50%. For a trade of sz at px, the maker earns sz * px * (1 - px) * taker_fee_rate * maker_rebate_share.
tick_size_mindecimal string, 6 d.p.Minimum tick size. The tick size for a given price px is max(tick_size_min, 10^(floor(log10(px')) - tick_sig_figs + 1)), where px' = min(px, 1 - px). The intuition is that we want some amount of decimal precision available, but we don't want to count 0 prefixes. For example, 0.0025, 0.025 and 0.25 all have 2 digits of precision.
tick_sig_figsintegerNumber of significant figures in the tick size formula. Must be in [1, 6].
display_attributesobject mapping string to JSON valueMarket display metadata for UI rendering. Keys and shape match MarketDisplayAttributes.
listing_ts_msu64 string(Optional) Timestamp when the market was listed, in milliseconds since the Unix epoch.
rules_urlstring(Optional) URL to the market's resolution rules document, if available.
statsMarketStatsMsg(Optional) Stats.
mark_pricedecimal string, 6 d.p.(Optional) Median of whichever of (best bid, best ask, last trade) are available.
open_interestu64 stringCurrent open interest.
resolutionMarketResolutionMsg(Optional) Final market resolution, if this market has resolved.

display_attributes object:

MarketDisplayAttributes#

Additional market attributes used for display purposes only.

FieldTypeDescription
event_descriptionstringHuman-readable description of the event, has to fit in-line. Unenforced max 50 chars. For example, 2025 NYC Mayoral Election.
market_descriptionstringShort label for this market, displayed in the context of event_description. For example, Mamdani or Yes.
reverse_descriptionstring(Optional) Short label for this market to display if the user has reversed the UI. Mirrors market_description. For example, if the market is YANKEES_VS_METS.YANKEES the market_description might be "Yankees win". In that case the reverse_description would be something like "Mets win". None indicates reversing the UI is not available for this market. This is a UI-only field.
colorsMarketDisplayColors(Optional) Theme colors for this market's outcomes, present on head-to-head sections such as a moneyline. Use the values as supplied.
topicTopicInfo(Optional) Higher-level topic this event belongs to, if any.
game_pageGamePageDisplayInfo(Optional) Display metadata for sections (game lines) under the same game. When absent, clients should use regular event-based display.
market_typeMarketType(Optional) Typed market semantics for display and validation.
tagsarray of stringFirst-letter-capitalized tags for filtering and discovery.
expected_event_start_time_msu64 string(Optional) Expected start time of the event. Indicative, for display only.
expected_resolution_time_msu64 stringExpected date for market resolution. Indicative, for display only.
sort_orderMarketSortDefault UI sort order
rangeRangeDisplayAttributes(Optional) Deprecated alias for scalar-market display metadata, retained during client migration.
scalarRangeDisplayAttributes(Optional) Scalar-market display metadata.
referenceReferenceAttributes(Optional) Reference that informs this market's resolution criteria.

display_attributes.scalar and legacy display_attributes.range objects:

RangeDisplayAttributes#

FieldTypeDescription
upperi16 stringUpper bound of the scalar market outcome space, from -999 to 999.
loweri16 stringLower bound of the scalar market outcome space, from -999 to 999.
scaleu8 stringHow many decimal points to move over in the bounds. For example, 15 with a scale of 1 is 1.5.
prefixstringString to display before the outcome-space price. EG: "$"
suffixstringString to display after the outcome-space price. EG: "K", "B", "T"

display_attributes.topic object:

TopicInfo#

Higher-level category that links related events for display.

FieldTypeDescription
idstringUsed by the front end for grouping and filtering. For display only.
descriptionstringHuman-readable label for this topic. For display only.

display_attributes.game_page object:

GamePageDisplayInfo#

Display metadata for sections (game lines) under the same game.

FieldTypeDescription
game_idstringStable frontend grouping/filtering id, e.g. fifa-world-cup-fra-irq-2026-06-22.
game_descriptionstringHuman-readable game title, e.g. France vs Iraq.
section_idstringStable section (game line) id, e.g. moneyline, spreads, or totals.
section_descriptionstringHuman-readable section (game line) label, e.g. Spreads, Totals.
section_display_orderintegerSection display order within the game. This orders sections, not rows.
row_display_orderinteger(Optional) Explicit row display order within the section.
subject_idstring(Optional) Stable subject slug for team/player-specific rows, e.g. lionel-messi.
subject_descriptionstring(Optional) Human-readable subject label, e.g. Lionel Messi.
valuestring(Optional) Normalized numeric value for a game line. Spread values are signed from the subject's perspective, e.g. -1.5; total values are unsigned, e.g. 2.5.

display_attributes.colors object, present on head-to-head sections such as a moneyline. Use the color for the active theme as supplied.

MarketDisplayColors#

Theme colors for a market's outcomes.

FieldTypeDescription
marketThemeDisplayColorsColors for the market_description outcome.
reverseThemeDisplayColors(Optional) Colors for the reverse_description outcome, when it names the other side of a head-to-head comparison.

display_attributes.colors.market and display_attributes.colors.reverse objects:

ThemeDisplayColors#

A display color for each theme, as an opaque #RRGGBB value. Use the one for the active theme as supplied.

FieldTypeDescription
lightstringColor to use on Pascal's light-theme surfaces.
darkstringColor to use on Pascal's dark-theme surfaces.

display_attributes.market_type enum:

MarketType#

Typed market category and category-specific details for a listed market.

For example, sports markets use kind: "sports" with a nested sport value such as "soccer".

Value
"sports"

"sports" fields

FieldTypeDescription
kindstring literal "sports"Discriminator.
0SportsMarketType0.

display_attributes.market_type.sport enum for kind: sports market types:

SportsMarketType#

Sport-specific market type details.

Value
"soccer"
"baseball"
"league_of_legends"
"counter_strike"
"tennis"
"football"
"basketball"

"soccer" fields

FieldTypeDescription
sportstring literal "soccer"Discriminator.
periodSoccerPeriodPeriod.
teamsHomeAwayTeamsTeams.
marketSoccerMarketMarket.

"baseball" fields

FieldTypeDescription
sportstring literal "baseball"Discriminator.
periodBaseballPeriodPeriod.
teamsHomeAwayTeamsTeams.
marketBaseballMarketMarket.

"league_of_legends" fields

FieldTypeDescription
sportstring literal "league_of_legends"Discriminator.
series_formatBestOfSeriesFormatSeries format.
teamsHomeAwayTeamsTeams.
marketLeagueOfLegendsMarketMarket.

"counter_strike" fields

FieldTypeDescription
sportstring literal "counter_strike"Discriminator.
series_formatBestOfSeriesFormatSeries format.
teamsHomeAwayTeamsTeams.
marketCounterStrikeMarketMarket.

"tennis" fields

FieldTypeDescription
sportstring literal "tennis"Discriminator.
periodTennisPeriodPeriod.
match_formatTennisMatchFormatMatch format.
teamsHomeAwayTeamsTeams.
marketTennisMarketMarket.

"football" fields

FieldTypeDescription
sportstring literal "football"Discriminator.
periodFootballPeriodPeriod.
teamsHomeAwayTeamsTeams.
marketFootballMarketMarket.

"basketball" fields

FieldTypeDescription
sportstring literal "basketball"Discriminator.
periodBasketballPeriodPeriod.
teamsHomeAwayTeamsTeams.
marketBasketballMarketMarket.

display_attributes.market_type.period enum for soccer markets:

SoccerPeriod#

Portion of a soccer match that a market settles on.

ValueDescription
"reg_time"Regular time plus stoppage time, excluding extra time and penalties.
"first_half"First half plus stoppage time, excluding second half, extra time, and penalties.
"second_half"Second half plus stoppage time, excluding first half, extra time, and penalties.

display_attributes.market_type.period enum for baseball markets:

BaseballPeriod#

Portion of a baseball game that a market settles on.

ValueDescription
"full_game"Official full-game result, including extra innings.

display_attributes.market_type.period enum for football markets:

FootballPeriod#

Portion of a football game that a market settles on.

ValueDescription
"full_game"Official full-game result, including any overtime.

display_attributes.market_type.period enum for basketball markets:

BasketballPeriod#

Portion of a basketball game that a market settles on.

ValueDescription
"full_game"Official full-game result, including any overtime.

display_attributes.market_type.period enum for tennis markets:

TennisPeriod#

Portion of a tennis match that a market settles on.

ValueDescription
"full_match"Official full-match result, including tiebreaks.

display_attributes.market_type.match_format enum for tennis markets:

TennisMatchFormat#

Number of sets in a completed tennis match.

Value
"best_of_three"
"best_of_five"

display_attributes.market_type.series_format enum for best-of esports markets:

BestOfSeriesFormat#

Format of a best-of series.

Value
"best_of_one"
"best_of_three"
"best_of_five"

display_attributes.market_type.teams object for fixture-scoped team sports:

HomeAwayTeams#

Provider-designated home and away competitors for match-scoped market types.

For neutral-site, esports, and individual-sport matches, home and away provide canonical fixture ordering and do not imply that either competitor is playing at a home venue.

FieldTypeDescription
home_namestringFull human-readable home team name used in market labels and rules.
home_short_namestring(Optional) Concise home team name used in generated fixture and event display labels. When provided, away_short_name must also be provided.
home_subject_idstringStable subject slug for the home team.
away_namestringFull human-readable away team name used in market labels and rules.
away_short_namestring(Optional) Concise away team name used in generated fixture and event display labels. When provided, home_short_name must also be provided.
away_subject_idstringStable subject slug for the away team.

display_attributes.market_type.market enum for soccer markets:

SoccerMarket#

Soccer market types.

Value
"moneyline"
"spread"
"total_goals"
"both_teams_to_score"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeThreeWayOutcomeOutcome.

"spread" fields

FieldTypeDescription
typestring literal "spread"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_goals" fields

FieldTypeDescription
typestring literal "total_goals"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for baseball markets:

BaseballMarket#

Market types for a baseball game.

Value
"moneyline"
"spread"
"total"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"spread" fields

FieldTypeDescription
typestring literal "spread"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total" fields

FieldTypeDescription
typestring literal "total"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for football markets:

FootballMarket#

Football market types.

Value
"moneyline"
"spread"
"total_points"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"spread" fields

FieldTypeDescription
typestring literal "spread"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_points" fields

FieldTypeDescription
typestring literal "total_points"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for basketball markets:

BasketballMarket#

Basketball market types.

Value
"moneyline"
"spread"
"total_points"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"spread" fields

FieldTypeDescription
typestring literal "spread"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_points" fields

FieldTypeDescription
typestring literal "total_points"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for League of Legends markets:

LeagueOfLegendsMarket#

League of Legends market types.

Value
"moneyline"
"map_winner"
"map_handicap"
"total_maps"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"map_winner" fields

FieldTypeDescription
typestring literal "map_winner"Request discriminator.
map_numberintegerMap number.
outcomeTeamSideOutcome.

"map_handicap" fields

FieldTypeDescription
typestring literal "map_handicap"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_maps" fields

FieldTypeDescription
typestring literal "total_maps"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for Counter-Strike markets:

CounterStrikeMarket#

Counter-Strike market types.

Value
"moneyline"
"map_winner"
"map_handicap"
"total_maps"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"map_winner" fields

FieldTypeDescription
typestring literal "map_winner"Request discriminator.
map_numberintegerMap number.
outcomeTeamSideOutcome.

"map_handicap" fields

FieldTypeDescription
typestring literal "map_handicap"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_maps" fields

FieldTypeDescription
typestring literal "total_maps"Request discriminator.
linestringLine.

display_attributes.market_type.market enum for tennis markets:

TennisMarket#

Tennis market types.

Value
"moneyline"
"set_handicap"
"total_sets"
"total_games"

"moneyline" fields

FieldTypeDescription
typestring literal "moneyline"Request discriminator.
outcomeTeamSideOutcome.

"set_handicap" fields

FieldTypeDescription
typestring literal "set_handicap"Request discriminator.
teamTeamSideTeam.
linestringLine.

"total_sets" fields

FieldTypeDescription
typestring literal "total_sets"Request discriminator.
linestringLine.

"total_games" fields

FieldTypeDescription
typestring literal "total_games"Request discriminator.
linestringLine.

display_attributes.market_type.market.outcome enum for moneyline market types that can draw:

ThreeWayOutcome#

Home, away, or draw outcome for three-way markets.

Value
"home"
"away"
"draw"

display_attributes.market_type.market.team enum for team-scoped market types:

TeamSide#

Value
"home"
"away"

display_attributes.sort_order enum:

MarketSort#

An enum encoding the default sort order for markets on the UI

Value
"PRICE"
"SYMBOL_ASC"
"SYMBOL_DESC"
"MARKET_DESCRIPTION_ASC"
"MARKET_DESCRIPTION_DESC"
"VOLUME"
"EXPECTED_RESOLUTION_TIME"

display_attributes.reference enum:

ReferenceAttributes#

Value
"polymarket"

"polymarket" fields

FieldTypeDescription
kindstring literal "polymarket"Discriminator.
market_slugstringPolymarket market slug mirrored by this Pascal market.
condition_idstringPolymarket condition id for the mirrored CLOB market.
market_outcome_token_idstring(Optional) Polymarket CLOB token id for the Pascal market side. If this token wins, the Pascal market resolves to 1.000000.
reverse_outcome_token_idstring(Optional) Polymarket CLOB token id for the other binary outcome. If this token wins, the Pascal market resolves to 0.000000.

resolution object:

MarketResolutionMsg#

Final resolution metadata for a market.

FieldTypeDescription
pricedecimal string, 6 d.p.Final settlement price in dollars.
sequ64 stringMatching engine sequence number when the market resolved.
market_resolution_ts_msu64 stringTimestamp when the market resolved, in milliseconds since the Unix epoch.

stats object:

MarketStatsMsg#

FieldTypeDescription
last_24hMarketStatsSnapshotMsgRolling 24-hour market statistics snapshot.
all_timeMarketStatsSnapshotMsgAll-time market statistics snapshot.

last_24h and all_time use the following shape:

MarketStatsSnapshotMsg#

FieldTypeDescription
mark_price_startdecimal string, 6 d.p.(Optional) Mark price at the start of the rolling window.
taker_buy_volumeu64 stringTotal base-asset volume bought by takers in the window.
taker_sell_volumeu64 stringTotal base-asset volume sold by takers in the window.
taker_buy_volume_notionaldecimal string, 6 d.p.Total quote-notional bought by takers in the window.
taker_sell_volume_notionaldecimal string, 6 d.p.Total quote-notional sold by takers in the window.
taker_buy_trade_countu64 stringTotal number of buy-side taker trades included in the snapshot window.
taker_sell_trade_countu64 stringTotal number of sell-side taker trades included in the snapshot window.
window_end_ts_ms_exclusiveu64 stringExclusive upper-bound timestamp for the aggregation window.

For last_24h, window_end_ts_ms_exclusive is the latest closed minute. Markets with no history receive a zero snapshot at that boundary.

Resolution does not freeze the 24-hour window. A resolved market keeps its final price while volume, trades, and earlier prices age out normally. Once the window begins after resolution, mark_price_start is the final price.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/markets'
Response
json
{
  "status": "success",
  "data": [
    {
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_fee_rate": "0.001000",
      "maker_rebate_share": "0.500000",
      "tick_size_min": "0.010000",
      "tick_sig_figs": 2,
      "display_attributes": {
        "event_description": "API documentation sample market",
        "expected_resolution_time_ms": "1735000000000",
        "market_description": "Yes",
        "reverse_description": "No",
        "sort_order": "SYMBOL_ASC",
        "tags": [
          "Docs"
        ],
        "topic": {
          "id": "docs",
          "description": "Documentation examples"
        }
      },
      "listing_ts_ms": "1731536000000",
      "rules_url": "https://sample-rules-url.com/events/SIM_EVENT_1/SIM_EVENT_1.MARKET_1.md",
      "stats": {
        "last_24h": {
          "mark_price_start": "0.520000",
          "taker_buy_volume": "120",
          "taker_sell_volume": "90",
          "taker_buy_volume_notional": "64.800000",
          "taker_sell_volume_notional": "48.600000",
          "taker_buy_trade_count": "18",
          "taker_sell_trade_count": "14",
          "window_end_ts_ms_exclusive": "1731535980000"
        },
        "all_time": {
          "taker_buy_volume": "1200",
          "taker_sell_volume": "900",
          "taker_buy_volume_notional": "648.000000",
          "taker_sell_volume_notional": "486.000000",
          "taker_buy_trade_count": "180",
          "taker_sell_trade_count": "140",
          "window_end_ts_ms_exclusive": "1731535980000"
        }
      },
      "mark_price": "0.550000",
      "open_interest": "250"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Resolved Markets#

GET <read_base_url>/api/v1/markets/resolved

Returns markets resolved within the requested lookback.

Query Parameters

FieldTypeDescription
lookbackstringOptional. Accepted values: 4h, 24h, 3d. Defaults to 24h.
order_bystringOptional. Accepted values: resolution_time, volume_24h, volume_all_time. Defaults to resolution_time.
limitintegerOptional. Maximum number of markets to return. Must be one of 50, 100, or 200. Defaults to 50.

order_by enum:

ResolvedMarketOrderBy#

Resolved market ordering field.

ValueDescription
"resolution_time"Sort by least distance between market resolution time and current exchange time.
"volume_24h"Sort by rolling 24-hour contract volume.
"volume_all_time"Sort by all-time contract volume.

order_by behavior:

ValueDescription
resolution_timeSort by least distance between resolution time and the current exchange time. For this endpoint, that returns the latest resolutions first.
volume_24hSort by rolling 24-hour contract volume. Ties use the resolution_time ordering.
volume_all_timeSort by all-time contract volume.

Resolved markets use the rolling-statistics semantics described under List Markets.

JSON Response Payload

Returns a JSON array of market objects. Same object shape as GET /api/v1/markets.

Market Symbols#

Symbols are uppercase ASCII identifiers up to 32 bytes long, of the form EVENT.MARKET. The event prefix groups markets that share a resolution event; the market suffix identifies the specific outcome.

For example, NYCMAYOR_25NOV04.MAMDANI denotes the "Mamdani" outcome of the 2025-11-04 New York City mayoral election event. A second outcome on the same event might be NYCMAYOR_25NOV04.ADAMS.

Symbols are immutable and globally unique.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/markets/resolved?lookback=24h&order_by=resolution_time&limit=50'
Response
json
{
  "status": "success",
  "data": [
    {
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_fee_rate": "0.001000",
      "maker_rebate_share": "0.500000",
      "tick_size_min": "0.010000",
      "tick_sig_figs": 2,
      "display_attributes": {
        "event_description": "API documentation sample market",
        "expected_resolution_time_ms": "1731536000050",
        "market_description": "Yes",
        "reverse_description": "No",
        "sort_order": "SYMBOL_ASC",
        "tags": [
          "Docs"
        ],
        "topic": {
          "id": "docs",
          "description": "Documentation examples"
        }
      },
      "listing_ts_ms": "1731536000000",
      "rules_url": "https://sample-rules-url.com/events/SIM_EVENT_1/SIM_EVENT_1.MARKET_1.md",
      "stats": {
        "last_24h": {
          "mark_price_start": "0.520000",
          "taker_buy_volume": "120",
          "taker_sell_volume": "90",
          "taker_buy_volume_notional": "64.800000",
          "taker_sell_volume_notional": "48.600000",
          "taker_buy_trade_count": "18",
          "taker_sell_trade_count": "14",
          "window_end_ts_ms_exclusive": "1731535980000"
        },
        "all_time": {
          "taker_buy_volume": "1200",
          "taker_sell_volume": "900",
          "taker_buy_volume_notional": "648.000000",
          "taker_sell_volume_notional": "486.000000",
          "taker_buy_trade_count": "180",
          "taker_sell_trade_count": "140",
          "window_end_ts_ms_exclusive": "1731535980000"
        }
      },
      "mark_price": "1.000000",
      "open_interest": "0",
      "resolution": {
        "price": "1.000000",
        "seq": "67891",
        "market_resolution_ts_ms": "1731536000050"
      }
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Order Books#

GET <read_base_url>/api/v1/books

Returns order books for up to 50 comma-delimited symbols.

Single-symbol responses may be up to one second old. For live order books, use the book WebSocket channel, which starts with a full snapshot and then streams updates.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)Comma-delimited list of market symbols.

JSON Response Payload

FieldTypeDescription
booksobject mapping string (Symbol) to OrderBookMsgOrder books keyed by market symbol.

Each value in books is an order book object:

OrderBookMsg#

Order book message for API responses.

Levels are (price, size) tuples serialized as string arrays: [["0.50", "100"], ...]

FieldTypeDescription
asksarray of tuple (decimal string, 6 d.p., u64 string)Ask levels as (price, size) tuples, sorted lowest price first.
bidsarray of tuple (decimal string, 6 d.p., u64 string)Bid levels as (price, size) tuples, sorted highest price first.
specMarketSpec(Optional) Market specification for this book, included on first fetch or when the market specification changes.

spec object:

MarketSpec#

Specification for a tradeable market.

FieldTypeDescription
symbolstring (Symbol)Human-readable identifier for a market. Globally Unique. Eg "NYCMAYOR_25NOV04.MAMDANI".
taker_fee_ratedecimal string, 6 d.p.Base fee rate paid by takers. Decimal string, e.g. "0.0010" for 10 bps. For a trade of sz at px, the taker fee is sz * px * (1 - px) * taker_fee_rate.
maker_rebate_sharedecimal string, 6 d.p.Share of taker fee rebated to makers. Decimal string, e.g. "0.50" for 50%. For a trade of sz at px, the maker earns sz * px * (1 - px) * taker_fee_rate * maker_rebate_share.
tick_size_mindecimal string, 6 d.p.Minimum tick size. The tick size for a given price px is max(tick_size_min, 10^(floor(log10(px')) - tick_sig_figs + 1)), where px' = min(px, 1 - px). The intuition is that we want some amount of decimal precision available, but we don't want to count 0 prefixes. For example, 0.0025, 0.025 and 0.25 all have 2 digits of precision.
tick_sig_figsintegerNumber of significant figures in the tick size formula. Must be in [1, 6].
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/books?symbols=SIM_EVENT_1.MARKET_1,SIM_EVENT_1.MARKET_2'
Response
json
{
  "status": "success",
  "data": {
    "books": {
      "SIM_EVENT_1.MARKET_1": {
        "asks": [
          [
            "0.560000",
            "12"
          ],
          [
            "0.570000",
            "20"
          ]
        ],
        "bids": [
          [
            "0.540000",
            "15"
          ],
          [
            "0.530000",
            "7"
          ]
        ],
        "spec": {
          "symbol": "SIM_EVENT_1.MARKET_1",
          "taker_fee_rate": "0.001000",
          "maker_rebate_share": "0.500000",
          "tick_size_min": "0.010000",
          "tick_sig_figs": 2
        }
      },
      "SIM_EVENT_1.MARKET_2": {
        "asks": [
          [
            "0.560000",
            "12"
          ],
          [
            "0.570000",
            "20"
          ]
        ],
        "bids": [
          [
            "0.540000",
            "15"
          ],
          [
            "0.530000",
            "7"
          ]
        ]
      }
    }
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Trade Candles#

GET <read_base_url>/api/v1/trade-candles

Legacy compatibility path: GET <read_base_url>/api/v1/candles.

Returns OHLCV trade candles for one market and interval. Ranges are end-exclusive. If start_time_ms is omitted, count_back controls how many candles are returned before end_time_ms. interval must be one of 1m, 5m, 15m, 1h, 4h, 12h, and 1d. count_back defaults to 100 and must be between 1 and 500.

Query Parameters

FieldTypeDescription
symbolstring (Symbol)Symbol of the market to fetch candles for.
intervalstring enum: "1m", "5m", "15m", "1h", "4h", "12h", "1d"Candle aggregation interval.
end_time_msintegerExclusive end timestamp for the query range, in milliseconds since the Unix epoch.
start_time_msinteger(Optional) Inclusive start timestamp for the query range. If omitted, use count_back.
count_backinteger(Optional) Number of candles to return ending at end_time_ms when start_time_ms is omitted.

JSON Response Payload

Returns a JSON array of candle objects. Every element in the array is a JSON object:

CandleMsg#

OHLCV candle in TradingView-compatible format.

FieldTypeDescription
timeu64 stringCandle open time in milliseconds since the Unix epoch.
opendecimal string, 6 d.p.Opening price in dollars.
highdecimal string, 6 d.p.Highest price in the interval, in dollars.
lowdecimal string, 6 d.p.Lowest price in the interval, in dollars.
closedecimal string, 6 d.p.Closing price in dollars.
volumeu64 stringTotal contracts traded in the interval.
taker_buy_volumeu64 stringContracts bought by takers in the interval.
taker_sell_volumeu64 stringContracts sold by takers in the interval.
volume_notionaldecimal string, 6 d.p.Total price * size traded in the interval.
taker_buy_volume_notionaldecimal string, 6 d.p.Price * size bought by takers in the interval.
taker_sell_volume_notionaldecimal string, 6 d.p.Price * size sold by takers in the interval.
trade_countu64 stringTotal number of trades in the interval.
taker_buy_countu64 stringNumber of taker buy trades in the interval.
taker_sell_countu64 stringNumber of taker sell trades in the interval.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/trade-candles?symbol=SIM_EVENT_1.MARKET_1&interval=1m&end_time_ms=1731536000000&count_back=2'
Response
json
{
  "status": "success",
  "data": [
    {
      "time": "1731535960000",
      "open": "0.520000",
      "high": "0.570000",
      "low": "0.510000",
      "close": "0.550000",
      "volume": "120",
      "taker_buy_volume": "70",
      "taker_sell_volume": "50",
      "volume_notional": "66.000000",
      "taker_buy_volume_notional": "38.500000",
      "taker_sell_volume_notional": "27.500000",
      "trade_count": "8",
      "taker_buy_count": "5",
      "taker_sell_count": "3"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Mark Price Candles#

GET <read_base_url>/api/v1/mark-price-candles

Returns sparse mark price OHLC candles for one market and interval. Ranges are end-exclusive. If start_time_ms is omitted, count_back controls how many candles are returned before end_time_ms. interval must be one of 1m, 5m, 15m, 1h, 4h, 12h, and 1d. count_back defaults to 100 and must be between 1 and 500.

Query Parameters

FieldTypeDescription
symbolstring (Symbol)Symbol of the market to fetch mark price candles for.
intervalstring enum: "1m", "5m", "15m", "1h", "4h", "12h", "1d"Candle aggregation interval.
end_time_msintegerExclusive end timestamp for the query range, in milliseconds since the Unix epoch.
start_time_msinteger(Optional) Inclusive start timestamp for the query range. If omitted, use count_back.
count_backinteger(Optional) Number of candles to return ending at end_time_ms when start_time_ms is omitted.

JSON Response Payload

Returns a JSON array of mark price candle objects. Every element in the array is a JSON object:

MarkPriceCandleMsg#

Mark price OHLC candle.

FieldTypeDescription
timeu64 stringCandle open time in milliseconds since the Unix epoch.
opendecimal string, 6 d.p.Opening mark price in dollars.
highdecimal string, 6 d.p.Highest mark price in the interval, in dollars.
lowdecimal string, 6 d.p.Lowest mark price in the interval, in dollars.
closedecimal string, 6 d.p.Closing mark price in dollars.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/mark-price-candles?symbol=SIM_EVENT_1.MARKET_1&interval=1m&end_time_ms=1731536000000&count_back=2'
Response
json
{
  "status": "success",
  "data": [
    {
      "time": "1731535960000",
      "open": "0.540000",
      "high": "0.580000",
      "low": "0.530000",
      "close": "0.560000"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Batch Mark Price Candles#

GET <read_base_url>/api/v1/mark-price-candles/batch

Returns sparse mark price OHLC candles for up to 20 symbols sharing one interval and time range.

Uses the same range semantics as GET /api/v1/mark-price-candles, except count_back and explicit range queries are capped at 200 candles per symbol.

Unknown symbols are omitted from items; returned items preserve request order.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)Comma-delimited list of market symbols to fetch mark price candles for.
intervalstring enum: "1m", "5m", "15m", "1h", "4h", "12h", "1d"Candle aggregation interval.
end_time_msintegerExclusive end timestamp for the query range, in milliseconds since the Unix epoch.
start_time_msinteger(Optional) Inclusive start timestamp for the query range. If omitted, use count_back.
count_backinteger(Optional) Number of candles to return ending at end_time_ms when start_time_ms is omitted.

JSON Response Payload

FieldTypeDescription
itemsarray of MarkPriceCandlesBatchItemPer-symbol candle results. Unknown requested symbols are omitted.

Each value in items is a JSON object:

MarkPriceCandlesBatchItem#

Mark price candle data for one requested symbol.

FieldTypeDescription
symbolstring (Symbol)Market symbol for this result.
candlesarray of MarkPriceCandleMsgMark price candles for this symbol, sorted newest-first.

Each value in candles is a mark price candle object using MarkPriceCandleMsg.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/mark-price-candles/batch?symbols=SIM_EVENT_1.MARKET_1,SIM_EVENT_1.MARKET_2&interval=1m&end_time_ms=1731536000000&count_back=2'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "candles": [
          {
            "time": "1731535960000",
            "open": "0.540000",
            "high": "0.580000",
            "low": "0.530000",
            "close": "0.560000"
          }
        ]
      },
      {
        "symbol": "SIM_EVENT_1.MARKET_2",
        "candles": []
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Trades#

GET <read_base_url>/api/v1/trades

Returns trade history for up to 30 markets.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)Comma-delimited list of market symbols.
before_cursorstring(Optional) Opaque cursor returned as next_cursor from a prior page. Results are strictly older than this cursor.
at_or_before_sequ64 string(Optional) Inclusive sequence number to start pagination from. Use this when you have an event (eg a fill or order) and want to start pagination from that event. After you get the first page, use the next_cursor to continue.
limitinteger(Optional) Maximum number of items to return.

JSON Response Payload

Returns a paginated JSON object:

FieldTypeDescription
itemsarray of PublicTradeMsgResult page items.
next_cursorstring(Optional) Cursor for the next page of results. Omitted on the last page.

Every element in items is a JSON object:

PublicTradeMsg#

Public trade message.

This is not the same as the account-specific FillMsg, which is a view of one side of a trade, from one account's perspective.

FieldTypeDescription
trade_idu64 stringUnique ID for this trade.
symbolstring (Symbol)Market symbol this trade belongs to.
taker_sidestring enum: "BID", "ASK"Side of the aggressive order that took liquidity.
pricedecimal string, 6 d.p.Trade price in dollars.
sizeu64 stringTrade size in contracts.
makerbase58 stringAccount owner public key of the resting (maker) order.
takerbase58 stringAccount owner public key of the aggressive (taker) order.
sequ64 stringMatching engine sequence number when this trade occurred.
trade_ts_msu64 stringTimestamp when this trade occurred, in milliseconds since the Unix epoch.

Next-page request using the data.next_cursor value above as before_cursor:

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/trades?symbols=SIM_EVENT_1.MARKET_1&limit=50'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "trade_id": "555001",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "taker_side": "BID",
        "price": "0.550000",
        "size": "3",
        "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
        "seq": "67891",
        "trade_ts_ms": "1731536000050"
      }
    ],
    "next_cursor": "00000000000000067890:0000000000"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}
Next Page Request
bash
curl 'https://data.pascal.trade/api/v1/trades?symbols=SIM_EVENT_1.MARKET_1&before_cursor=00000000000000067890%3A0000000000&limit=50'

Batch Trades#

GET <read_base_url>/api/v1/trades/batch

Returns the most recent trades for up to 20 markets, newest-first per market. This is the batch equivalent of the first page of GET /api/v1/trades.

Unknown symbols are omitted from items; returned items preserve request order. limit defaults to 10 and must be between 1 and 50.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)Comma-delimited list of market symbols to fetch recent trades for.
limitinteger(Optional) Maximum number of recent trades to return per symbol. Defaults to 10. Maximum 50.

JSON Response Payload

FieldTypeDescription
itemsarray of TradesBatchItemPer-symbol recent trade results. Unknown requested symbols are omitted.

Each value in items is a JSON object:

TradesBatchItem#

Recent trades for one requested symbol.

FieldTypeDescription
symbolstring (Symbol)Market symbol for this result.
tradesarray of PublicTradeMsgRecent trades for this symbol, sorted newest-first.

Each value in trades is a public trade object using PublicTradeMsg.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/trades/batch?symbols=SIM_EVENT_1.MARKET_1,SIM_EVENT_1.MARKET_2&limit=10'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "trades": [
          {
            "trade_id": "555001",
            "symbol": "SIM_EVENT_1.MARKET_1",
            "taker_side": "BID",
            "price": "0.550000",
            "size": "3",
            "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
            "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
            "seq": "67891",
            "trade_ts_ms": "1731536000050"
          }
        ]
      },
      {
        "symbol": "SIM_EVENT_1.MARKET_2",
        "trades": [
          {
            "trade_id": "555001",
            "symbol": "SIM_EVENT_1.MARKET_2",
            "taker_side": "BID",
            "price": "0.550000",
            "size": "3",
            "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
            "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
            "seq": "67891",
            "trade_ts_ms": "1731536000050"
          }
        ]
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Top Holders#

GET <read_base_url>/api/v1/top-holders

Returns the largest active position holders across up to 50 comma-delimited symbols, split into longs (positive position size) and shorts (negative position size) per market. limit applies to each side independently. Each side is ordered by absolute contract size descending, then owner public-key byte order ascending. Requested symbols for resolved markets are returned with empty longs and shorts arrays because they no longer have active positions.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)Comma-delimited list of market symbols.
limitinteger(Optional) Maximum number of holders to return per side per market. Defaults to 10. Maximum 50.

JSON Response Payload

FieldTypeDescription
holdersobject mapping string (Symbol) to MarketTopHoldersMsgLargest active holders per side, keyed by market symbol.

Each value in holders is a JSON object:

MarketTopHoldersMsg#

Largest active holders for one market, split by position side.

FieldTypeDescription
longsarray of TopHolderMsgLargest long positions, ordered by contracts descending.
shortsarray of TopHolderMsgLargest short positions, ordered by absolute contracts descending.

Every holder row in longs and shorts is a JSON object. See Realized and Unrealized PnL for the position lifecycle, average entry price, and PnL semantics:

TopHolderMsg#

Holder row for a market.

FieldTypeDescription
ownerbase58 stringAccount owner public key.
positionPositionMsgThe holder's current position in this market.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/top-holders?symbols=SIM_EVENT_1.MARKET_1,SIM_EVENT_1.MARKET_2&limit=20'
Response
json
{
  "status": "success",
  "data": {
    "holders": {
      "SIM_EVENT_1.MARKET_1": {
        "longs": [
          {
            "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
            "position": {
              "symbol": "SIM_EVENT_1.MARKET_1",
              "size": "3",
              "cumulative_fees_usd": "0.000742",
              "realized_pnl_usd": "0.000000",
              "unrealized_pnl_usd": "0.000000",
              "mark_price": "0.550000",
              "average_entry_price": "0.550000",
              "seq": "67880",
              "update_seq": "67891",
              "open_ts_ms": "1731535900000",
              "update_ts_ms": "1731536000050"
            }
          }
        ],
        "shorts": []
      },
      "SIM_EVENT_1.MARKET_2": {
        "longs": [],
        "shorts": [
          {
            "owner": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
            "position": {
              "symbol": "SIM_EVENT_1.MARKET_2",
              "size": "-80",
              "cumulative_fees_usd": "0.019800",
              "realized_pnl_usd": "0.000000",
              "unrealized_pnl_usd": "1.600000",
              "mark_price": "0.530000",
              "average_entry_price": "0.550000",
              "seq": "67880",
              "update_seq": "67891",
              "open_ts_ms": "1731535900000",
              "update_ts_ms": "1731536000050"
            }
          }
        ]
      }
    }
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Account Data Endpoints#

All account data endpoints use {owner} as a path parameter. owner is the account owner's Solana address, encoded as the base58 wallet public key that identifies the Pascal account.

Account State Snapshot#

GET <read_base_url>/api/v1/accounts/{owner}

Returns open orders, positions, pending deposits/withdrawals, and collateral for one account, with orders and positions optionally filtered to a single market. The same shape is sent over the account WebSocket channel.

Successful responses may be served from cache for up to 1 second, so a snapshot fetched immediately after submitting an order may not reflect it yet. For real-time account state, subscribe to the account WebSocket channel.

Query Parameters

FieldTypeDescription
symbolstring (Symbol)(Optional) Market symbol to filter by. When set, orders and positions contain only that market's entries; account-wide fields such as collateral are unaffected. Omit to include all markets.
exclude_ordersboolean(Optional) Return account state with an empty orders array. Defaults to false.

JSON Response Payload

FieldTypeDescription
usernameUsername(Optional) Owner-chosen handle. Present in REST responses and WebSocket subscription snapshots when set; always omitted from incremental WebSocket updates.
collateral_usddecimal string, 6 d.p.(Optional) Current free collateral in USD. The account WebSocket channel omits this field when there has been no collateral change since the last snapshot.
rewardsAccountRewardsMsg(Optional) Complete cumulative account reward totals. Present in REST and WebSocket snapshots. The account WebSocket channel omits this field when no reward component changed since the last snapshot.
lifetime_statsAccountLifetimeStatsMsg(Optional) Complete account lifetime trade totals. Omitted if data is not available. The account WebSocket channel also omits this field when no trade has changed the totals since the last snapshot.
ordersarray of OrderMsgOpen orders for this account, or all changed orders when used on the account WebSocket channel.
positionsarray of PositionMsgCurrent open positions for this account, or all changed positions when used on the account WebSocket channel.
pending_depositsarray of DepositMsgDeposits being processed onchain but not yet credited.
pending_withdrawalsarray of PendingWithdrawalMsgActive pending withdrawals.

When present, rewards is an account rewards object:

AccountRewardsMsg#

Cumulative reward totals received by an account.

FieldTypeDescription
referral_rewards_received_usddecimal string, 6 d.p.Cumulative referral rewards received.
builder_fees_received_usddecimal string, 6 d.p.Cumulative builder fees received.

When present, lifetime_stats is an account lifetime stats object:

AccountLifetimeStatsMsg#

Complete maker and taker lifetime trade totals for an account.

FieldTypeDescription
makerLifetimeStatsMsgMaker.
takerLifetimeStatsMsgTaker.

Each maker and taker value is a liquidity-side lifetime stats object:

LifetimeStatsMsg#

Lifetime trade totals for one account liquidity side.

FieldTypeDescription
volume_contractsstringWhole contracts traded over the account's lifetime.
volume_notional_usdstringLifetime traded notional in decimal USD.
fees_paid_usdstringLifetime fees paid in decimal USD; maker rebates are negative.

Every element in positions is a JSON object. See Realized and Unrealized PnL for position lifecycle, average entry price, and PnL semantics:

PositionMsg#

An account's current position in a market.

A position's lifecycle begins when the position becomes non-zero and ends when it touches or crosses zero.

FieldTypeDescription
symbolstring (Symbol)Symbol of the market this position is in.
sizei64 stringSigned position size. Positive for long, negative for short.
cumulative_fees_usdsigned decimal string, 6 d.p.Signed cumulative fees paid for the current position. Positive values are fees paid; negative values are rebates.
realized_pnl_usdsigned decimal string, 6 d.p.Cumulative realized PnL for the current position, excluding fees. PnL only realizes on closing trades.
unrealized_pnl_usdsigned decimal string, 6 d.p.Unrealized PnL for the remaining open position, excluding fees. This is the amount of PnL that would be realized if the remaining position were closed at mark price. Equal to size * (mark - entry).
mark_pricedecimal string, 6 d.p.Most recent mark price for the market.
average_entry_pricedecimal string, 6 d.p.(Optional) Average entry price rounded to 6dp. Not included if position size is zero.
sequ64 stringThe sequence number when this position was opened. Resets on touching zero.
update_sequ64 stringThe sequence number when this position last changed. Advances on every fill and resolution.
open_ts_msu64 stringWhen this position was opened. Resets on touching zero.
update_ts_msu64 stringWhen this position last changed. Advances on every fill and resolution.

Every element in pending_deposits is a JSON object:

DepositMsg#

Deposit lifecycle entry included in account updates.

FieldTypeDescription
deposit_idu64 stringUnique identifier for this deposit lifecycle.
ownerbase58 stringAccount owner's wallet public key (Solana address).
amountdecimal string, 6 d.p.Deposited amount in USD.
statusstring enum: "PENDING", "FINALIZED"Deprecated legacy deposit lifecycle state. @deprecated Use transfer_status instead. This field remains PENDING/FINALIZED during the frontend rollout so old frontend bundles keep validating account WebSocket messages.
transfer_statusstring enum: "VERIFYING", "PROCESSING", "REJECTED", "COMPLETED"Generic lifecycle state of this deposit. Deposits use PROCESSING or COMPLETED.

Every element in pending_withdrawals is a JSON object:

PendingWithdrawalMsg#

Pending withdrawal lifecycle item exposed by read API snapshots and WebSocket updates.

Snapshots contain active VERIFYING and PROCESSING withdrawals. WebSocket updates may also include terminal REJECTED or COMPLETED lifecycle updates so clients can remove the withdrawal from pending UI.

FieldTypeDescription
ownerbase58 stringAccount owner's wallet public key (Solana address).
statusstring enum: "VERIFYING", "PROCESSING", "REJECTED", "COMPLETED"Current lifecycle state for this pending withdrawal.
amountdecimal string, 6 d.p.Amount of collateral being withdrawn in USD.
destination_authoritybase58 stringSolana wallet address that will receive the withdrawal. This is the owner/authority address, not the associated token account address.
chain_sequ64 string(Optional) Withdrawal transfer chain sequence. Present when status is PROCESSING or COMPLETED.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB'
Response
json
{
  "status": "success",
  "data": {
    "username": "Trader",
    "collateral_usd": "100.000000",
    "rewards": {
      "referral_rewards_received_usd": "12.500000",
      "builder_fees_received_usd": "34.375000"
    },
    "lifetime_stats": {
      "maker": {
        "volume_contracts": "1250",
        "volume_notional_usd": "625.000000",
        "fees_paid_usd": "-0.625000"
      },
      "taker": {
        "volume_contracts": "750",
        "volume_notional_usd": "375.000000",
        "fees_paid_usd": "1.125000"
      }
    },
    "orders": [
      {
        "id": "987654321",
        "client_order_id": "42",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "price": "0.550000",
        "size_original": "10",
        "size_remaining": "7",
        "size_filled": "3",
        "notional_filled": "1.650000",
        "tif": "GTC",
        "type": "LIMIT",
        "post_only": false,
        "reduce_only": false,
        "seq": "67880",
        "update_seq": "67891",
        "place_ts_ms": "1731536000050",
        "update_ts_ms": "1731536000050"
      }
    ],
    "positions": [
      {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "size": "3",
        "cumulative_fees_usd": "0.000742",
        "realized_pnl_usd": "0.000000",
        "unrealized_pnl_usd": "0.000000",
        "mark_price": "0.550000",
        "average_entry_price": "0.550000",
        "seq": "67880",
        "update_seq": "67891",
        "open_ts_ms": "1731535900000",
        "update_ts_ms": "1731536000050"
      }
    ],
    "pending_deposits": [
      {
        "deposit_id": "7",
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "amount": "100.000000",
        "status": "PENDING",
        "transfer_status": "PROCESSING"
      }
    ],
    "pending_withdrawals": [
      {
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "status": "VERIFYING",
        "amount": "25.000000",
        "destination_authority": "29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Trading Keys#

GET <read_base_url>/api/v1/accounts/{owner}/trading-keys

Returns active trading keys for one account.

JSON Response Payload

Response data is an array of TradingKeyMsg objects.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/trading-keys'
Response
json
{
  "status": "success",
  "data": [
    {
      "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "name": "api-doc-key",
      "expiration_ts_ms": "1732140800000",
      "open_order_count": "2"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Trading Key Accounts#

GET <read_base_url>/api/v1/trading-keys/{trading_key_pubkey}/accounts

Returns account owner addresses that have {trading_key_pubkey} permissioned as an active trading key. One trading key can be assigned to multiple accounts.

JSON Response Payload

Response data is an array of account owner addresses, encoded as base58 strings.

Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/trading-keys/2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1/accounts'
Response
json
{
  "status": "success",
  "data": [
    "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB"
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Builder Stats#

GET <read_base_url>/api/v1/accounts/{owner}/builder-stats

Returns lifetime statistics for fills from orders that named owner as builder. Each fill contributes separately, so a trade contributes twice when both orders name owner. Accounts never named as builder return zero totals.

JSON Response Payload

AccountBuilderStatsMsg#

Account builder stats response: lifetime totals across fills of orders where this account was the builder.

FieldTypeDescription
builder_volume_contractsu128 stringTotal contracts across attributed fills.
builder_volume_notional_usddecimal string, 6 d.p.Sum of size × price across attributed fills.
builder_fee_base_usddecimal string, 6 d.p.Sum of size × price × (1 − price), rounded per fill. At a constant rate r, fees equal r × this value, up to per-fill rounding.
builder_fees_received_usddecimal string, 6 d.p.Builder fees received, credited to free collateral at settlement.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/builder-stats'
Response
json
{
  "status": "success",
  "data": {
    "builder_volume_contracts": "12500",
    "builder_volume_notional_usd": "6875.000000",
    "builder_fee_base_usd": "3093.750000",
    "builder_fees_received_usd": "34.375000"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Taker Discount Terms#

GET <read_base_url>/api/v1/accounts/{owner}/taker-discount-terms

Returns the stored taker discount terms and fee-savings usage for owner.

taker_discount_terms is absent when the owner has no stored terms. Stored terms discount the owner's taker fees by taker_fee_discount_share until fee_savings_usd crosses fee_savings_limit_usd; later taker fills receive no discount. See the Referral Program for the terms applied when an invite code is redeemed.

JSON Response Payload

AccountTakerDiscountTermsMsg#

Account taker discount terms response. taker_discount_terms is absent when the owner has no stored terms.

FieldTypeDescription
taker_discount_termsTakerDiscountTermsMsg(Optional) The owner's stored terms; absent when the owner has none.

taker_discount_terms, when present, is a JSON object:

TakerDiscountTermsMsg#

Stored taker discount terms and fee-savings usage for one owner.

FieldTypeDescription
taker_fee_discount_sharedecimal string, 6 d.p.Fraction of the taker fee discounted while the fee-savings cap is not exhausted.
created_ts_msu64 stringTimestamp when these terms were created, in milliseconds since the Unix epoch.
fee_savings_limit_usddecimal string, 6 d.p.Maximum taker fee savings this owner can receive through these terms.
fee_savings_usddecimal string, 6 d.p.Cumulative taker fee savings. The discount stops after this crosses the limit.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/taker-discount-terms'
Response
json
{
  "status": "success",
  "data": {
    "taker_discount_terms": {
      "taker_fee_discount_share": "0.050000",
      "created_ts_ms": "1731535900000",
      "fee_savings_limit_usd": "250.000000",
      "fee_savings_usd": "12.500000"
    }
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Invite Codes#

POST <read_base_url>/api/v1/invite-codes

Returns Invite Codes for the owner in the signed request. The request does not accept an owner path or query parameter; the backend always uses the verified auth.owner.

The request may be signed by the owner wallet or by any active trading key for that owner.

JSON Request Payload

OwnerInviteCodesRequest#

Request body for the owner Invite Codes endpoint.

FieldTypeDescription
authRequestAuthSigned request authentication metadata. The lookup owner is always auth.owner.

JSON Response Payload

OwnerInviteCodesMsg#

Owner Invite Codes response.

FieldTypeDescription
owner_invite_codesarray of OwnerInviteCodeMsgOwner invite codes.

Every element in owner_invite_codes is a JSON object:

OwnerInviteCodeMsg#

One Invite Code available to the owner.

FieldTypeDescription
invite_codestringInvite code.
statusstring enum: "active", "redeemed"Status.
redeemed_bybase58 string(Optional) Redeemed by.
Examples
Request
bash
curl -X POST https://data.pascal.trade/api/v1/invite-codes \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "u9FGGLvU3mbj8n64XVg1MaR4bBecTDteBxEdpdSvxeuVzi5ViS1nVEJroHt5e8CdADvzngcJy1TtVto4ymVFsYH"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "owner_invite_codes": [
      {
        "invite_code": "4PHPW-S4BM2-U9MS6-KP2JF",
        "status": "active"
      },
      {
        "invite_code": "ABCDE-FGHJK-LMNPQ-RSTUV",
        "status": "redeemed",
        "redeemed_by": "J2xccRtuG43drESLYznHhLhQkLTdfepcKYbiQ9BsJVaf"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Referrals#

GET <read_base_url>/api/v1/accounts/{owner}/referrals

Returns up to the 500 most recently created Referrals for which {owner} is the Reward Owner: one entry per referred user, with the reward paid so far and the reward-paid cap. Referrals are included identically regardless of their original attribution mechanism.

The response includes the owner's Generated and Custom Referral Codes and whether the Custom Code has been revoked. can_generate_referral_code and can_claim_custom_referral_code report whether the corresponding signed write requests would currently succeed; a submitted Custom Code may still be unavailable.

referral_reward_terms is the terms applied to Referrals created for this owner in the future. Each existing Referral keeps the share and cap fixed when it was created. A Referral pays rewards until reward_paid_usd crosses reward_paid_limit_usd; later fills pay no reward. See the Referral Program for the current terms.

JSON Response Payload

OwnerReferralsMsg#

Owner referrals response.

FieldTypeDescription
referral_reward_termsReferralRewardTermsMsgTerms applied to Referrals created for this owner in the future. Existing Referrals keep the share and limit fixed when they were created.
reward_paid_total_usddecimal string, 6 d.p.Total reward paid to this owner across all their Referrals.
referral_count_totalintegerTotal number of this owner's Referrals, including any omitted from referrals by an endpoint response limit.
referralsarray of ReferralMsgThis owner's Referrals as Reward Owner, one per referred owner, newest first.
generated_referral_codestring(Optional) This owner's Generated Referral Code, if they have created one.
custom_referral_codestring(Optional) This owner's Custom Referral Code, including a revoked code retained for support context.
custom_referral_code_revokedbooleanWhether custom_referral_code has been revoked and no longer resolves for attribution.
can_generate_referral_codebooleanWhether a generate-referral-code request from this owner would currently succeed: Referral Codes are enabled, the owner has deposited, no Generated Code exists, and no active Custom Code exists.
can_claim_custom_referral_codebooleanWhether the owner may currently claim a Custom Referral Code. The submitted code may still be unavailable.

referral_reward_terms is a JSON object:

ReferralRewardTermsMsg#

Reward terms applied to a Reward Owner's future Referrals.

FieldTypeDescription
referral_reward_sharedecimal string, 6 d.p.Fraction of the venue fee from each referred taker fill paid to the Reward Owner.
per_referred_owner_reward_paid_limit_usddecimal string, 6 d.p.Maximum reward paid to the Reward Owner for a single Referred Owner.

Every element in referrals is a JSON object:

ReferralMsg#

One Referral from its Reward Owner's perspective.

FieldTypeDescription
seqReferralSeqMonotonic referral creation sequence for stable ordering and future pagination.
referred_ownerbase58 stringOwner whose registration created this Referral.
created_ts_msu64 stringTimestamp when the Referral was created, in milliseconds since the Unix epoch.
referral_reward_sharedecimal string, 6 d.p.Reward share fixed when the Referral was created.
reward_paid_usddecimal string, 6 d.p.Cumulative reward paid to the Reward Owner for this Referral. Rewards stop after this crosses the limit.
reward_paid_limit_usddecimal string, 6 d.p.Maximum reward paid to the Reward Owner for this Referral.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/referrals'
Response
json
{
  "status": "success",
  "data": {
    "referral_reward_terms": {
      "referral_reward_share": "0.100000",
      "per_referred_owner_reward_paid_limit_usd": "10000.000000"
    },
    "reward_paid_total_usd": "25.000000",
    "referral_count_total": 1,
    "referrals": [
      {
        "seq": "42",
        "referred_owner": "J2xccRtuG43drESLYznHhLhQkLTdfepcKYbiQ9BsJVaf",
        "created_ts_ms": "1731535900000",
        "referral_reward_share": "0.100000",
        "reward_paid_usd": "25.000000",
        "reward_paid_limit_usd": "10000.000000"
      }
    ],
    "generated_referral_code": "k7wq2m4x",
    "custom_referral_code": "trader1",
    "custom_referral_code_revoked": false,
    "can_generate_referral_code": false,
    "can_claim_custom_referral_code": false
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Legacy Signed Referrals#

POST <read_base_url>/api/v1/referrals

Deprecated: use GET /api/v1/accounts/{owner}/referrals. This signed endpoint is temporarily retained for frontend rollout compatibility.

Returns the Referrals for which the owner in the signed request is the Reward Owner: one entry per referred user, with the reward paid so far and the reward-paid cap. The request does not accept an owner path or query parameter; the backend always uses the verified auth.owner.

The request may be signed by the owner wallet or by any active trading key for that owner.

referral_reward_terms is the terms applied to Referrals created for this owner in the future. Each existing Referral keeps the share and cap fixed when it was created. A Referral pays rewards until reward_paid_usd crosses reward_paid_limit_usd; later fills pay no reward. See the Referral Program for the current terms.

JSON Request Payload

OwnerReferralsRequest#

Request body for the legacy signed owner referrals endpoint.

FieldTypeDescription
authRequestAuthSigned request authentication metadata. The lookup owner is always auth.owner.

The response payload is the same OwnerReferralsMsg documented in Referrals.

Examples
Request
bash
curl -X POST https://data.pascal.trade/api/v1/referrals \
  -H 'Content-Type: application/json' \
  --data @- <<'EOF'
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "2gypF8ds55gagM8c9koyp7g4eZr4vSaehkbVFP9ohSzd4EMZsE29xYyi8Jcfp7BLLK3hWK7SP1JVHAoZBoYre6xm"
  }
}
EOF
Response
json
{
  "status": "success",
  "data": {
    "referral_reward_terms": {
      "referral_reward_share": "0.100000",
      "per_referred_owner_reward_paid_limit_usd": "10000.000000"
    },
    "reward_paid_total_usd": "25.000000",
    "referral_count_total": 1,
    "referrals": [
      {
        "seq": "42",
        "referred_owner": "J2xccRtuG43drESLYznHhLhQkLTdfepcKYbiQ9BsJVaf",
        "created_ts_ms": "1731535900000",
        "referral_reward_share": "0.100000",
        "reward_paid_usd": "25.000000",
        "reward_paid_limit_usd": "10000.000000"
      }
    ],
    "generated_referral_code": "k7wq2m4x",
    "custom_referral_code": "trader1",
    "custom_referral_code_revoked": false,
    "can_generate_referral_code": false,
    "can_claim_custom_referral_code": false
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Deposit Address#

GET <read_base_url>/api/v1/accounts/{owner}/deposit-address

Returns the wallet-facing deposit address for one account.

JSON Response Payload

DepositAddressMsg#

Wallet-facing deposit address data for a user owner.

FieldTypeDescription
ownerbase58 stringThe account owner.
deposit_addressbase58 string(Optional) The deposit address PDA corresponding to the account owner. Present only after the account has registered a deposit address, and immutable once set.
registeredbooleanWhether the deposit address is registered.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/deposit-address'
Response
json
{
  "status": "success",
  "data": {
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "deposit_address": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "registered": true
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Fill History#

GET <read_base_url>/api/v1/accounts/{owner}/fills

Returns fill history for one account, optionally filtered by up to 50 symbols.

Query Parameters

FieldTypeDescription
symbolscomma-separated array of string (Symbol)(Optional) Comma-delimited list of market symbols to filter by. Empty returns fills for all markets.
before_cursorstring(Optional) Opaque cursor returned as next_cursor from a prior page. Results are strictly older than this cursor.
at_or_before_sequ64 string(Optional) Inclusive sequence number to start pagination from. Use this when you have an event (eg a fill or order) and want to start pagination from that event. After you get the first page, use the next_cursor to continue.
limitinteger(Optional) Maximum number of items to return.

JSON Response Payload

Returns a paginated JSON object:

FieldTypeDescription
itemsarray of FillMsgResult page items.
next_cursorstring(Optional) Cursor for the next page of results. Omitted on the last page.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/fills?symbols=SIM_EVENT_1.MARKET_1&limit=50'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "trade_id": "555001",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "fill_price": "0.550000",
        "fill_size": "3",
        "liquidity": "TAKER",
        "order_id": "987654321",
        "client_order_id": "42",
        "fee_usd": "0.000742",
        "collateral_change_usd": "-1.650742",
        "position_size_prev": "0",
        "realized_pnl_usd": "0.000000",
        "position_open_seq": "67891",
        "position_open_ts_ms": "1731536000050",
        "seq": "67891",
        "trade_ts_ms": "1731536000050"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Transfer History#

GET <read_base_url>/api/v1/accounts/{owner}/transfers

Returns transfer (deposit and withdrawal) history for one account.

Query Parameters

FieldTypeDescription
before_cursorstring(Optional) Opaque cursor returned as next_cursor from a prior page. Results are strictly older than this cursor.
at_or_before_sequ64 string(Optional) Inclusive sequence number to start pagination from. Use this when you have an event (eg a fill or order) and want to start pagination from that event. After you get the first page, use the next_cursor to continue.
limitinteger(Optional) Maximum number of items to return.

JSON Response Payload

Returns a paginated JSON object:

FieldTypeDescription
itemsarray of TransferMsgResult page items.
next_cursorstring(Optional) Cursor for the next page of results. Omitted on the last page.

Every element in items is a JSON object:

TransferMsg#

Transfer ledger entry included in account updates.

FieldTypeDescription
typestring enum: "DEPOSIT", "WITHDRAWAL"Whether this transfer was a deposit or withdrawal.
statusstring enum: "VERIFYING", "PROCESSING", "REJECTED", "COMPLETED"Current user-facing lifecycle state for this transfer.
collateral_change_usdsigned decimal string, 6 d.p.Net change to account collateral from this transfer.
fee_usdsigned decimal string, 6 d.p.Transfer fee paid by the user. Positive values are fees paid.
net_usdsigned decimal string, 6 d.p.Signed external transfer amount after fees.
sequ64 stringSequence number where account collateral was updated.
transfer_ts_msu64 stringTimestamp when account collateral was updated.
chain_sequ64 string(Optional) Chain sequence used to correlate withdrawal lifecycle updates. Present only for withdrawal transfers.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/transfers?limit=50'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "type": "DEPOSIT",
        "status": "COMPLETED",
        "collateral_change_usd": "100.000000",
        "fee_usd": "0.000000",
        "net_usd": "100.000000",
        "seq": "67880",
        "transfer_ts_ms": "1731535900000"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Position Resolution History#

GET <read_base_url>/api/v1/accounts/{owner}/position-resolutions

Returns historical position-resolution payouts for one account.

Query Parameters

FieldTypeDescription
before_cursorstring(Optional) Opaque cursor returned as next_cursor from a prior page. Results are strictly older than this cursor.
at_or_before_sequ64 string(Optional) Inclusive sequence number to start pagination from. Use this when you have an event (eg a fill or order) and want to start pagination from that event. After you get the first page, use the next_cursor to continue.
limitinteger(Optional) Maximum number of items to return.

JSON Response Payload

Returns a paginated JSON object:

FieldTypeDescription
itemsarray of PositionResolutionMsgResult page items.
next_cursorstring(Optional) Cursor for the next page of results. Omitted on the last page.

Every element in items is a JSON object:

PositionResolutionMsg#

Resolution message for API responses.

FieldTypeDescription
symbolstring (Symbol)Market symbol for the resolved position.
resolutiondecimal string, 6 d.p.Final settlement price in dollars.
sizei64 stringSigned position size settled by this resolution.
collateral_change_usddecimal string, 6 d.p.Net collateral change from settling this position.
realized_pnl_usdsigned decimal string, 6 d.p.Realized PnL from this resolution, excluding fees.
position_open_sequ64 string(Optional) Opening sequence of the position settled by this resolution.
position_open_ts_msu64 string(Optional) Opening timestamp, in milliseconds since the Unix epoch, of the position settled by this resolution.
sequ64 stringMatching engine sequence number when this resolution was applied.
position_resolution_ts_msu64 stringTimestamp when this position was resolved, in milliseconds since the Unix epoch.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/position-resolutions?limit=50'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "resolution": "1.000000",
        "size": "3",
        "collateral_change_usd": "3.000000",
        "realized_pnl_usd": "1.350000",
        "position_open_seq": "67891",
        "position_open_ts_ms": "1731536000050",
        "seq": "67891",
        "position_resolution_ts_ms": "1731536000050"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

PnL History#

GET <read_base_url>/api/v1/accounts/{owner}/history

Returns account value and PnL time series for one account.

Historical windows are day (30m), week (4h), month (12h), and all_time (24h).

JSON Response Payload

FieldTypeDescription
itemsarray of AccountHistoryTimeseriesAccount value and PnL series, one entry per standard timeframe.

Every element in items is a JSON object:

AccountHistoryTimeseries#

Account history series for one timeframe.

FieldTypeDescription
timeframestring enum: "day", "week", "month", "all_time"Chart lookback window for this series.
dataAccountHistoryTimeseriesDataSampled account value and PnL points for this timeframe.

data object:

AccountHistoryTimeseriesData#

Chart lines for account history.

FieldTypeDescription
account_value_historyarray of TimeseriesPointSampled total account value over time.
pnl_historyarray of TimeseriesPointSampled cumulative account PnL, including rewards received. @deprecated Use trading_pnl_history for trading performance. Add the reward series when total account PnL is needed.
trading_pnl_historyarray of TimeseriesPointSampled cumulative trading PnL, excluding rewards received.
referral_rewards_received_historyarray of TimeseriesPointSampled cumulative referral rewards received.
builder_fees_received_historyarray of TimeseriesPointSampled cumulative builder fees received.

Every value in an account-history series is a two-element array [timestamp_ms, decimal_string]:

TimeseriesPoint#

One chart point, serialized as [timestamp_ms, decimal_string].

FieldTypeDescription
0u64 stringTimestamp in milliseconds.
1stringDecimal string value.
Examples
Request
bash
curl 'https://data.pascal.trade/api/v1/accounts/GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB/history'
Response
json
{
  "status": "success",
  "data": {
    "items": [
      {
        "timeframe": "day",
        "data": {
          "account_value_history": [
            [
              "1731536000000",
              "1000.000000"
            ]
          ],
          "pnl_history": [
            [
              "1731536000000",
              "25.000000"
            ]
          ],
          "trading_pnl_history": [
            [
              "1731536000000",
              "20.000000"
            ]
          ],
          "referral_rewards_received_history": [
            [
              "1731536000000",
              "3.000000"
            ]
          ],
          "builder_fees_received_history": [
            [
              "1731536000000",
              "2.000000"
            ]
          ]
        }
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Market Data WebSocket#

GET <ws_base_url> upgrades to a WebSocket connection. Clients send JSON text frames.

The server allows up to 10,000 concurrent connections and 5,000 subscriptions per connection.

Client Messages#

Clients send JSON text frames. There are three message types: subscribe, unsubscribe, and ping.

Subscribe to one or more channels. The server responds with a snapshot on each channel, followed by update messages as state changes.

Unsubscribe uses the same shape with type: "unsubscribe".

Ping keeps the connection alive. The server responds with the literal frame {"type":"pong"}.

Channels#

Each entry in channels is one of:

{"channel": "book",    "symbol": "<market symbol>"}
{"channel": "trades",  "symbol": "<market symbol>"}
{"channel": "candles", "symbol": "<market symbol>", "interval": "1m"}
{"channel": "mark_price_candles", "symbol": "<market symbol>", "interval": "1m"}
{"channel": "account", "owner":  "<wallet base58>"}
{"channel": "market_update"}
  • book — order book snapshots and price-level updates for one market.
  • trades — trades for one market.
  • candles — current in-progress candle for one market and interval; interval must be one of 1m, 5m, 15m, 1h, 4h, 12h, and 1d.
  • mark_price_candles — current in-progress mark price candle for one market and interval; interval must be one of 1m, 5m, 15m, 1h, 4h, 12h, and 1d.
  • account — live account updates for one wallet.
  • market_update — market listings and updates.
Examples
Subscribe Request
json
{
  "type": "subscribe",
  "channels": [
    {
      "channel": "book",
      "symbol": "SIM_EVENT_1.MARKET_1"
    },
    {
      "channel": "trades",
      "symbol": "SIM_EVENT_1.MARKET_1"
    },
    {
      "channel": "all_trades"
    }
  ]
}
Unsubscribe Request
json
{
  "type": "unsubscribe",
  "channels": [
    {
      "channel": "book",
      "symbol": "SIM_EVENT_1.MARKET_1"
    }
  ]
}
Request
json
{
  "type": "ping"
}

Server Messages#

For each subscribed channel, the server emits a snapshot message followed by update messages whenever the channel state changes. Error messages flatten the channel descriptor with type: "error" and error fields.

Message type enum

WsMessageType#

Type of WebSocket message sent to clients.

ValueDescription
"snapshot"A snapshot message is sent when a client first subscribes.
"update"An update message is sent when channel state changes.
"error"An error message reports a client- or channel-level failure. It may be sent in response to a malformed or rejected request, or emitted later for a subscribed channel that can no longer be served.

Shared server context fields

FieldTypeDescription
sequ64 string(Optional) Matching engine sequence number. Not present if the request is rejected before reaching the matching engine, for example due to invalid request data.
state_time_msu64 string(Optional) Matching engine timestamp as of the last matching round. Not present if the request is rejected before reaching the matching engine, for example due to invalid request data.
server_time_msu64 stringAPI server timestamp when this result was produced. Requests in a batch request can resolve at different times, so items in one response may carry different timestamps.

Error data object

FieldTypeDescription
codeApiErrorCodeStable, machine-readable error code.
messagestringHuman-readable error message. May evolve over time. For debugging and display only.
Examples
Error
json
{
  "channel": "book",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "type": "error",
  "data": {
    "code": "SYMBOL_NOT_FOUND",
    "message": "symbol not found: symbol=SIM_EVENT_1.MARKET_1"
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Book Channel#

L2 book feed for one market.

The first message after subscription is always a snapshot message with the full order book state and market spec.

Subsequent messages are updates which include price level changes (level removals are represented with size = 0) and any changes to the market spec.

Channel Descriptor

FieldTypeDescription
channelstring literal "book"Channel discriminator.
symbolstring (Symbol)Market symbol to stream order book updates for.

JSON Message Payload

Payload uses OrderBookMsg.

Examples
Snapshot
json
{
  "channel": "book",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "type": "snapshot",
  "data": {
    "asks": [
      [
        "0.560000",
        "12"
      ],
      [
        "0.570000",
        "20"
      ]
    ],
    "bids": [
      [
        "0.540000",
        "15"
      ],
      [
        "0.530000",
        "7"
      ]
    ],
    "spec": {
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_fee_rate": "0.001000",
      "maker_rebate_share": "0.500000",
      "tick_size_min": "0.010000",
      "tick_sig_figs": 2
    }
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}
Update
json
{
  "channel": "book",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "type": "update",
  "data": {
    "asks": [
      [
        "0.570000",
        "0"
      ]
    ],
    "bids": [
      [
        "0.540000",
        "15"
      ]
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Trades Channel#

Trades for one market.

Channel Descriptor

FieldTypeDescription
channelstring literal "trades"Channel discriminator.
symbolstring (Symbol)Market symbol to stream public trades for.

JSON Message Payload

data is a JSON array of trade objects. Every element in the array is a JSON object using PublicTradeMsg.

Examples
Update
json
{
  "channel": "trades",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "type": "update",
  "data": [
    {
      "trade_id": "555001",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_side": "BID",
      "price": "0.550000",
      "size": "3",
      "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "seq": "67891",
      "trade_ts_ms": "1731536000050"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

All Trades Channel#

Recent public trades across all markets. The initial snapshot contains up to 100 recent trades. Subsequent update messages contain every new trade since the last update.

Snapshots and updates are ordered chronologically, oldest first.

Channel Descriptor

FieldTypeDescription
channelstring literal "all_trades"Channel discriminator.

JSON Message Payload

data is a JSON array of trade objects. Every element in the array is a JSON object using PublicTradeMsg.

Examples
Snapshot
json
{
  "channel": "all_trades",
  "type": "snapshot",
  "data": [
    {
      "trade_id": "555001",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_side": "BID",
      "price": "0.550000",
      "size": "3",
      "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "seq": "67891",
      "trade_ts_ms": "1731536000050"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}
Update
json
{
  "channel": "all_trades",
  "type": "update",
  "data": [
    {
      "trade_id": "555001",
      "symbol": "SIM_EVENT_1.MARKET_1",
      "taker_side": "BID",
      "price": "0.550000",
      "size": "3",
      "maker": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "taker": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "seq": "67891",
      "trade_ts_ms": "1731536000050"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Candles Channel#

Current in-progress candle for one market and interval.

Channel Descriptor

FieldTypeDescription
channelstring literal "candles"Channel discriminator.
symbolstring (Symbol)Market symbol to stream candles for.
intervalstring enum: "1m", "5m", "15m", "1h", "4h", "12h", "1d"Candle aggregation interval.

JSON Message Payload

data is a JSON array of candle objects. Every element in the array uses CandleMsg.

Examples
Update
json
{
  "channel": "candles",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "interval": "1m",
  "type": "update",
  "data": [
    {
      "time": "1731535960000",
      "open": "0.520000",
      "high": "0.570000",
      "low": "0.510000",
      "close": "0.550000",
      "volume": "120",
      "taker_buy_volume": "70",
      "taker_sell_volume": "50",
      "volume_notional": "66.000000",
      "taker_buy_volume_notional": "38.500000",
      "taker_sell_volume_notional": "27.500000",
      "trade_count": "8",
      "taker_buy_count": "5",
      "taker_sell_count": "3"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Mark Price Candles Channel#

Current in-progress mark price candle for one market and interval.

Channel Descriptor

FieldTypeDescription
channelstring literal "mark_price_candles"Channel discriminator.
symbolstring (Symbol)Market symbol to stream mark price candles for.
intervalstring enum: "1m", "5m", "15m", "1h", "4h", "12h", "1d"Candle aggregation interval.

JSON Message Payload

data is a JSON array of mark price candle objects. Every element in the array uses MarkPriceCandleMsg.

Examples
Update
json
{
  "channel": "mark_price_candles",
  "symbol": "SIM_EVENT_1.MARKET_1",
  "interval": "1m",
  "type": "update",
  "data": [
    {
      "time": "1731535960000",
      "open": "0.540000",
      "high": "0.580000",
      "low": "0.530000",
      "close": "0.560000"
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Account Channel#

Live account updates for one account.

The first message after subscription is always a snapshot message, containing the same fields as the Account State Snapshot.

Subsequent update messages only populate fields which have changed since the most recent message. For example, if you cancel an order, you will see one update inside the orders array, and no other fields. If an order fills, you will see an update with changes in orders, positions, and collateral_usd.

We chose this consolidated shape to make it easy for consumers to atomically register updates to the account state.

Channel Descriptor

FieldTypeDescription
channelstring literal "account"Channel discriminator.
ownerbase58 stringAccount owner's wallet public key (Solana address).
exclude_ordersboolean(Optional) When true, the initial snapshot contains an empty orders array and subsequent order updates are not sent. Defaults to false.

JSON Message Payload

FieldTypeDescription
usernameUsername(Optional) Owner-chosen handle. Present in REST responses and WebSocket subscription snapshots when set; always omitted from incremental WebSocket updates.
collateral_usddecimal string, 6 d.p.(Optional) Current free collateral in USD. The account WebSocket channel omits this field when there has been no collateral change since the last snapshot.
rewardsAccountRewardsMsg(Optional) Complete cumulative account reward totals. Present in REST and WebSocket snapshots. The account WebSocket channel omits this field when no reward component changed since the last snapshot.
lifetime_statsAccountLifetimeStatsMsg(Optional) Complete account lifetime trade totals. Omitted if data is not available. The account WebSocket channel also omits this field when no trade has changed the totals since the last snapshot.
ordersarray of OrderMsgOpen orders for this account, or all changed orders when used on the account WebSocket channel.
positionsarray of PositionMsgCurrent open positions for this account, or all changed positions when used on the account WebSocket channel.
pending_depositsarray of DepositMsgDeposits being processed onchain but not yet credited.
pending_withdrawalsarray of PendingWithdrawalMsgActive pending withdrawals.
fillsarray of FillMsg(Optional) Fills since the last snapshot or update. Omitted when no fills occurred.
resolutionsarray of PositionResolutionMsg(Optional) Position resolutions since the last snapshot or update. Omitted when no resolutions occurred.
transfersarray of TransferMsg(Optional) Balance transfers since the last snapshot or update. Omitted when no transfers occurred.
Examples
Update
json
{
  "channel": "account",
  "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "type": "update",
  "data": {
    "collateral_usd": "100.000000",
    "rewards": {
      "referral_rewards_received_usd": "12.500000",
      "builder_fees_received_usd": "34.375000"
    },
    "lifetime_stats": {
      "maker": {
        "volume_contracts": "1250",
        "volume_notional_usd": "625.000000",
        "fees_paid_usd": "-0.625000"
      },
      "taker": {
        "volume_contracts": "750",
        "volume_notional_usd": "375.000000",
        "fees_paid_usd": "1.125000"
      }
    },
    "orders": [
      {
        "id": "987654321",
        "client_order_id": "42",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "price": "0.550000",
        "size_original": "10",
        "size_remaining": "7",
        "size_filled": "3",
        "notional_filled": "1.650000",
        "tif": "GTC",
        "type": "LIMIT",
        "post_only": false,
        "reduce_only": false,
        "seq": "67880",
        "update_seq": "67891",
        "place_ts_ms": "1731536000050",
        "update_ts_ms": "1731536000050"
      }
    ],
    "positions": [
      {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "size": "3",
        "cumulative_fees_usd": "0.000742",
        "realized_pnl_usd": "0.000000",
        "unrealized_pnl_usd": "0.000000",
        "mark_price": "0.550000",
        "average_entry_price": "0.550000",
        "seq": "67880",
        "update_seq": "67891",
        "open_ts_ms": "1731535900000",
        "update_ts_ms": "1731536000050"
      }
    ],
    "pending_deposits": [
      {
        "deposit_id": "7",
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "amount": "100.000000",
        "status": "PENDING",
        "transfer_status": "PROCESSING"
      }
    ],
    "pending_withdrawals": [
      {
        "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
        "status": "VERIFYING",
        "amount": "25.000000",
        "destination_authority": "29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2"
      }
    ],
    "fills": [
      {
        "trade_id": "555001",
        "symbol": "SIM_EVENT_1.MARKET_1",
        "side": "BID",
        "fill_price": "0.550000",
        "fill_size": "3",
        "liquidity": "TAKER",
        "order_id": "987654321",
        "client_order_id": "42",
        "fee_usd": "0.000742",
        "collateral_change_usd": "-1.650742",
        "position_size_prev": "0",
        "realized_pnl_usd": "0.000000",
        "position_open_seq": "67891",
        "position_open_ts_ms": "1731536000050",
        "seq": "67891",
        "trade_ts_ms": "1731536000050"
      }
    ],
    "transfers": [
      {
        "type": "DEPOSIT",
        "status": "COMPLETED",
        "collateral_change_usd": "100.000000",
        "fee_usd": "0.000000",
        "net_usd": "100.000000",
        "seq": "67880",
        "transfer_ts_ms": "1731535900000"
      }
    ]
  },
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Market Update Channel#

Listings and updates for all markets.

The initial snapshot message contains an empty data payload; you may retrieve all markets via the GET /api/v1/markets endpoint. In order to guarantee your markets list is up to date, you may perform the following initialization process:

  1. Subscribe to the market_update websocket channel.
  2. Upon receiving the first message in the channel, request from the GET /api/v1/markets endpoint.
  3. Cache all updates from the websocket locally.
  4. When you finish receiving all markets from the GET /api/v1/markets endpoint, compare the sequence id of the markets snapshot to the sequence ids of each cached websocket message.
  5. If update.seq > snapshot.seq, then apply the update to your local store of markets.

Subsequent update messages pertain to changes that occur to markets, such as a new listing or resolution.

Note: the stats field present in the GET /api/v1/markets endpoint does not exist in the websocket update message. If needed, you can get stats from the GET /api/v1/markets endpoint.

Channel Descriptor

FieldTypeDescription
channelstring literal "market_update"Channel discriminator.

Update Type

MarketUpdateType#

Reason a market appeared on the market_update channel.

Value
"new"
"display_attributes_change"
"resolved"

JSON Message Payload

FieldTypeDescription
update_typeMarketUpdateTypeUpdate type.
marketMarketStateMsgMarket.
Examples
Snapshot
json
{
  "channel": "market_update",
  "type": "snapshot",
  "data": [],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}
Update
json
{
  "channel": "market_update",
  "type": "update",
  "data": [
    {
      "update_type": "new",
      "market": {
        "symbol": "SIM_EVENT_1.MARKET_1",
        "taker_fee_rate": "0.001000",
        "maker_rebate_share": "0.500000",
        "tick_size_min": "0.010000",
        "tick_sig_figs": 2,
        "display_attributes": {
          "event_description": "API documentation sample market",
          "expected_resolution_time_ms": "1735000000000",
          "market_description": "Yes",
          "reverse_description": "No",
          "sort_order": "SYMBOL_ASC",
          "tags": [
            "Docs"
          ],
          "topic": {
            "id": "docs",
            "description": "Documentation examples"
          }
        },
        "listing_ts_ms": "1731536000000",
        "rules_url": "https://sample-rules-url.com/events/SIM_EVENT_1/SIM_EVENT_1.MARKET_1.md",
        "mark_price": "0.550000",
        "open_interest": "250"
      }
    }
  ],
  "seq": "67891",
  "state_time_ms": "1731536000050",
  "server_time_ms": "1731536000123"
}

Request Signing#

This section describes the payloads to sign over for each request type.

Info

In order to settle transactions directly on Solana, requests to Pascal's API use binary permits that are propagated onchain, which must be compact for performance, especially for order requests.

This section is for advanced users who want to roll their own signing code.

The easiest way to start signing requests is with github.com/pascal-research-inc/pascal-python-quickstart

All test vectors in this section use the following keys:

RolePrivate key bytesPublic key
Owner wallet[7; 32] (0707070707070707070707070707070707070707070707070707070707070707)GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB
Trading key[8; 32] (0808080808080808080808080808080808080808080808080808080808080808)2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1

And constants:

ConstantValue
deployment_id3

Deployment Id#

All signed requests encode a deployment_id field to make sure requests are not replayable across environments:

Environmentdeployment_id
local0
staging1
canary2
prod3

Use prod for real trading.

Permit Header#

Binary permits use little-endian encoding for all multi-byte integer fields, both in the header and in each permit body.

All binary permits begin with the same 80-byte header:

FieldByte offsetSizeNotes
version01 bytePermit envelope version; always the first byte.
cmd_type11 byteCommand type discriminator (1 = place order, 2 = cancel, ...), used to decode the body.
deployment_id21 byteProtection against replay across deployments on same or different networks.
_padding031 byteReserved padding. Must be zero.
recv_window_ms44 bytesFreshness validity window in milliseconds from client_ts_ms.
client_ts_ms88 bytesClient timestamp in epoch milliseconds, also the strictly increasing replay nonce for wallet-signed requests.
owner1632 bytesAccount owner public key.
signer4832 bytesPublic key that produced the signature; may equal owner.

Place Order#

Signed by: Trading key.

Used for: Place And Replace Orders

The body follows the 80-byte PermitHeader, starting at byte offset 80:

FieldByte offsetSizeNotes
size808 bytesOrder size in contracts.
price888 bytesLimit price multiplied by 1e6.
client_order_id968 bytesClient-assigned order identifier.
replace_client_order_id1048 bytesClient order id being replaced. 0xFFFFFFFFFFFFFFFF (all bits set) when replace_client_order_id is omitted.
expire_ts_ms1128 bytesGTT expiration timestamp. 0 when expires_ts_ms is omitted (non-GTT orders).
reserved12032 bytesZeros.
symbol15232 bytesFixed market symbol bytes, zero-padded.
reserved1844 bytesZeros.
offchain_fields_digest18832 bytesHash of offchain-only order attributes included as instructions to the matching engine. Reserved for future order semantics. Currently always the fixed value 0xdcaf53738f1d5f06164660498a75888e7f0c78ed5f1bc17be1dd1cb3ccf53ce3 (SHA256 of pascal.place_order.offchain_fields.v1).
reserved2201 byteZero byte.
side2211 byte0 = bid, 1 = ask.
order_type2221 byte0 = limit, 1 = market. Defaults to 0 (limit).
tif2231 byte0 = GTC, 1 = GTT, 2 = IOC. Defaults to 0 (GTC).
flags2241 bytePacked order flags: bit 0 = post_only, bit 1 = reduce_only, bit 2 = allow_missing_replace, bit 3 = replace_with_open_size. Defaults to 0 when no flag is set; other bits must be zero.
_padding12257 bytesTrailing padding. Must be zero.

Test vector

{
  "permit": "PlaceOrderPermit",
  "signer_role": "trading_key",
  "private_key_hex": "0808080808080808080808080808080808080808080808080808080808080808",
  "public_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "deployment_id": 3,
  "message_hex": "01010300881300000094962793010000ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22c1398f62c6d1a457c51ba6a4b5f3dbd2f69fca93216218dc8997e416bd17d93ca0a0000000000000070640800000000002a000000000000002b000000000000008082cd2793010000000000000000000000000000000000000000000000000000000000000000000053494d5f4556454e545f312e4d41524b45545f3100000000000000000000000000000000dcaf53738f1d5f06164660498a75888e7f0c78ed5f1bc17be1dd1cb3ccf53ce3000000010700000000000000",
  "signature": "46gXGMwTxEckctHD3tjVyedhGG95VK2F97F3idkSYFR3WNEX4rG9g6g8KDTQPoxzLieudk8WHDXw7xNV3R2WwKCd"
}
Examples
Request
json
[
  {
    "client_order_id": "42",
    "replace_client_order_id": "43",
    "symbol": "SIM_EVENT_1.MARKET_1",
    "side": "BID",
    "price": "0.550000",
    "size": "10",
    "tif": "GTT",
    "post_only": true,
    "reduce_only": true,
    "allow_missing_replace": true,
    "expires_ts_ms": "1731539600000",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "46gXGMwTxEckctHD3tjVyedhGG95VK2F97F3idkSYFR3WNEX4rG9g6g8KDTQPoxzLieudk8WHDXw7xNV3R2WwKCd"
    }
  }
]

Cancel Order#

Signed by: Trading key.

Used for: Cancel orders

The body follows the 80-byte PermitHeader, starting at byte offset 80:

FieldByte offsetSizeNotes
cancel_by801 byte0 = client order id, 1 = exchange order id.
reserved811 byteZero byte.
_padding0826 bytesReserved padding. Must be zero.
id888 bytesClient order id or exchange order id, depending on cancel_by.

Test vector

{
  "permit": "CancelOrderPermit",
  "signer_role": "trading_key",
  "private_key_hex": "0808080808080808080808080808080808080808080808080808080808080808",
  "public_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "deployment_id": 3,
  "message_hex": "01020300881300000094962793010000ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22c1398f62c6d1a457c51ba6a4b5f3dbd2f69fca93216218dc8997e416bd17d93ca00000000000000002a00000000000000",
  "signature": "591YziUjZzro24mamxgRrSDdYD71NpsDBmU4UGSyrBYbFiWzYaLjR9he1P5DP8djMKMJzHTsZzwRAagvLqgtiXnc"
}
Examples
Request
json
[
  {
    "client_order_id": "42",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "591YziUjZzro24mamxgRrSDdYD71NpsDBmU4UGSyrBYbFiWzYaLjR9he1P5DP8djMKMJzHTsZzwRAagvLqgtiXnc"
    }
  },
  {
    "client_order_id": "43",
    "auth": {
      "client_ts_ms": "1731536000000",
      "recv_window_ms": "5000",
      "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
      "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
      "signature": "3wFZKJWKA92W6iPszoKQccQmrVEfd6hU68HY6mqWYKEH8yFKQEFRJs1tKmrGDy1PMd3wWwcdcYSVoTgrW5KSnP3f"
    }
  }
]

Create Trading Key#

Signed by: Wallet key.

Used for: Create A Trading Key

Wallets sign the UTF-8 JSON array below, not the binary permit. The binary body follows the 80-byte PermitHeader, starting at byte offset 80:

FieldByte offsetSizeNotes
name8016 bytesFixed-length trading key name bytes, UTF-8.
trading_key9632 bytesTrading key public key being authorized.
expiration1288 bytesTrading key expiration timestamp in milliseconds.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"create_trading_key".
1namestringTrading key name.
2trading_keystringTrading key public key, base58.
3expirationintegerExpiration timestamp in milliseconds.
4versionintegerPermit envelope version.
5deployment_idintegerDeployment identifier.
6recv_window_msintegerRequest validity window in milliseconds.
7client_ts_msintegerClient timestamp in milliseconds.
8ownerstringAccount owner public key, base58.
9signerstringMust equal owner.

Test vector

{
  "permit": "CreateTradingKeyPermit",
  "signer_role": "owner_wallet",
  "private_key_hex": "0707070707070707070707070707070707070707070707070707070707070707",
  "public_key": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "deployment_id": 3,
  "message_utf8": "[\"create_trading_key\",\"api-doc-key\",\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\",1732140800000,1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\"]",
  "signature": "kyc2KdqPTeEWqoK3pXR2rgPUiK4zXg9QdQffPUWk8eALpFy7uaHBEsASt7RQyVzDeubHVsPqQAHXrt8m8KZ78Ep"
}
Examples
Request
json
{
  "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "name": "api-doc-key",
  "expiration_ts_ms": "1732140800000",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "kyc2KdqPTeEWqoK3pXR2rgPUiK4zXg9QdQffPUWk8eALpFy7uaHBEsASt7RQyVzDeubHVsPqQAHXrt8m8KZ78Ep"
  }
}

Revoke Trading Key#

Signed by: Wallet key.

Used for: Revoke A Trading Key

Wallets sign the UTF-8 JSON array below, not the binary permit. The binary body follows the 80-byte PermitHeader, starting at byte offset 80:

FieldByte offsetSizeNotes
trading_key8032 bytesTrading key public key being revoked.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"revoke_trading_key".
1trading_keystringTrading key public key, base58.
2versionintegerPermit envelope version.
3deployment_idintegerDeployment identifier.
4recv_window_msintegerRequest validity window in milliseconds.
5client_ts_msintegerClient timestamp in milliseconds.
6ownerstringAccount owner public key, base58.
7signerstringMust equal owner.

Test vector

{
  "permit": "RevokeTradingKeyPermit",
  "signer_role": "owner_wallet",
  "private_key_hex": "0707070707070707070707070707070707070707070707070707070707070707",
  "public_key": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "deployment_id": 3,
  "message_utf8": "[\"revoke_trading_key\",\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\"]",
  "signature": "3muKct6upyRNAr1CPdYcxQGCojpuB4gaEnWvbNwEBJ6rrKaThRZrKY56rpYbFEsGUZqJ4iCTJqYrPuxfGYVzXQX3"
}
Examples
Request
json
{
  "trading_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "3muKct6upyRNAr1CPdYcxQGCojpuB4gaEnWvbNwEBJ6rrKaThRZrKY56rpYbFEsGUZqJ4iCTJqYrPuxfGYVzXQX3"
  }
}

Signed Withdrawal#

Signed by: Wallet key.

Used for: Withdraw Collateral

Wallets sign the UTF-8 JSON array below, not the binary permit. The binary body follows the 80-byte PermitHeader, starting at byte offset 80:

FieldByte offsetSizeNotes
gross_amount808 bytesGross withdrawal amount in micro-USD.
destination_authority8832 bytesOwner of the destination token account.
destination_token_account12032 bytesDestination token account.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"withdrawal".
1gross_amountintegerGross withdrawal amount in micro-USD.
2destination_authoritystringDestination authority public key, base58.
3destination_token_accountstringDestination token account public key, base58.
4versionintegerPermit envelope version.
5deployment_idintegerDeployment identifier.
6recv_window_msintegerRequest validity window in milliseconds.
7client_ts_msintegerClient timestamp in milliseconds.
8ownerstringAccount owner public key, base58.
9signerstringMust equal owner.

Test vector

{
  "permit": "SignedWithdrawalPermit",
  "signer_role": "owner_wallet",
  "private_key_hex": "0707070707070707070707070707070707070707070707070707070707070707",
  "public_key": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "deployment_id": 3,
  "message_utf8": "[\"withdrawal\",25000000,\"29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2\",\"3JF3sEqM796hk5WFqA6EtmEwJQ9quALszsfJyvXNQKy3\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\"]",
  "signature": "61YYUBk6BTXkfrksMbqDLUNxwo6jhMPqdP7fNnWjNF2FYvMDujTsaUxfCiRxXX72jxy4oLtt6AtLodCvjDi4XpDn"
}
Examples
Request
json
{
  "amount": "25.000000",
  "destination_authority": "29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2",
  "destination_token_account": "3JF3sEqM796hk5WFqA6EtmEwJQ9quALszsfJyvXNQKy3",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "61YYUBk6BTXkfrksMbqDLUNxwo6jhMPqdP7fNnWjNF2FYvMDujTsaUxfCiRxXX72jxy4oLtt6AtLodCvjDi4XpDn"
  }
}

Register Deposit Address#

Signed by: Wallet key.

Used for: Register A Deposit Address

There is no fixed binary layout; sign the UTF-8 JSON array field order below.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"register_deposit_address".
1ownerstringAccount owner public key, base58.
2versionintegerPermit envelope version.
3deployment_idintegerDeployment identifier.
4recv_window_msintegerRequest validity window in milliseconds.
5client_ts_msintegerClient timestamp in milliseconds.
6ownerstringRepeated owner public key from auth.owner, base58.
7signerstringMust equal owner.

Then append at most one attribution suffix; requests carrying both codes are rejected:

  • Invite Code registration: index 8 is invite_code.
  • Referral-code registration: index 8 is the literal string "referral_code", index 9 is referral_code. The literal tag is part of the signed payload and must be included; it keeps referral-code payloads distinct from invite-attributed ones.
  • No attribution: the payload ends at signer.

Test vector (Invite Code)

{
  "permit": "RegisterDepositAddressPermit",
  "signer_role": "owner_wallet",
  "private_key_hex": "0707070707070707070707070707070707070707070707070707070707070707",
  "public_key": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "deployment_id": 3,
  "message_utf8": "[\"register_deposit_address\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"4PHPW-S4BM2-U9MS6-KP2JF\"]",
  "signature": "3y43tLaBWdQyxkj7BAbbSEmGfd6Fu3Xop5ATyWcLiC4XYFgNJSmoKXmyC8Qnu7oRUzAnEycmfSLzdyAV5L3cxecc"
}

Test vector (referral code)

{
  "permit": "RegisterDepositAddressReferralCodePermit",
  "signer_role": "owner_wallet",
  "private_key_hex": "0707070707070707070707070707070707070707070707070707070707070707",
  "public_key": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "deployment_id": 3,
  "message_utf8": "[\"register_deposit_address\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"referral_code\",\"k7wq2m4x\"]",
  "signature": "2GVZuf1birCpfr6DKbu1ALSxRTpVnzFAa6oYqnEsnDUB2TFqeYTsKEZrCxNzATyCZbEaLW7koZJPRh9euhYiLtm4"
}
Examples
Request
json
{
  "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
  "invite_code": "4PHPW-S4BM2-U9MS6-KP2JF",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signature": "3y43tLaBWdQyxkj7BAbbSEmGfd6Fu3Xop5ATyWcLiC4XYFgNJSmoKXmyC8Qnu7oRUzAnEycmfSLzdyAV5L3cxecc"
  }
}

Generate Referral Code#

Signed by: Active trading key.

Used for: Generate A Referral Code

There is no fixed binary layout; sign the UTF-8 JSON array field order below.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"generate_referral_code".
1versionintegerPermit envelope version.
2deployment_idintegerDeployment identifier.
3recv_window_msintegerRequest validity window in milliseconds.
4client_ts_msintegerClient timestamp in milliseconds.
5ownerstringAccount owner public key, base58.
6signerstringActive trading key public key, base58.

Test vector

{
  "permit": "GenerateReferralCodePermit",
  "signer_role": "trading_key",
  "private_key_hex": "0808080808080808080808080808080808080808080808080808080808080808",
  "public_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "deployment_id": 3,
  "message_utf8": "[\"generate_referral_code\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\"]",
  "signature": "4UpZqQfCNDbj3NEnkkRUQsW5USTTcv7JekvPWyHsEC2ifcc3R8PwEVd5xVWZuSNacyNHrZg29ZqPeUME3GWGpnCN"
}
Examples
Request
json
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "4UpZqQfCNDbj3NEnkkRUQsW5USTTcv7JekvPWyHsEC2ifcc3R8PwEVd5xVWZuSNacyNHrZg29ZqPeUME3GWGpnCN"
  }
}

Claim Custom Referral Code#

Signed by: Active trading key.

Used for: Claim A Custom Referral Code

There is no fixed binary layout; sign the UTF-8 JSON array field order below. The exact submitted code is part of the signed payload.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"claim_custom_referral_code".
1referral_codestringExact owner-chosen code.
2versionintegerPermit envelope version.
3deployment_idintegerDeployment identifier.
4recv_window_msintegerRequest validity window in milliseconds.
5client_ts_msintegerClient timestamp in milliseconds.
6ownerstringAccount owner public key, base58.
7signerstringActive trading key public key, base58.

Test vector

{
  "permit": "ClaimCustomReferralCodePermit",
  "signer_role": "trading_key",
  "private_key_hex": "0808080808080808080808080808080808080808080808080808080808080808",
  "public_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "deployment_id": 3,
  "message_utf8": "[\"claim_custom_referral_code\",\"trader1\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\"]",
  "signature": "26FzDQPEDR6u7rKhkaEEJL1YSyKpbUCCibQtQxkDfEfEjyAsJu17X5p8TtuW5iXQfCNbs9gjEsFecqPv4yffwsgS"
}
Examples
Request
json
{
  "referral_code": "trader1",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "26FzDQPEDR6u7rKhkaEEJL1YSyKpbUCCibQtQxkDfEfEjyAsJu17X5p8TtuW5iXQfCNbs9gjEsFecqPv4yffwsgS"
  }
}

Set Username#

Signed by: Active trading key.

Used for: Set A Username

There is no fixed binary layout; sign the UTF-8 JSON array field order below. The exact submitted Username, including casing or the empty clear value, is part of the signed payload.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"set_username".
1usernamestringExact submitted Username, or an empty string to clear.
2versionintegerPermit envelope version.
3deployment_idintegerDeployment identifier.
4recv_window_msintegerRequest validity window in milliseconds.
5client_ts_msintegerClient timestamp in milliseconds.
6ownerstringAccount owner public key, base58.
7signerstringActive trading key public key, base58.

Test vector

{
  "permit": "SetUsernamePermit",
  "signer_role": "trading_key",
  "private_key_hex": "0808080808080808080808080808080808080808080808080808080808080808",
  "public_key": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
  "deployment_id": 3,
  "message_utf8": "[\"set_username\",\"Trader\",1,3,5000,1731536000000,\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\"]",
  "signature": "2aEt4QQeG3iFTD6tAZvDnfKzna7cnVh18eq6cveGho9mdwfDGLoFjs1fsyYieXuZKKGdpFFCH8VgvVQJ2K1BBmjV"
}
Examples
Request
json
{
  "username": "Trader",
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "2aEt4QQeG3iFTD6tAZvDnfKzna7cnVh18eq6cveGho9mdwfDGLoFjs1fsyYieXuZKKGdpFFCH8VgvVQJ2K1BBmjV"
  }
}

Owner Invite Codes#

Signed by: Wallet key or active trading key.

Used for: Invite Codes

There is no fixed binary layout; sign the UTF-8 JSON array field order below.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"owner_invite_codes".
1versionintegerPermit envelope version.
2deployment_idintegerDeployment identifier.
3recv_window_msintegerRequest validity window in milliseconds.
4client_ts_msintegerClient timestamp in milliseconds.
5ownerstringAccount owner public key, base58.
6signerstringOwner wallet or active trading key public key, base58.
Examples
Request
json
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "u9FGGLvU3mbj8n64XVg1MaR4bBecTDteBxEdpdSvxeuVzi5ViS1nVEJroHt5e8CdADvzngcJy1TtVto4ymVFsYH"
  }
}

Owner Referrals#

Signed by: Wallet key or active trading key.

Used for: Legacy Signed Referrals

There is no fixed binary layout; sign the UTF-8 JSON array field order below.

JSON field order:

IndexFieldJSON typeNotes
0command_typestring"owner_referrals".
1versionintegerPermit envelope version.
2deployment_idintegerDeployment identifier.
3recv_window_msintegerRequest validity window in milliseconds.
4client_ts_msintegerClient timestamp in milliseconds.
5ownerstringAccount owner public key, base58.
6signerstringOwner wallet or active trading key public key, base58.
Examples
Request
json
{
  "auth": {
    "client_ts_ms": "1731536000000",
    "recv_window_ms": "5000",
    "owner": "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB",
    "signer": "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1",
    "signature": "2gypF8ds55gagM8c9koyp7g4eZr4vSaehkbVFP9ohSzd4EMZsE29xYyi8Jcfp7BLLK3hWK7SP1JVHAoZBoYre6xm"
  }
}

Reference Types#

Error Codes#

API errors return an error envelope whose data object has these fields:

FieldTypeDescription
codeApiErrorCodeStable, machine-readable error code.
messagestringHuman-readable error message. May evolve over time. For debugging and display only.

Error codes are SCREAMING_SNAKE_CASE identifiers. Use code for client control flow and message for display, logging, and more specific diagnostics.

Stable error codes:

ApiErrorCode#

Stable machine-readable error codes for public API operations.

ValueDescription
"INVALID_JSON"The request body could not be parsed as valid JSON.
"INVALID_REQUEST"The request was well-formed JSON but failed validation, such as a missing or malformed field, or a value outside its accepted range.
"UNAUTHORIZED"The request signature was missing or invalid, or the signing key is not authorized to act for the account.
"SYMBOL_NOT_FOUND"A request referenced a symbol that does not exist.
"ACCOUNT_NOT_FOUND"A request referenced an account owner that does not exist.
"ACCOUNT_RESTRICTED"The account is restricted by compliance policy.
"GEO_RESTRICTED"The request is restricted by geography policy.
"INVALID_GEO_HEADERS"Required Cloudflare geo headers were missing, malformed, or unusable.
"ORDER_NOT_FOUND"The referenced order does not exist or is no longer open.
"TRADING_KEY_NOT_FOUND"The referenced trading key does not exist for the account.
"DUPLICATE_REQUEST"A request with the same signature was already received.
"DUPLICATE_CLIENT_ORDER_ID"An open order with the same client_order_id already exists for the account.
"UNSUPPORTED_ORDER_TYPE"The requested order type is not supported.
"INVALID_SIZE"The order size is zero or otherwise invalid for the market.
"INVALID_PRICE"The order price is out of range or not a multiple of the market's tick size.
"INVALID_EXPIRATION"The provided expiration timestamp is invalid for the order's time in force.
"WOULD_CROSS"A post-only order was rejected because it would have crossed the book and traded as a taker.
"SELF_TRADE"The order was rejected because it would have matched against another of the account's own resting orders.
"INSUFFICIENT_COLLATERAL"The account does not have enough free collateral to place the order.
"NOT_FUNDED"The account has not been funded and cannot trade.
"DEPOSIT_ADDRESS_NOT_REGISTERED"The account has not registered a Pascal deposit address.
"MARKET_RESOLVED"The market has resolved and no longer accepts orders.
"POSITION_LIMIT_EXCEEDED"The order would move the account's position beyond the market's position limit.
"TRADING_KEY_SLOTS_FULL"The account has reached its maximum number of trading keys.
"TRADING_KEY_PUB_KEY_ALREADY_USED"A trading key with the same public key already exists for the account.
"TRADING_KEY_NAME_ALREADY_USED"A trading key with the same name already exists for the account.
"TRADING_KEY_MUST_OVERWRITE_WITH_GREATER_EXPIRATION"Overwriting an existing trading key requires an expiration later than the current one.
"REDUCE_ONLY_REJECTED"A reduce-only order was rejected because it would have increased the account's position.
"PENDING_WALLET_OP_CONFLICTS"Another wallet-signed operation (such as a withdrawal) is already in flight for the account; retry once it settles.
"PENDING_DEPOSIT_REGISTRATION"Deposit-address registration is already being processed for this account.
"REPLACEMENT_ORDER_NOT_FOUND"The order targeted for replacement does not exist or is no longer open.
"REPLACEMENT_ORDER_DIFF_MARKET"The replacement order's market differs from the order being replaced.
"REPLACEMENT_ORDER_DIFF_SIDE"The replacement order's side differs from the order being replaced.
"RECV_WINDOW_TOO_LARGE"The request's recv_window_ms exceeds the maximum allowed value.
"CLIENT_TS_TOO_FAR_IN_FUTURE"The request's client_ts_ms is too far ahead of server time, beyond the allowed clock-skew tolerance.
"CLIENT_TS_EXPIRED"The request expired: server_time_ms - client_ts_ms exceeded recv_window_ms.
"CLIENT_TS_NOT_INCREASING"A wallet-signed request's client_ts_ms did not increase past the last accepted timestamp nonce for the account.
"CONFLICT"The request conflicts with existing state, such as creating something that already exists.
"RATE_LIMITED"The client exceeded its rate limit; slow down before retrying.
"BACKPRESSURE"The server is shedding load and rejected the request; retry after a short delay.
"NOT_READY"The service is still starting up or catching up and cannot serve the request yet.
"BAD_GATEWAY"Transient upstream failure, retry.
"SERVICE_UNAVAILABLE"The service is temporarily unavailable; retry later.
"WRITE_RESULT_UNAVAILABLE"The write request reached the write pipeline, but the service could not return a result.
"TIMEOUT"The request timed out before completing.
"INTERNAL_ERROR"An unexpected internal error occurred.
"INVITE_CODE_REQUIRED"An invite code is required to perform this action.
"INVITE_CODE_INVALID"The provided invite code is invalid or expired.
"INVITE_CODE_ALREADY_EXISTS"An invite code with the same value already exists.
"INVITE_REDEMPTIONS_CAP_REACHED"The invite code has reached its maximum number of redemptions.
"REFERRAL_CODE_INVALID"The provided Referral Code could not be resolved for registration.
"BUILDER_FEE_RATE_INVALID"The order's builder fee rate is outside the accepted range.
"BUILDER_INVALID"The order's builder pubkey is not accepted as a builder.
"BUILDER_COLLATERAL_BELOW_MINIMUM"The order's builder is below the minimum free collateral required to be named on new orders.