Real-Time Streams

The Streaming API provides real-time updates using Server-Sent Events (SSE). This lightweight protocol allows you to receive push notifications for order updates without the overhead of WebSockets or constant polling.

Overview

Server-Sent Events (SSE) is a standard allowing servers to push data to web clients over HTTP. Our implementation provides:

  • Real-time order status updates
  • Automatic reconnection handling
  • Minimal bandwidth usage
  • Simple client implementation

Order Stream

Subscribe to real-time order updates for your entity. Updates are pushed immediately when order status changes occur.

Endpoint

GET https://api.paperinvest.io/v1/orders/stream

Query Parameters

ParameterTypeDescriptionRequired
accountId
StringFilter updates for specific account (optional)No

Connection Examples

JavaScript

SSE
// Browser/Node.js example
const eventSource = new EventSource(
  'https://api.paperinvest.io/v1/orders/stream?accountId=550e8400-e29b-41d4-a716-446655440000',
  {
    headers: {
      'Authorization': 'Bearer YOUR_JWT_TOKEN'
    }
  }
);


// Handle order updates
eventSource.addEventListener('order-update', (event) => {
  const order = JSON.parse(event.data);
  console.log('Order update:', order);
  updateOrderUI(order);
});


// Handle heartbeat
eventSource.addEventListener('heartbeat', (event) => {
  console.log('Connection alive');
});


// Handle errors
eventSource.addEventListener('error', (event) => {
  if (event.data) {
    const error = JSON.parse(event.data);
    console.error('Stream error:', error.message);
  }
});


// Handle connection errors
eventSource.onerror = (error) => {
  console.error('Connection error:', error);
  // EventSource will automatically reconnect
};


// Clean up when done
// eventSource.close();

Event Types

The stream emits different event types to handle various scenarios:

Event TypeDescriptionFrequency
order-update
Order status change or fill updateOn order changes
heartbeat
Keep-alive signalEvery 30 seconds
error
Stream error notificationOn errors

Order Update Payload

Each order-update event contains essential order information. For complete order details, use the Get Order endpoint.

Order Update Event

{
  "orderId": "123e4567-e89b-12d3-a456-426614174000",
  "accountId": "550e8400-e29b-41d4-a716-446655440000",
  "portfolioId": "8b72f1a5-c2e4-48d9-b5a3-1c7e5d3f9e8a",
  "brokerId": "default",
  "symbol": "AAPL",
  "side": "BUY",
  "status": "FILLED",
  "quantity": 100,
  "filledQuantity": 100,
  "outstandingQuantity": 0,
  "limitPrice": 150.00,
  "createdAt": "2025-01-15T10:30:00Z"
}

Event Stream Format

SSE events follow a specific text-based format. Each event consists of fields separated by newlines:

FieldDescriptionExample
id
Unique identifier for the event (optional)123e4567-e89b-12d3-a456-426614174000
event
Event type nameorder-update, heartbeat, error
data
JSON payload containing event details{"orderId":"...","status":"FILLED"...}
retry
Reconnection time in milliseconds (optional)5000

Example Event Stream

SSE Event Stream

id: 123e4567-e89b-12d3-a456-426614174000
event: order-update
data: {"orderId":"123e4567-e89b-12d3-a456-426614174000","symbol":"AAPL","status":"FILLED","filledQuantity":100,"outstandingQuantity":0}


event: heartbeat
data: {"timestamp":"2025-01-15T10:30:30Z"}


id: 456e7890-e89b-12d3-a456-426614174111
event: order-update
data: {"orderId":"456e7890-e89b-12d3-a456-426614174111","symbol":"MSFT","status":"ACTIVE","filledQuantity":0,"outstandingQuantity":50}

Connection Management

Authentication

The streaming endpoint uses the same JWT token authentication as other API endpoints. Include your bearer token in the Authorization header.

Reconnection

SSE connections automatically reconnect on network interruptions. The EventSource API handles this transparently, using the last event ID to resume where it left off.

Connection Details

  • Heartbeat interval: 30 seconds
  • Automatic deduplication: Updates within 100ms are deduplicated

Best Practices

  1. Implement Error Handling: Always handle connection errors and stream errors separately
  2. Monitor Heartbeats: Use heartbeats to detect stale connections
  3. Efficient Updates: Process updates efficiently to avoid blocking the event loop
  4. Resource Cleanup: Close connections when no longer needed

Connection Management

class OrderStreamManager {
  constructor(token, accountId) {
    this.token = token;
    this.accountId = accountId;
    this.eventSource = null;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
  }


  connect() {
    const url = new URL('https://api.paperinvest.io/v1/orders/stream');
    if (this.accountId) {
      url.searchParams.append('accountId', this.accountId);
    }


    this.eventSource = new EventSource(url, {
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });


    this.eventSource.addEventListener('order-update', (event) => {
      this.handleOrderUpdate(JSON.parse(event.data));
    });


    this.eventSource.addEventListener('heartbeat', () => {
      this.lastHeartbeat = Date.now();
    });


    this.eventSource.onerror = (error) => {
      console.error('SSE Error:', error);
      this.handleReconnect();
    };


    // Monitor heartbeats
    this.heartbeatInterval = setInterval(() => {
      if (Date.now() - this.lastHeartbeat > 60000) {
        console.warn('No heartbeat for 60s, reconnecting...');
        this.reconnect();
      }
    }, 10000);
  }


  handleOrderUpdate(order) {
    // Process order update
    console.log('Order update:', order);
  }


  handleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      setTimeout(() => this.connect(), 1000 * this.reconnectAttempts);
    } else {
      console.error('Max reconnection attempts reached');
    }
  }


  disconnect() {
    if (this.eventSource) {
      this.eventSource.close();
      clearInterval(this.heartbeatInterval);
    }
  }
}


// Usage
const streamManager = new OrderStreamManager(
  'YOUR_JWT_TOKEN',
  '550e8400-e29b-41d4-a716-446655440000'
);
streamManager.connect();

See Also