Skip to main content

DropCopy & Trade Capture Streaming

Subscribe to real-time execution reports, trade captures, instrument state changes, and position updates for your firm using gRPC streaming.
gRPC Only - DropCopy endpoints are gRPC streaming only. There are no REST equivalents.

Service Definition

Service: polymarket.v1.DropCopyAPI Type: Server-side streaming (all endpoints)
service DropCopyAPI {
    rpc CreateDropCopySubscription(CreateDropCopySubscriptionRequest)
        returns (stream CreateDropCopySubscriptionResponse);

    rpc CreateTradeCaptureReportSubscription(CreateTradeCaptureReportSubscriptionRequest)
        returns (stream CreateTradeCaptureReportSubscriptionResponse);

    rpc CreateInstrumentStateChangeSubscription(CreateInstrumentStateChangeSubscriptionRequest)
        returns (stream CreateInstrumentStateChangeSubscriptionResponse);

    rpc CreatePositionChangeSubscription(CreatePositionChangeSubscriptionRequest)
        returns (stream CreatePositionChangeSubscriptionResponse);
}

Available Streams

StreamDescriptionUse Case
DropCopyExecution reports (fills, cancels)Real-time order execution monitoring
Trade Capture ReportCompleted tradesTrade reconciliation, compliance
Instrument State ChangeMarket state updatesTrading halts, market open/close
Position ChangePosition updatesReal-time P&L, risk monitoring

1. DropCopy Subscription

Stream execution reports as they occur for your firm.

Request Parameters

FieldTypeRequiredDescription
resume_tokenbytesNoResume from previous position
resume_timeTimestampNoResume from specific time
symbolslist[str]NoFilter by symbols. Empty = all symbols
firmslist[str]NoFilter by firms. Empty = authenticated firm

Response Fields

FieldTypeDescription
resume_tokenbytesStore for reconnection
executionslist[Execution]Execution reports in this batch

Example

import grpc
from datetime import datetime
from polymarket.v1 import dropcopy_pb2
from polymarket.v1 import dropcopy_pb2_grpc
from polymarket.v1 import enums_pb2


class DropCopyStreamer:
    def __init__(self, grpc_server: str = "grpc-api.preprod.polymarketexchange.com:443"):
        self.grpc_server = grpc_server
        self.access_token = None
        self.last_resume_token = None

    def stream_executions(self, symbols: list = None, resume_token: bytes = None):
        """Stream execution reports via DropCopy."""
        credentials = grpc.ssl_channel_credentials()
        channel = grpc.secure_channel(self.grpc_server, credentials)
        stub = dropcopy_pb2_grpc.DropCopyAPIStub(channel)

        request = dropcopy_pb2.CreateDropCopySubscriptionRequest(
            symbols=symbols or []
        )
        if resume_token:
            request.resume_token = resume_token

        metadata = [('authorization', f'Bearer {self.access_token}')]

        try:
            print("Starting DropCopy stream...")
            print(f"Symbols: {symbols or 'ALL'}")
            print("-" * 60)

            for response in stub.CreateDropCopySubscription(request, metadata=metadata):
                self.last_resume_token = response.resume_token

                for execution in response.executions:
                    self._display_execution(execution)

        except grpc.RpcError as e:
            print(f"gRPC error: {e.code()} - {e.details()}")
        finally:
            channel.close()

    def _display_execution(self, execution):
        """Display execution details."""
        print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Execution")
        print(f"  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"  Fill Qty: {execution.last_shares}")
        if execution.last_px > 0:
            print(f"  Fill Price: {execution.last_px}")
        if execution.trade_id:
            print(f"  Trade ID: {execution.trade_id}")

        print("-" * 60)


# Usage
if __name__ == "__main__":
    streamer = DropCopyStreamer()
    # streamer.access_token = "your_token"
    streamer.stream_executions()

Sample Output

Starting DropCopy stream...
Symbols: ALL
------------------------------------------------------------

[14:30:15] Execution
  ID: exec_abc123
  Type: EXECUTION_TYPE_FILL
  Order ID: order_xyz789
  Symbol: tec-nfl-sbw-2026-02-08-kc
  Side: SIDE_BUY
  State: ORDER_STATE_FILLED
  Fill Qty: 100
  Fill Price: 525
  Trade ID: trade_def456
------------------------------------------------------------

[14:30:16] Execution
  ID: exec_ghi789
  Type: EXECUTION_TYPE_CANCELED
  Order ID: order_abc123
  Symbol: tec-nfl-sbw-2026-02-08-kc
  Side: SIDE_SELL
  State: ORDER_STATE_CANCELED
------------------------------------------------------------

2. Trade Capture Report Subscription

Stream completed trades for reconciliation and compliance.

Request Parameters

FieldTypeRequiredDescription
resume_tokenbytesNoResume from previous position
resume_timeTimestampNoResume from specific time
symbolslist[str]NoFilter by symbols
firmslist[str]NoFilter by firms

Response Fields

FieldTypeDescription
resume_tokenbytesStore for reconnection
trade_capture_reportslist[Trade]Trade records

Example

def stream_trade_captures(self, symbols: list = None):
    """Stream trade capture reports."""
    credentials = grpc.ssl_channel_credentials()
    channel = grpc.secure_channel(self.grpc_server, credentials)
    stub = dropcopy_pb2_grpc.DropCopyAPIStub(channel)

    request = dropcopy_pb2.CreateTradeCaptureReportSubscriptionRequest(
        symbols=symbols or []
    )

    metadata = [('authorization', f'Bearer {self.access_token}')]

    for response in stub.CreateTradeCaptureReportSubscription(request, metadata=metadata):
        for trade in response.trade_capture_reports:
            print(f"Trade ID: {trade.id}")
            print(f"  Aggressor: {trade.aggressor.order.id if trade.aggressor else 'N/A'}")
            print(f"  Passive: {trade.passive.order.id if trade.passive else 'N/A'}")
            print(f"  State: {trade.state}")

Trade Structure

Each trade contains two executions:
FieldDescription
idUnique trade ID
aggressorExecution for the incoming (taker) order
passiveExecution for the resting (maker) order
trade_typeType of trade (REGULAR, CROSS, etc.)
stateTrade state (NEW, CLEARED, etc.)

3. Instrument State Change Subscription

Stream market state changes (halts, opens, closes).
No Participant ID RequiredThis endpoint only requires Auth0 JWT authentication with read:instruments scope. You do not need to provide the x-participant-id header or complete KYC onboarding to access instrument state changes.

Request Parameters

FieldTypeRequiredDescription
resume_tokenbytesNoResume from previous position
resume_timeTimestampNoResume from specific time
symbolslist[str]NoFilter by symbols

Response Fields

FieldTypeDescription
resume_tokenbytesStore for reconnection
instrumentslist[Instrument]Updated instrument states

Instrument States

StateDescription
INSTRUMENT_STATE_OPENMarket is open for trading
INSTRUMENT_STATE_CLOSEDMarket is closed
INSTRUMENT_STATE_HALTEDTrading is halted
INSTRUMENT_STATE_PRE_OPENPre-market period
INSTRUMENT_STATE_POST_CLOSEPost-market period

Example

def stream_instrument_states(self, symbols: list = None):
    """Stream instrument state changes."""
    credentials = grpc.ssl_channel_credentials()
    channel = grpc.secure_channel(self.grpc_server, credentials)
    stub = dropcopy_pb2_grpc.DropCopyAPIStub(channel)

    request = dropcopy_pb2.CreateInstrumentStateChangeSubscriptionRequest(
        symbols=symbols or []
    )

    metadata = [('authorization', f'Bearer {self.access_token}')]

    for response in stub.CreateInstrumentStateChangeSubscription(request, metadata=metadata):
        for instrument in response.instruments:
            print(f"Symbol: {instrument.symbol}")
            print(f"  State: {instrument.state}")
            print(f"  Name: {instrument.name}")

4. Position Change Subscription

Stream real-time position updates.

Request Parameters

FieldTypeRequiredDescription
resume_tokenbytesNoResume from previous position
resume_timeTimestampNoResume from specific time
symbolslist[str]NoFilter by symbols
firmslist[str]NoFilter by firms

Response Fields

FieldTypeDescription
resume_tokenbytesStore for reconnection
position_changeslist[PositionChange]Position updates

PositionChange Structure

FieldTypeDescription
positionPositionCurrent position state
change_timeTimestampWhen change occurred

Example

def stream_position_changes(self, symbols: list = None):
    """Stream position changes."""
    credentials = grpc.ssl_channel_credentials()
    channel = grpc.secure_channel(self.grpc_server, credentials)
    stub = dropcopy_pb2_grpc.DropCopyAPIStub(channel)

    request = dropcopy_pb2.CreatePositionChangeSubscriptionRequest(
        symbols=symbols or []
    )

    metadata = [('authorization', f'Bearer {self.access_token}')]

    for response in stub.CreatePositionChangeSubscription(request, metadata=metadata):
        for change in response.position_changes:
            pos = change.position
            print(f"Position Update: {pos.symbol}")
            print(f"  Account: {pos.account}")
            print(f"  Net Qty: {pos.net_position}")
            print(f"  Avg Price: {pos.average_price}")
            print(f"  Change Time: {change.change_time}")

Reconnection Handling

All DropCopy streams support resume tokens for seamless reconnection:
# Store resume_token from each response
last_token = response.resume_token

# On reconnect, pass the token
request = dropcopy_pb2.CreateDropCopySubscriptionRequest(
    resume_token=last_token
)
Resume Token ExpiryResume tokens may expire after extended disconnection periods. If resumption fails, start a fresh subscription and reconcile with the Report API for any missed data.

Comparing DropCopy vs Order Stream

FeatureDropCopyOrder Stream
ScopeFirm-wide executionsUser’s orders only
Use CaseBack-office, complianceTrading UI, order management
DataExecutions, tradesOrders, executions
AuthenticationFirm-level tokenUser token
When to Use DropCopyUse DropCopy when you need:
  • Firm-wide visibility across all users
  • Trade capture reports for reconciliation
  • Instrument state change notifications
  • Real-time position monitoring across accounts

Next Steps