Set Up Perps Access
- TypeScript
- Python
- API
1
Create a Secure Client
Create a
SecureClient for the Polymarket wallet that owns the Perps account,
using the signer that controls it.import { createSecureClient } from "@polymarket/client";
import { privateKey } from "@polymarket/client/viem";
const client = await createSecureClient({
wallet: process.env.POLYMARKET_WALLET_ADDRESS!,
signer: privateKey(process.env.PRIVATE_KEY!),
});
This example uses Viem for wallet signing. See the TypeScript tooling
guide for other wallet library
integrations.
2
Open a Perps Session
Open a Perps session. By default, delegated Perps credentials expire after one
week.You can also set the session lifetime and label explicitly.
const session = await client.openPerpsSession();
expiresIn is
measured in milliseconds.const session = await client.openPerpsSession({
expiresIn: 7 * 24 * 60 * 60 * 1000,
label: "trading-app",
});
1
Create a Secure Client
Create an
AsyncSecureClient for the Polymarket wallet that owns the Perps
account, using the signer that controls it.import os
from polymarket import AsyncSecureClient
client = await AsyncSecureClient.create(
private_key=os.environ["PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
)
2
Open a Perps Session
Open a Perps session. By default, delegated Perps credentials expire after one
week.You can also set the session lifetime and label explicitly.
session = await client.open_perps_session()
expires_in is a
timedelta.from datetime import timedelta
session = await client.open_perps_session(
expires_in=timedelta(days=7),
label="trading-app",
)
Start by registering new proxy credentials for an existing Polymarket account.
If you do not have one yet, create an account at
polymarket.com first.
1
Generate a Proxy Signer
Generate a fresh keypair for the proxy signer. Perps uses this key to authorize
trading operations on behalf of the Polymarket account signer, without requiring
the account signer to sign every order.Use any secure EVM key-generation flow.Store the private key securely. It will be used to sign Perps trading operations
for the Polymarket account signer.
$ cast wallet new
Successfully created new keypair.
Address: <proxy_address>
Private key: <proxy_private_key>
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const privateKey = generatePrivateKey();
const { address } = privateKeyToAccount(privateKey);
2
Create Proxy Typed Data
Create an EIP-712 Use these values consistently in the typed data and request body.
CreateProxy payload.{
"domain": {
"name": "Polymarket",
"version": "1",
"chainId": 137
},
"primaryType": "CreateProxy",
"types": {
"CreateProxy": [
{ "name": "addr", "type": "address" },
{ "name": "exp", "type": "uint64" },
{ "name": "salt", "type": "uint64" },
{ "name": "ts", "type": "uint64" }
]
},
"message": {
"addr": "<proxy_address>",
"exp": 1767225600000,
"salt": 123456789,
"ts": 1767000000000
}
}
| Field | Value |
|---|---|
addr | <proxy_address> from the previous step. |
exp | Unix timestamp in milliseconds when the proxy signer stops being valid. |
ts | Current Unix timestamp in milliseconds. |
salt | Random integer generated for this signed request. |
3
Sign Proxy Typed Data
Sign the
CreateProxy typed data with the signer for the Polymarket account.Viem
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("<polymarket_account_signer_private_key>");
const signature = await account.signTypedData({
domain: {
name: "Polymarket",
version: "1",
chainId: 137,
},
primaryType: "CreateProxy",
types: {
CreateProxy: [
{ name: "addr", type: "address" },
{ name: "exp", type: "uint64" },
{ name: "salt", type: "uint64" },
{ name: "ts", type: "uint64" },
],
},
message: {
addr: "<proxy_address>",
exp: 1767225600000,
salt: 123456789,
ts: 1767000000000,
},
});
4
Authorize the Proxy Signer
Authorize the generated proxy signer for your Perps account.Map the request fields to the values from the previous steps.Store the proxy private key, proxy address, proxy secret, and expiry securely.
curl -X POST "https://api.perpetuals.polymarket.com/v1/account/proxy" \
-H "content-type: application/json" \
-d '{
"op": {
"type": "createProxy",
"args": {
"owner": "<polymarket_account_signer_address>",
"proxy": "<proxy_address>",
"expiry": 1767225600000
}
},
"sig": "<signature>",
"salt": 123456789,
"ts": 1767000000000,
"label": "trading-app"
}'
owneris the signer address for the Polymarket account.proxyis<proxy_address>from the first step.sigis<signature>from the previous step.saltandtsare the same values used in the typed data from step 2.labelis an identifier for this proxy credential instance.
{
"secret": "<proxy_secret>"
}
Session Lifecycle
Open an authenticated session to start trading, read private Perps account data, and receive private real-time updates.- TypeScript
- Python
- API
1
Listen for Session Events
After opening a Perps session, iterate over it to receive private real-time
updates.This example handles a few common session events.
const session = await client.openPerpsSession();
for await (const event of session) {
switch (event.type) {
case "order":
// Update local order state.
break;
case "fill":
// Update position, PnL, or execution history.
break;
case "portfolio":
// Refresh margin, equity, and position views.
break;
}
}
order and fill are the
core trading updates, while portfolio provides periodic account-level snapshots
for margin, equity, positions, and withdrawable balance.See Reconcile Trade State for how to use
these events to keep local trading state in sync.2
Close the Session
You can close the session at any time by calling
session.close(). Closing a
session releases local resources; stored credentials can still be resumed until
they expire.for await (const event of session) {
if (shouldCloseSession) {
await session.close();
break;
}
// …
}
1
Listen for Session Events
After opening a Perps session, iterate over it to receive private real-time
updates.This example handles a few common session events.
session = await client.open_perps_session()
async for event in session:
if event.type == "order":
# Update local order state.
pass
elif event.type == "fill":
# Update position, PnL, or execution history.
pass
elif event.type == "portfolio":
# Refresh margin, equity, and position views.
pass
order and fill are the
core trading updates, while portfolio provides periodic account-level snapshots
for margin, equity, positions, and withdrawable balance.See Reconcile Trade State for how to use
these events to keep local trading state in sync.2
Close the Session
You can close the session at any time by calling
session.close(). Closing a
session releases local resources; stored credentials can still be resumed until
they expire.async for event in session:
if should_close_session:
await session.close()
break
# …
1
Authenticate a WebSocket Connection
Connect to the Perps WebSocket production URL.After the connection opens, send an authentication frame with the proxy address
and proxy secret.Check the authentication response before subscribing to private channels.A successful acknowledgement includes
wss://ws.perpetuals.polymarket.com/v1/ws
{
"id": 1,
"req": "post",
"op": {
"type": "auth",
"args": {
"proxy": "<proxy_address>",
"secret": "<proxy_secret>"
}
}
}
{
"id": 1,
"data": {
"status": "ok",
"pod": "gateway-ws-7b68c9d4f5-m2n8p"
}
}
{
"id": 1,
"data": {
"status": "err",
"error": "<error_message>"
}
}
pod, the identity of the gateway pod
serving the connection. Record it with your connection logs so your activity
can be matched against exchange logs. It is correlation metadata only — pods
are not addressable, and a reconnect can land on a different pod. Treat the
field as optional: older pods omit it during a rolling deploy, and failures
never include it.2
Handle Session Events
Authenticated WebSocket connections receive private session update frames. These
examples show a few common events: These are examples of common session events, not the full event list.
orders and fills for trading activity,
and portfolio for periodic account-level snapshots.{
"ch": "orders",
"ts": 1767225600000,
"sq": 1234567890,
"data": {
"oid": 1234567890,
"iid": 1,
"buy": true,
"p": "65000.00",
"qty": "0.01",
"tif": "gtc",
"po": false,
"status": "open",
"rest": "0.01",
"fill": "0",
"cts": 1767225600000,
"uts": 1767225600000
}
}
{
"ch": "fills",
"ts": 1767225600000,
"sq": 1234567891,
"data": {
"tid": 987654321,
"oid": 1234567890,
"iid": 1,
"side": "long",
"p": "65000.00",
"qty": "0.01",
"taker": true,
"fee": "0.26",
"fea": "pUSD",
"psz": "0",
"pep": "0",
"pnl": "0",
"liq": false,
"adl": false,
"ts": 1767225600000
}
}
{
"ch": "portfolio",
"ts": 1767225600000,
"sq": 1234567892,
"data": {
"positions": [],
"margin": {
"total_account_value": "10.00",
"available_order_margin": "10.00",
"total_initial_margin": "0",
"total_maintenance_margin": "0",
"total_position_value": "0"
},
"withdrawable": "10.00",
"in_liquidation": false,
"fee_tier": 0,
"timestamp": 1767225600000
}
}
3
Keep the Connection Alive
Send an application-level ping from the client about every 25 seconds.The server responds with a pong payload.Treat the connection as stale if no messages arrive for about 65 seconds.
{
"id": 0,
"req": "post",
"op": {
"type": "ping"
}
}
{
"id": 0,
"data": {
"status": "ok",
"ts": 1767225600000,
"sq": 1234567890
}
}
4
Close the Connection
Close the WebSocket connection when the current workflow is finished. Closing the
connection does not revoke the proxy credential.
Resume a Session
Resume a session when stored credentials are still valid and a Perps workflow needs to continue in a new runtime context.- TypeScript
- Python
- API
Read where Pass stored credentials back to
session.credentials after opening a session and store the object in secure
credential storage.const credentials = session.credentials;
// credentials: PerpsCredentials
PerpsCredentials is:type PerpsCredentials = {
proxy: EvmAddress;
privateKey: PrivateKey;
secret: string;
expiresAt: number;
};
{
"proxy": "0x1111111111111111111111111111111111111111",
"privateKey": "0x2222222222222222222222222222222222222222222222222222222222222222",
"secret": "<proxy_secret>",
"expiresAt": 1766725200000
}
openPerpsSession() while they are still valid.
The SDK validates them and resumes the session.const session = await client.openPerpsSession({
credentials,
});
// session: PerpsSession
Read where For JSON-backed storage, serialize the model and keep the result encrypted.Pass stored credentials back to
session.credentials after opening a session and store the object in secure
credential storage.credentials = session.credentials
# credentials: PerpsCredentials
PerpsCredentials is:from polymarket import PerpsCredentials
# credentials: PerpsCredentials
{
"proxy": "0x1111111111111111111111111111111111111111",
"private_key": "0x2222222222222222222222222222222222222222222222222222222222222222",
"secret": "<proxy_secret>",
"expires_at": "2026-01-25T18:20:00Z"
}
stored_credentials = credentials.model_dump(mode="json")
open_perps_session() while they are still
valid. The SDK validates them and resumes the session.from polymarket import PerpsCredentials
credentials = PerpsCredentials.model_validate(stored_credentials)
session = await client.open_perps_session(credentials=credentials)
# session: PerpsSession
Resume an API session by reusing stored proxy credentials while they are still
valid.Store the credential material from the setup flow in secure credential storage.
For private REST reads, pass the proxy address and proxy secret as headers.For a new WebSocket connection, send the same authentication frame used when the
session was opened.If the credential has expired, register a new proxy credential before resuming
private workflows.
| Field | Use it to |
|---|---|
<proxy_address> | Identify the proxy credential. |
<proxy_private_key> | Sign Perps trading operations. |
<proxy_secret> | Authenticate private REST and real-time access. |
expiry | Know when to register a new proxy credential. |
curl "https://api.perpetuals.polymarket.com/v1/account/portfolio" \
-H "polymarket-proxy: <proxy_address>" \
-H "polymarket-secret: <proxy_secret>"
{
"id": 1,
"req": "post",
"op": {
"type": "auth",
"args": {
"proxy": "<proxy_address>",
"secret": "<proxy_secret>"
}
}
}