Skip to main content
UpsideMAX uses a small set of consistent conventions for data representation throughout the API. Understanding these conventions upfront will save you debugging time — in particular, note that prices and sizes are strings, not numbers, and that raw integer values in responses must be scaled before display.

Numeric Values: Strings to Avoid Float Precision Loss

All prices, quantities, and monetary amounts are raw integer strings — a whole number with no decimal point, carried as a JSON string rather than a JSON number. This avoids the precision loss that occurs when JSON parsers store large numbers as IEEE 754 doubles.
Always treat these fields as strings when reading or writing. Pass them through to your exchange layer without converting to float. The same applies to requests: p and s in an order are raw integer strings, not display values. The server rejects a value with a real fractional part ("85000.5" → 400). A lossless suffix such as "85000.0" is accepted, but it still denotes the raw integer 85000 — requests do not accept decimal values.

Raw Integers and Scale Factors

Numeric values in API responses are raw integers. To obtain the human-readable display value, divide by 10 raised to the scale exponent for that field. Use the configs endpoint to retrieve priceScale and qtyScale for each contract.
Use exact decimal arithmetic in both directions. int(raw) / 10 ** scale returns a float and drifts on values such as 0.1 × 10 ** 4 — a drifted size fails step validation or, worse, passes as the wrong quantity.

Display to Raw — the order path

The wire protocol is integer-only. Convert before signing; the signature covers the raw values.
1

Read the contract's own parameters

Fetch priceScale, qtyScale, tickSize, and stepSize from configs for the exact contract. Never reuse another contract’s values, and never use a value from documentation.
2

Scale to raw integers

price_raw = display_price × 10 ** priceScale and size_raw = display_size × 10 ** qtyScale, computed with Decimal. If either result is not a whole number, the input carries more precision than the contract accepts.
3

Validate, do not round

price_raw must be a positive multiple of tickSize; size_raw a positive multiple of stepSize. Reject the request if it is not. Silently rounding changes the order the caller asked for.
4

Send as strings

Place the raw integers into p and s as decimal strings, then sign.
A contract whose priceScale is 0 takes the display price unchanged. Scales of 0 are common. Applying a scale copied from another contract or from an example misprices the order by orders of magnitude, and the server accepts it — the resulting fill is real.

Which scale applies

Scales come in three kinds and must not be mixed. All of them are read from configs.
Do not convert amounts with priceScale. Amounts are denominated in a coin, and that coin’s precision is independent of the contract’s price precision — using the wrong scale shifts equity and PnL by orders of magnitude.
Which coin’s szDecimals applies depends on where the amount appears: Look the coin up as shown above rather than hardcoding a number of decimal places — different coins have different szDecimals.

Fixed-denominator fields

The following fields do not use any scale from configs. Each has a fixed denominator:
Trading fees are scaled by 1e5, not 1e4. A fee value of 5 is 0.005%, not 0.05%. Instead of hardcoding the denominator, divide by the feePrecision field returned by configs and userAccount.
A fee field that is not set is returned as JSON null. 0 (a 0% fee) and negative values (rebates, negative funding) are real values and are returned as such — do not treat 0 as “not set”.

Large integers and IDs

Raw values are int64 and can exceed JavaScript’s Number.MAX_SAFE_INTEGER (2^53 − 1); beyond that, a default JSON.parse loses precision silently. Parse amounts with a string-preserving decoder or BigInt. ID fields such as positions[].id, accountId, blockHeight, nonce, and cloid are identifiers that are never scaled, but must also be handled as strings or big integers, never as floating-point numbers.
Each contract’s priceScale and qtyScale are published by the configs endpoint. The values above are illustrative only. Scales differ per contract, are frequently 0, and change as contracts are listed or reconfigured — always read them from configs for the exact contract you are trading, and never carry a value over from another contract or from an example.

Naming quirks to watch

These are wire-format facts, not typos. They cost a round trip each to discover, so they are collected here.

Timestamps

All timestamps are int64 Unix milliseconds — the number of milliseconds elapsed since 1970-01-01T00:00:00Z. Timestamps are represented as JSON integers (not strings).

Ethereum Addresses

Addresses follow the standard Ethereum format: 0x prefix followed by 40 lowercase hexadecimal characters (42 characters total).
Always send addresses in lowercase. The server compares addresses case-insensitively, but using lowercase consistently avoids subtle bugs in signature verification and logging.

Contract (Asset) IDs

Each perpetual contract has an integer ID stored in the a field of order, cancel, and modify actions. Call configs to enumerate the contracts and their IDs — the listed set changes, IDs are assigned by the server, and contractId 1/2/3 do not map to any fixed pair. Match on name and never hardcode an ID.

Client Order IDs (cloid)

A client order ID is an int64 decimal string that you assign when placing an order. It must be a unique positive integer. You can use it later to cancel or query orders without needing the server-assigned oid. The format is validated strictly wherever a client order ID is accepted — c in order and cancel, cloid in cancelByCloid, and cloid / c in modify:
  • Decimal digits (0–9) only, at most 20 bytes, and within the int64 range.
  • Hexadecimal, UUIDs, prefixed values (0x…), and signed values are rejected with 400 INVALID_PARAM, errors[].reason INVALID_FORMAT (OUT_OF_RANGE when the value exceeds int64).
  • Omitting the field or sending an empty string "" means no client order ID is set.
  • In cancel, c is used to locate the order only when o (order ID) is absent, and is validated only in that case. When both are sent, o takes precedence.
Do not use 0 as a client order ID. An unset client order ID is reported as "0", so an order placed with 0 cannot be told apart from one with no client order ID in userOrders.
A Unix millisecond timestamp works well as a client order ID for most use cases — it is unique, monotonically increasing, and easy to generate without a counter.

Short Key Convention (Order Payloads)

Order-related actions use single-letter compact keys to reduce payload size. The mapping is: Example order object using short keys:

Full Key Convention (Margin and Mode Actions)

Margin and account mode actions use full field names rather than single-letter abbreviations: Example leverage update using full keys: