Implementing SSE and WebSocket crypto price feeds with DexPaprika: choosing the right protocol
Learn to implement Server-Sent Events and WebSocket streaming with DexPaprika. Compare protocols, handle production failures, choose the right approach, and build hybrid implementations with complete code examples.

Implementing SSE and WebSocket crypto price feeds with DexPaprika: choosing the right protocol
Real-time crypto prices require persistent connections. Server-Sent Events (SSE) and WebSocket both deliver streaming data, but they work differently and excel in different scenarios.
This tutorial will teach you to implement both protocols with DexPaprika's streaming API. You will build production-ready price feeds using SSE and WebSocket, understand their technical differences, and learn when to choose each protocol for your application.
What you will learn
In this tutorial, you will:
- Implement SSE price streaming with DexPaprika (no API key required)
- Build WebSocket price feeds with reconnection logic
- Compare bandwidth overhead and performance characteristics
- Handle corporate firewall compatibility issues
- Choose the right protocol based on your requirements
- Build hybrid implementations that use both protocols
Prerequisites
To follow this tutorial, you will need:
- Node.js 18+ installed
- Basic understanding of JavaScript async/await patterns
- Familiarity with HTTP and persistent connections
- No API key required (DexPaprika streaming is completely free)
Quick start: SSE streaming in 30 seconds
Get live WETH prices with Server-Sent Events:
// SSE client - simple and production-ready
import { EventSource } from 'eventsource';
const wethStream = new EventSource(
'https://streaming.dexpaprika.com/stream?method=t_p&chain=ethereum&address=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
);
wethStream.onmessage = (event) => {
const price = JSON.parse(event.data);
console.log(`WETH: $${price.price} (${price.price_change_24h}%)`);
};
wethStream.onerror = (error) => {
console.error('Stream error:', error);
// Browser automatically reconnects
};This basic SSE implementation connects to DexPaprika's WETH price stream. The endpoint provides real-time price updates from Ethereum DEXs with automatic browser reconnection handling.
Understanding protocol differences
SSE and WebSocket serve different purposes. Understanding these differences will help you choose the right protocol for your application.
SSE: HTTP-based streaming
Server-Sent Events use standard HTTP. The server keeps the HTTP response open and pushes text events. The browser handles reconnection automatically. When the connection drops, the browser reconnects and sends a Last-Event-ID header for event replay.
WebSocket: binary protocol
WebSocket creates a bidirectional TCP socket after an HTTP upgrade handshake. Messages flow both ways with 2-6 bytes of overhead per frame. The protocol supports both text and binary data.
After the initial HTTP handshake, WebSocket uses its own protocol. This provides efficiency but requires manual reconnection logic when connections drop.
Section 1: Building SSE price feeds with DexPaprika
You will implement a production-ready SSE client that handles multiple tokens, automatic reconnection, and error cases.
Basic SSE implementation
Start with a single-token price stream:
// Basic SSE price stream for WETH
import { EventSource } from 'eventsource';
class DexPaprikaSSEStream {
constructor(chain, address) {
this.chain = chain;
this.address = address;
this.url = `https://streaming.dexpaprika.com/stream?method=t_p&chain=${chain}&address=${address}`;
this.stream = null;
this.reconnectCount = 0;
}
connect() {
this.stream = new EventSource(this.url);
this.stream.onopen = () => {
console.log(`Connected to ${this.chain} token ${this.address}`);
this.reconnectCount = 0; // Reset on successful connection
};
this.stream.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.handlePriceUpdate(data);
} catch (error) {
console.error('Parse error:', error);
// Don't crash on malformed data
}
};
this.stream.onerror = (error) => {
console.error('SSE error:', error);
this.reconnectCount++;
// Browser reconnects automatically
// But bail if reconnection fails repeatedly
if (this.reconnectCount > 10) {
console.error('Max reconnects reached, closing stream');
this.stream.close();
this.fallbackToPolling();
}
};
}
handlePriceUpdate(data) {
console.log(`Price update: ${data.price} (${data.price_change_24h}%)`);
}
fallbackToPolling() {
console.warn('SSE failed, implementing polling fallback');
// Polling fallback implementation here
}
close() {
if (this.stream) {
this.stream.close();
}
}
}
// Usage
const wethFeed = new DexPaprikaSSEStream('ethereum', '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2');
wethFeed.connect();This implementation handles connection establishment, message parsing, and error cases. The browser's built-in reconnection mechanism handles network disruptions automatically.
Section 2: Building WebSocket price feeds
You will implement WebSocket streaming with manual reconnection logic, heartbeats, and error handling.
Basic WebSocket implementation
Implement a WebSocket client with exponential backoff reconnection:
// WebSocket price feed with reconnection
class DexPaprikaWebSocketStream {
constructor(endpoint, symbols) {
this.endpoint = endpoint;
this.symbols = symbols;
this.ws = null;
this.reconnectDelay = 1000; // Start at 1 second
this.pingInterval = null;
this.pongTimeout = null;
}
connect() {
this.ws = new WebSocket(this.endpoint);
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectDelay = 1000; // Reset delay on success
// Subscribe to price feeds
this.ws.send(JSON.stringify({
type: 'subscribe',
symbols: this.symbols
}));
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'pong') {
this.handlePong();
} else {
this.handlePriceUpdate(data);
}
} catch (error) {
console.error('Parse error:', error);
}
};
this.ws.onclose = (event) => {
console.log(`WebSocket closed: ${event.code} ${event.reason}`);
this.stopHeartbeat();
this.scheduleReconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
// onclose will fire next, handle reconnection there
};
}
startHeartbeat() {
// Send ping every 30 seconds to detect dead connections
this.pingInterval = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
// If no pong within 10 seconds, connection is dead
this.pongTimeout = setTimeout(() => {
console.error('Pong timeout, closing connection');
this.ws.close();
}, 10000);
}
}, 30000);
}
handlePong() {
clearTimeout(this.pongTimeout);
}
stopHeartbeat() {
clearInterval(this.pingInterval);
clearTimeout(this.pongTimeout);
}
scheduleReconnect() {
// Add random jitter to prevent thundering herd
const jitter = Math.random() * 1000;
setTimeout(() => {
console.log('Reconnecting...');
this.connect();
// Exponential backoff: 1s -> 2s -> 4s -> 8s -> 16s -> 30s max
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000);
}, this.reconnectDelay + jitter);
}
handlePriceUpdate(data) {
console.log(`Price update: ${data.price} (${data.symbol})`);
}
close() {
this.stopHeartbeat();
if (this.ws) {
this.ws.close();
}
}
}This WebSocket implementation handles reconnection with exponential backoff, implements heartbeat ping/pong to detect dead connections, and manages connection state transitions. Unlike SSE, all reconnection logic must be implemented manually.
Section 3: Handling production failures
Both protocols fail in production. You will implement solutions for common failure patterns.
SSE: Preventing nginx proxy buffering
Nginx buffers SSE responses by default, causing 30-second delays:
# nginx configuration for SSE streaming
location /stream {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Disable buffering - critical for SSE
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
chunked_transfer_encoding off;
proxy_set_header X-Accel-Buffering no;
}This configuration disables all buffering and sets a 1-hour read timeout. Without these settings, nginx queues messages until the buffer fills, then dumps them all at once.
Section 4: Choosing the right protocol
Protocol selection depends on your specific requirements. Use this decision framework to choose.
Choose SSE when
Enterprise/corporate customers: SSE works through corporate proxies. WebSocket gets blocked by 30% of enterprise firewalls.
Server-to-client only: SSE excels at unidirectional data flow. No bidirectional complexity.
Development speed matters: SSE requires less code. Browser handles reconnection automatically.
HTTP/2 available: Multiplexing eliminates connection limits. 100+ streams over one TCP connection.
Choose WebSocket when
Bidirectional communication needed: Trading platforms require client→server commands and server→client updates on the same connection.
Binary data: Order books, market depth, tick data benefit from binary encoding (60-70% bandwidth reduction).
Extreme scale: WebSocket scales to 100K+ concurrent connections per server more easily than SSE.
Mobile optimization: WebSocket with proper heartbeat tuning (30s intervals) conserves battery better than SSE's frequent reconnections.
Testing your implementation
Verify your implementation handles production scenarios correctly.
Test 1: Connection establishment
Verify both protocols connect successfully:
# Test SSE connection
curl -N -H "Accept: text/event-stream" \
"https://streaming.dexpaprika.com/stream?method=t_p&chain=ethereum&address=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
# Expected: Stream of price updates
# data: {"price": 2500.00, "price_change_24h": 2.5}
# ... (continuous updates)Troubleshooting
Common issues and solutions for both protocols.
SSE: Connection drops at exactly 60 seconds
Cause: Nginx proxy_read_timeout default is 60 seconds.
Solution: Configure nginx with proxy_read_timeout 3600s and proxy_buffering off.
WebSocket: Silent connection failures
Cause: Corporate proxies block WebSocket upgrades without error messages.
Solution: Implement SSE fallback. Test connection with timeout, fall back if timeout expires.
Next steps
You have successfully implemented SSE and WebSocket price streaming with DexPaprika. Your implementations handle reconnection, heartbeats, and error cases for production use.
To extend these implementations:
Add database persistence: Store price history to PostgreSQL or TimescaleDB for historical analysis and charting.
Implement connection pooling: For applications with many users, pool connections server-side to reduce load on DexPaprika's endpoints.
Build hybrid architecture: Use SSE for public price displays (simple, firewall-friendly) and WebSocket for authenticated trading (bidirectional, low latency).
Related tutorials
- Server-Sent Events (SSE) explained for crypto apps - Deep dive into SSE implementation patterns and browser APIs
- Polling vs streaming for price updates - When to use polling, streaming, or hybrid approaches
- How live price feeds fail in production - Common production failures and prevention strategies
About DexPaprika
DexPaprika provides completely free cryptocurrency price streaming with no API keys, no credit cards, no rate limits, and no KYC requirements. The streaming endpoint covers 1,300+ cryptocurrencies across 28+ blockchains including Ethereum, BSC, Polygon, Arbitrum, Optimism, Avalanche, and more.
Related articles
Coinpaprika education
Discover practical guides, definitions, and deep dives to grow your crypto knowledge.
Cryptocurrencies are highly volatile and involve significant risk. You may lose part or all of your investment.
All information on Coinpaprika is provided for informational purposes only and does not constitute financial or investment advice. Always conduct your own research (DYOR) and consult a qualified financial advisor before making investment decisions.
Coinpaprika is not liable for any losses resulting from the use of this information.