POST /info), the signed write API
(POST /exchange), and the realtime WebSocket streams behind two typed client
classes. Authentication is EIP-712 wallet signing (secp256k1) — there are no
API keys to manage.
upsidemax/upside-python-sdk
Source, examples, and issue tracker on GitHub. MIT licensed.
- EIP-712 request signing across the Agent and Typed paths — handled for you.
- Synchronous REST with timeouts and safe retries, plus a threaded WebSocket client that auto-reconnects.
- Checked inputs: every field is validated before signing, so a malformed request fails locally instead of being silently dropped.
- Raw-dict responses, typed inputs, and full type hints (ships
py.typed). - Agent (API-wallet) delegation, TP/SL, leverage / margin, collateral, and portfolio-margin actions.
Installation
requests, websocket-client,
eth-account, eth-utils. This documentation describes version 0.3.0.
Quick start
marginAvailableForOrder in user_account
first. The Quickstart walks through the same flow with the
checks built in.
The two clients
Info — reads & streams
Market data, account and order queries, history, and realtime WebSocket
subscriptions. No wallet or signing required.
Exchange — signed writes
Orders, cancels, margin and leverage, TP/SL, collateral, portfolio margin,
and agent delegation — every call signed with your wallet key.
Key behaviors
Prices and sizes are raw integer strings. The wire protocol is
integer-only: convert a display price with the contract’s
priceScale and a
display size with its qtyScale, both read from
configs — never hardcode a scale. Scales differ per contract
and are often 0.- Orders and cancels have two receipt shapes.
order,cancel,cancel_by_cloid,cancel_all, andmodifytake asyncflag. Withsync=Truethe call returns once the action has executed, with onestatuses[]entry per submitted item. Without it the server answers202with{"status": "accepted", "response": {"type": "accepted", "data": {"count": n}}}and the per-order outcome — including any rejection — arrives only on theorderUpdatesWebSocket channel. See Receipts. - Every other action is synchronous. Margin, leverage, TP/SL, collateral, and agent actions always return their result in the HTTP response.
- HTTP 200 does not always mean success. Business rejections come back as
HTTP 200 — a per-item
errorinsidestatuses[], or a non-zeroerrorCodeinsideresponse.data. Always inspect the response.
Timeouts and retries
Every request has a timeout — 5 seconds to connect and 10 seconds to read by default; passtimeout= (seconds, or a (connect, read) tuple) to either
client. Failed requests are retried with exponential backoff (max_retries=3,
backoff=0.5 seconds; max_retries=0 disables retrying) only where resending
is safe:
A signed write cannot be resent once it may have reached the server: the resend
carries the same nonce and is rejected as a duplicate even if the first attempt
executed. So instead of guessing, the SDK raises
OutcomeUnknownError.
Error handling
Every SDK error derives fromUpsideError:
Arguments are checked before anything is signed: a malformed id, size, price,
time-in-force, or type raises Python’s own
ValueError / TypeError, and
nothing is sent.
user_orders
for orders still resting and order_history
/ user_fills for orders that already filled or were canceled — or watch the
orderUpdates channel. orders_by_cloids alone is not enough: it only sees
live orders, so an order that filled immediately looks missing. History is
indexed shortly after execution; re-check for a few seconds before concluding
the order did not land.
Environments
Without abase_url the SDK uses Mainnet; pass constants.DEVNET_API_URL
for Devnet:
Exchange signs with the chain ID of the environment its base_url points at.
If you reach Mainnet through a different address, pass chain_id=9788
explicitly — a signature over the wrong chain ID is rejected with
401 SIGNATURE_INVALID.
Contract IDs, coin IDs, scales, and tick / step sizes are server-assigned and
differ per environment — always read them from Info.configs() rather than
hardcoding.
Signing
Every/exchange write is authorized by an EIP-712 signature over the fixed
domain (Exchange / version 1 / the environment’s chain ID / zero verifying contract).
The SDK selects the correct path automatically — the Typed path for
registerAccount, approveAgent, revokeAgent, lockCollateral,
unlockCollateral, and transferBetweenDeployers, and the Agent path for
everything else. Nonces are strictly increasing millisecond timestamps managed
per Exchange instance; share one NonceManager between clients that sign with
the same key.
Browser-extension wallets sign typed structs with their active chain id rather
than the environment’s. To match such a signature, pass Exchange(..., signature_chain_id=...);
with a raw private key, leave it unset. See Authentication
for the full scheme.