This guide will help you set up gRPC streaming with Python and connect to your first data stream in minutes.
Prerequisites
Before you begin, ensure you have:
- API Credentials: Client ID from authentication setup
- Python 3.7+: Python development environment
- Network Access: Ability to connect to
grpc-api.preprod.polymarketexchange.com:443
Step 1: Install Python gRPC Libraries
pip install grpcio grpcio-tools protobuf requests
Required packages:
grpcio: gRPC runtime library
grpcio-tools: Tools for generating Python code from proto files
protobuf: Protocol buffer runtime
requests/httpx: For REST API authentication
Step 2: Obtain Protocol Buffer Definitions
Proto files define the gRPC service interfaces and message structures. You can obtain them in two ways:
Option A: Request from Support
Contact onboarding@qcex.com to receive the complete proto file package.
Option B: Use Server Reflection
Server reflection allows you to discover service definitions at runtime without proto files. This is useful for dynamic clients and debugging.
# Install reflection client
pip install grpcio-reflection
# Use grpcurl to explore services (TLS connection - no -plaintext flag)
grpcurl grpc-api.preprod.polymarketexchange.com:443 list
# Describe a service
grpcurl grpc-api.preprod.polymarketexchange.com:443 describe polymarket.v1.MarketDataSubscriptionAPI
Step 3: Generate Python Client Code
Once you have the proto files, generate Python client code:
python -m grpc_tools.protoc \
--python_out=. \
--grpc_python_out=. \
--proto_path=protos \
protos/polymarket/v1/marketdatasubscription.proto \
protos/polymarket/v1/trading.proto \
protos/polymarket/v1/enums.proto \
protos/polymarket/v1/types.proto \
protos/polymarket/v1/refdata.proto
This generates:
*_pb2.py files: Message definitions
*_pb2_grpc.py files: Service stubs
Step 4: Authenticate
CRITICAL: Tokens must be refreshed every 3 minutes.Access tokens have a short expiration. Your application MUST implement automatic token refresh before expiration to maintain uninterrupted streaming connections.
Auth Domains
| Environment | Auth Domain |
|---|
| Production | pmx-prod.us.auth0.com |
| Preprod | pmx-preprod.us.auth0.com |
Obtain a JWT token using Private Key JWT authentication:
import jwt
import uuid
import time
import requests
from cryptography.hazmat.primitives import serialization
# Load your private key
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
# Create signed JWT assertion
now = int(time.time())
claims = {
"iss": "YOUR_CLIENT_ID",
"sub": "YOUR_CLIENT_ID",
"aud": "https://pmx-preprod.us.auth0.com/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(
"https://pmx-preprod.us.auth0.com/oauth/token",
json={
"client_id": "YOUR_CLIENT_ID",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": assertion,
"audience": "YOUR_API_AUDIENCE",
"grant_type": "client_credentials"
}
)
access_token = response.json()["access_token"]
The response contains your access_token:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIs...",
"token_type": "Bearer",
"expires_in": 180
}
The expires_in is 180 seconds (3 minutes). Implement automatic token refresh before expiration.
Step 5: Connect to Market Data Stream
Create your first streaming connection:
import grpc
from datetime import datetime
from polymarket.v1 import marketdatasubscription_pb2
from polymarket.v1 import marketdatasubscription_pb2_grpc
# Create secure channel
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel('grpc-api.preprod.polymarketexchange.com:443', credentials)
# Create stub
stub = marketdatasubscription_pb2_grpc.MarketDataSubscriptionAPIStub(channel)
# Create request
request = marketdatasubscription_pb2.CreateMarketDataSubscriptionRequest(
symbols=["tec-nfl-sbw-2026-02-08-kc"],
unaggregated=False,
depth=10,
snapshot_only=False
)
# Set up metadata with authorization
metadata = [
('authorization', f'Bearer {access_token}')
]
# Start streaming
print("Starting market data stream...")
response_stream = stub.CreateMarketDataSubscription(request, metadata=metadata)
for response in response_stream:
if response.HasField('heartbeat'):
timestamp = datetime.now().strftime('%H:%M:%S')
print(f"[{timestamp}] Heartbeat received")
elif response.HasField('update'):
update = response.update
timestamp = datetime.now().strftime('%H:%M:%S')
print(f"\n[{timestamp}] Market Update for {update.symbol}")
print(f" Bids: {len(update.bids)}")
print(f" Offers: {len(update.offers)}")
# Get price_scale from instrument metadata (via list_instruments API)
# You must implement this function to fetch from your instrument cache
price_scale = get_instrument_price_scale(update.symbol) # implement this
if update.bids:
top_bid_px = update.bids[0].px / price_scale
print(f" Top Bid: ${top_bid_px:.4f} x {update.bids[0].qty}")
if update.offers:
top_offer_px = update.offers[0].px / price_scale
print(f" Top Offer: ${top_offer_px:.4f} x {update.offers[0].qty}")
Price Representation: All prices are int64 values. Divide by the instrument’s price_scale to get the decimal price.price_scale varies by instrument. Get it from:
- Instrument metadata via
list_instruments or get_instrument_metadata
- The
price_scale field in order responses
price_scale = get_instrument_price_scale(symbol)
decimal_price = raw_price / price_scale
Common Setup Issues
Connection Refused
- Cause: Firewall blocking outbound gRPC connections
- Solution: Ensure port 443 is open for outbound connections
Authentication Failed
- Cause: Invalid or expired access token
- Solution: Verify JWT, refresh token if expired (tokens expire every 3 minutes)
Import Errors
- Cause: Generated proto files not in Python path
- Solution: Ensure proto files are generated in the correct directory, or add to
PYTHONPATH:
export PYTHONPATH="${PYTHONPATH}:."
Module Not Found: polymarket
- Cause: Proto files not generated or in wrong location
- Solution: Re-run the
protoc command from step 3, ensure you’re in the correct directory
Next Steps
Need Help?
If you encounter issues during setup:
- Check the Error Handling Guide
- Review the Market Data Stream or Order Stream pages for complete implementations
- Contact onboarding@qcex.com for assistance