Documentation Index Fetch the complete documentation index at: https://docs.polymarket.us/llms.txt
Use this file to discover all available pages before exploring further.
Subscribe to real-time order updates, execution reports, fills, and cancellations for your trading activity using Python and gRPC.
Service Definition
Service: polymarket.v1.OrderEntryAPI
RPC: CreateOrderSubscription
Type: Server-side streaming
service OrderEntryAPI {
rpc CreateOrderSubscription ( CreateOrderSubscriptionRequest )
returns ( stream CreateOrderSubscriptionResponse );
}
Request Parameters
CreateOrderSubscriptionRequest
Field Type Required Description symbolslist[str]No Filter by symbols. Empty list = all symbols. accountslist[str]No Filter by trading accounts. Empty list = all accounts for authenticated user. snapshot_onlyboolNo If True, receive snapshot of current orders then close stream. If False (default), receive continuous updates.
Example Request
from polymarket.v1 import trading_pb2
# Subscribe to all orders for your accounts
request = trading_pb2.CreateOrderSubscriptionRequest(
symbols = [],
accounts = [],
snapshot_only = False
)
# Subscribe to specific symbol
request = trading_pb2.CreateOrderSubscriptionRequest(
symbols = [ "tec-nfl-sbw-2026-02-08-kc" ],
accounts = [],
snapshot_only = False
)
# Get snapshot only (current state)
request = trading_pb2.CreateOrderSubscriptionRequest(
symbols = [],
accounts = [],
snapshot_only = True
)
Response Messages
The stream returns CreateOrderSubscriptionResponse messages with multiple event types:
Response Fields
Field Type Description eventoneofOne of: heartbeat, snapshot, or update session_idstrUnique session identifier for this stream processed_sent_timeTimestampServer timestamp when response was sent
1. Heartbeat Messages
Keep-alive messages to confirm connection health.
if response.HasField( 'heartbeat' ):
print ( f "[ { datetime.now().strftime( '%H:%M:%S' ) } ] Heartbeat received" )
2. Snapshot Messages
Initial state of all matching orders when subscription starts.
from polymarket.v1 import enums_pb2
if response.HasField( 'snapshot' ):
snapshot = response.snapshot
print ( f "Snapshot received: { len (snapshot.orders) } orders" )
for order in snapshot.orders:
print ( f " Order ID: { order.id } " )
print ( f " Symbol: { order.symbol } " )
print ( f " Side: { enums_pb2.Side.Name(order.side) } " )
print ( f " State: { enums_pb2.OrderState.Name(order.state) } " )
3. Update Messages
Real-time order updates and executions.
if response.HasField( 'update' ):
update = response.update
# Process executions
for execution in update.executions:
print ( f "Execution: { enums_pb2.ExecutionType.Name(execution.type) } " )
# Process cancel rejects
if update.HasField( 'cancel_reject' ):
print ( f "Cancel rejected: { enums_pb2.CxlRejReason.Name(update.cancel_reject.reject_reason) } " )
Order Message Structure
Core Order Fields
Field Type Description idstrExchange-assigned order ID clord_idstrClient-assigned order ID (from your order request) symbolstrTrading symbol sideSideSIDE_BUY or SIDE_SELLtypeOrderTypeOrder type (LIMIT, MARKET_TO_LIMIT, etc.) stateOrderStateCurrent order state accountstrTrading account order_qtyintOriginal order quantity priceintOrder price (Γ· price_scale for decimal)cum_qtyintCumulative filled quantity leaves_qtyintRemaining unfilled quantity avg_pxintAverage fill price (Γ· price_scale)insert_timeTimestampWhen order was accepted create_timeTimestampWhen order was created
Price Fields:
price, avg_px, stop_price are int64 values
Divide by the instrumentβs price_scale to get decimal prices
price_scale is available from instrument metadata via the RefDataAPI
price = order.price / price_scale
print ( f "Price: $ { price :.4f} " )
Order States
State Value Description ORDER_STATE_NEW0 Order accepted, resting in book ORDER_STATE_PARTIALLY_FILLED1 Order partially executed ORDER_STATE_FILLED2 Order completely filled ORDER_STATE_CANCELED3 Order canceled (leaves_qty = 0) ORDER_STATE_REPLACED4 Order modified/replaced ORDER_STATE_REJECTED5 Order rejected by exchange ORDER_STATE_EXPIRED6 Order expired (e.g., Day order at end of day) ORDER_STATE_PENDING_NEW7 Order pending acceptance ORDER_STATE_PENDING_REPLACE8 Replace request pending ORDER_STATE_PENDING_CANCEL9 Cancel request pending
from polymarket.v1 import enums_pb2
# Get state name
state_name = enums_pb2.OrderState.Name(order.state)
print ( f "State: { state_name } " )
Order Sides
Side Value SIDE_BUY1 SIDE_SELL2
side_name = enums_pb2.Side.Name(order.side)
print ( f "Side: { side_name } " )
Order Types
Type Value Description ORDER_TYPE_LIMIT2 Limit order with specified price ORDER_TYPE_MARKET_TO_LIMIT1 Market order that converts to limit ORDER_TYPE_STOP3 Stop order ORDER_TYPE_STOP_LIMIT4 Stop-limit order
Execution Message Structure
Executions represent order lifecycle events (new, fill, cancel, reject).
Execution Fields
Field Type Description idstrUnique execution ID typeExecutionTypeType of execution event orderOrderCurrent order state after this execution last_sharesintQuantity filled in this execution (for fills) last_pxintPrice of this fill (Γ· price_scale)trade_idstrTrade ID (for fills) aggressorboolTrue if you were the aggressor in the trade transact_timeTimestampWhen execution occurred textstrFree-form text (e.g., reject reason) order_reject_reasonOrdRejectReasonRejection reason (if rejected)
Execution Types
Type Value Description EXECUTION_TYPE_NEW0 Order accepted (confirmed) EXECUTION_TYPE_PARTIAL_FILL1 Partial fill occurred EXECUTION_TYPE_FILL2 Complete fill (order fully executed)EXECUTION_TYPE_CANCELED3 Order canceled EXECUTION_TYPE_REPLACE4 Order modified EXECUTION_TYPE_REJECTED5 Order rejected EXECUTION_TYPE_EXPIRED6 Order expired EXECUTION_TYPE_DONE_FOR_DAY7 Order done for day
Order Reject Reasons
Reason Value Description ORD_REJECT_REASON_EXCHANGE_OPTION0 Exchange option ORD_REJECT_REASON_UNKNOWN_SYMBOL1 Symbol not found ORD_REJECT_REASON_EXCHANGE_CLOSED2 Market is closed ORD_REJECT_REASON_INCORRECT_QUANTITY3 Invalid quantity ORD_REJECT_REASON_INVALID_PRICE_INCREMENT4 Invalid price increment ORD_REJECT_REASON_INCORRECT_ORDER_TYPE5 Incorrect order type ORD_REJECT_REASON_PRICE_OUT_OF_BOUNDS6 Price out of bounds ORD_REJECT_REASON_NO_LIQUIDITY7 No liquidity available
Complete Example (from order_stream.py)
This example matches the implementation from the Python examples repository:
import grpc
import requests
from datetime import datetime, timedelta
from typing import Optional
from polymarket.v1 import trading_pb2
from polymarket.v1 import trading_pb2_grpc
from polymarket.v1 import enums_pb2
class PolymarketOrderStreamer :
def __init__ ( self , base_url : str = "https://rest.preprod.polymarketexchange.com" ,
grpc_server : str = "grpc-api.preprod.polymarketexchange.com:443" ):
self .base_url = base_url
self .grpc_server = grpc_server
self .access_token: Optional[ str ] = None
self .refresh_token: Optional[ str ] = None
self .access_expiration: Optional[datetime] = None
self .session_id: Optional[ str ] = None
self .price_scale: int = 1000 # Get from instrument metadata
def login ( self , auth0_domain : str , client_id : str , private_key_path : str , audience : str ) -> dict :
"""Authenticate using Private Key JWT and store the access token."""
import jwt
import uuid
from cryptography.hazmat.primitives import serialization
# Load private key
with open (private_key_path, 'rb' ) as f:
private_key = serialization.load_pem_private_key(f.read(), password = None )
# Create JWT assertion
now = int (datetime.now().timestamp())
claims = {
"iss" : client_id,
"sub" : client_id,
"aud" : f "https:// { auth0_domain } /oauth/token" ,
"iat" : now,
"exp" : now + 300 ,
"jti" : str (uuid.uuid4()),
}
assertion = jwt.encode(claims, private_key, algorithm = "RS256" )
# Exchange for access token
response = requests.post(
f "https:// { auth0_domain } /oauth/token" ,
json = {
"client_id" : client_id,
"client_assertion_type" : "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" ,
"client_assertion" : assertion,
"audience" : audience,
"grant_type" : "client_credentials"
}
)
response.raise_for_status()
token_data = response.json()
self .access_token = token_data[ "access_token" ]
# Set expiration with 30-second buffer (tokens expire in 180 seconds)
expires_in = token_data.get( "expires_in" , 180 )
self .access_expiration = datetime.now() + timedelta( seconds = expires_in - 30 )
return token_data
def stream_orders ( self , symbols : list = None , accounts : list = None , snapshot_only : bool = False ):
"""Stream order updates using gRPC."""
if not self .access_token:
raise ValueError ( "Not authenticated. Please login first." )
# Create credentials
credentials = grpc.ssl_channel_credentials()
# Create channel
channel = grpc.secure_channel( self .grpc_server, credentials)
# Create stub
stub = trading_pb2_grpc.OrderEntryAPIStub(channel)
# Create request
request = trading_pb2.CreateOrderSubscriptionRequest(
symbols = symbols or [],
accounts = accounts or [],
snapshot_only = snapshot_only
)
# Set up metadata with authorization
metadata = [
( 'authorization' , f 'Bearer { self .access_token } ' )
]
try :
print ( f "Starting order stream" )
print ( f "Symbols: { symbols or 'ALL' } " )
print ( f "Accounts: { accounts or 'ALL' } " )
print ( f "Snapshot only: { snapshot_only } " )
print ( "-" * 60 )
# Start streaming
response_stream = stub.CreateOrderSubscription(request, metadata = metadata)
for response in response_stream:
self ._process_order_response(response)
except grpc.RpcError as e:
print ( f "gRPC error: { e.code() } - { e.details() } " )
raise
except KeyboardInterrupt :
print ( " \n Stream interrupted by user" )
finally :
channel.close()
def _process_order_response ( self , response ):
"""Process and display order response."""
# Capture session ID on first message
if response.session_id and not self .session_id:
self .session_id = response.session_id
print ( f " \n [ { datetime.now().strftime( '%H:%M:%S' ) } ] Session established" )
print ( f " Session ID: { self .session_id } " )
print ( "-" * 60 )
if response.HasField( 'heartbeat' ):
print ( f "[ { datetime.now().strftime( '%H:%M:%S' ) } ] Heartbeat received" )
elif response.HasField( 'snapshot' ):
snapshot = response.snapshot
print ( f " \n [ { datetime.now().strftime( '%H:%M:%S' ) } ] Order Snapshot" )
print ( f " Total orders: { len (snapshot.orders) } " )
for order in snapshot.orders:
self ._display_order(order)
print ( "-" * 60 )
elif response.HasField( 'update' ):
update = response.update
print ( f " \n [ { datetime.now().strftime( '%H:%M:%S' ) } ] Order Update" )
# Display executions
if update.executions:
print ( f " Executions: { len (update.executions) } " )
for execution in update.executions:
self ._display_execution(execution)
# Display cancel rejects
if update.HasField( 'cancel_reject' ):
self ._display_cancel_reject(update.cancel_reject)
print ( "-" * 60 )
def _display_order ( self , order ):
"""Display order details."""
print ( f " Order ID: { order.id } " )
print ( f " Client Order ID: { order.clord_id } " )
print ( f " Symbol: { order.symbol } " )
print ( f " Side: { enums_pb2.Side.Name(order.side) } " )
print ( f " Type: { enums_pb2.OrderType.Name(order.type) } " )
print ( f " State: { enums_pb2.OrderState.Name(order.state) } " )
if order.price > 0 :
price = order.price / self .price_scale
print ( f " Price: $ { price :.4f} " )
print ( f " Order Qty: { order.order_qty } " )
print ( f " Filled Qty: { order.cum_qty } " )
print ( f " Remaining Qty: { order.leaves_qty } " )
if order.avg_px > 0 :
avg_px = order.avg_px / self .price_scale
print ( f " Avg Price: $ { avg_px :.4f} " )
if order.account:
print ( f " Account: { order.account } " )
print ()
def _display_execution ( self , execution ):
"""Display execution details."""
print ( f " Execution ID: { execution.id } " )
print ( f " Type: { enums_pb2.ExecutionType.Name(execution.type) } " )
if execution.HasField( 'order' ):
order = execution.order
print ( f " Order ID: { order.id } " )
print ( f " Symbol: { order.symbol } " )
print ( f " Side: { enums_pb2.Side.Name(order.side) } " )
print ( f " State: { enums_pb2.OrderState.Name(order.state) } " )
if execution.last_shares > 0 :
print ( f " Last Shares: { execution.last_shares } " )
if execution.last_px > 0 :
last_px = execution.last_px / self .price_scale
print ( f " Last Price: $ { last_px :.4f} " )
if execution.trade_id:
print ( f " Trade ID: { execution.trade_id } " )
if execution.text:
print ( f " Text: { execution.text } " )
if execution.order_reject_reason != enums_pb2. ORD_REJECT_REASON_EXCHANGE_OPTION :
print ( f " Reject Reason: { enums_pb2.OrdRejectReason.Name(execution.order_reject_reason) } " )
print ()
def _display_cancel_reject ( self , cancel_reject ):
"""Display cancel reject details."""
print ( f " Cancel Reject:" )
print ( f " Order ID: { cancel_reject.order_id } " )
print ( f " Client Order ID: { cancel_reject.clord_id } " )
print ( f " Reject Reason: { enums_pb2.CxlRejReason.Name(cancel_reject.reject_reason) } " )
if cancel_reject.text:
print ( f " Text: { cancel_reject.text } " )
print ()
# Usage
if __name__ == "__main__" :
streamer = PolymarketOrderStreamer()
# Login using Private Key JWT
streamer.login(
auth0_domain = "pmx-preprod.us.auth0.com" ,
client_id = "your_client_id" ,
private_key_path = "private_key.pem" ,
audience = "https://api.preprod.polymarketexchange.com"
)
# Stream orders
streamer.stream_orders(
symbols = [ "tec-nfl-sbw-2026-02-08-kc" ],
accounts = []
)
Sample Output
Starting order stream
Symbols: ['tec-nfl-sbw-2026-02-08-kc']
Accounts: ALL
Snapshot only: False
------------------------------------------------------------
[14:30:15] Session established
Session ID: session_abc123
------------------------------------------------------------
[14:30:15] Order Snapshot
Total orders: 3
Order ID: order_12345
Client Order ID: clord_abc
Symbol: tec-nfl-sbw-2026-02-08-kc
Side: SIDE_BUY
Type: ORDER_TYPE_LIMIT
State: ORDER_STATE_NEW
Price: $0.525
Order Qty: 1000
Filled Qty: 0
Remaining Qty: 1000
------------------------------------------------------------
[14:30:45] Heartbeat received
[14:31:02] Order Update
Executions: 1
Execution ID: exec_67890
Type: EXECUTION_TYPE_PARTIAL_FILL
Order ID: order_12345
Symbol: tec-nfl-sbw-2026-02-08-kc
Side: SIDE_BUY
State: ORDER_STATE_PARTIALLY_FILLED
Last Shares: 300
Last Price: $0.525
Trade ID: trade_xyz
------------------------------------------------------------
Order Lifecycle
Typical Order Flow
1. NEW -> Order accepted, resting in book
2. PARTIAL_FILL -> First partial fill
3. PARTIAL_FILL -> Additional partial fills (if any)
4. FILL -> Final fill, order complete
Cancel Flow
1. NEW -> Order resting
2. PENDING_CANCEL -> Cancel request received
3. CANCELED -> Cancel confirmed
Reject Flow
1. REJECTED -> Order rejected immediately
Next Steps
Proto Reference Detailed message and field reference
Error Handling Handle errors and reconnections
Market Data Stream Stream real-time market data