◈   ⌘ api · Intermediate

Alpaca Crypto WebSocket: Real-Time Market Data Guide

Learn how to use Alpaca's crypto WebSocket API to stream live price data, build trading bots, and react to market moves in milliseconds.

Uncle Solieditor · voc · 06.03.2026 ·views 33
◈   Contents
  1. → What Is the Alpaca Crypto WebSocket API?
  2. → Connecting and Authenticating
  3. → Subscribing to Trades and Quotes
  4. → Parsing the Message Schema
  5. → Error Handling and Reconnection Logic
  6. → Integrating with Trading Signals
  7. → Frequently Asked Questions
  8. → Conclusion

If you've ever watched a price spike on Binance and wished your bot had caught it three seconds earlier, the problem probably wasn't your strategy — it was your data pipeline. REST polling introduces latency that kills edge. WebSocket connections solve that by pushing data to you the moment it exists on the exchange. Alpaca's crypto WebSocket API is one of the cleaner implementations available to retail algo traders today, and this guide walks through everything you need to get live market data flowing into your system.

What Is the Alpaca Crypto WebSocket API?

Alpaca is a commission-free brokerage and market data provider that offers both REST and WebSocket APIs for crypto and equities. Their crypto data feed connects to major liquidity sources and normalizes tick data across venues — so instead of maintaining separate connections to Binance, Coinbase, or Bybit, you get a unified stream through one authenticated socket.

The WebSocket endpoint streams three primary data types: trades (individual executed transactions), quotes (best bid/ask at any moment), and bars (OHLCV candles at configurable intervals). For algo traders, quotes and trades are the most valuable — they let you react to microstructure changes before they're reflected in candle closes.

Alpaca offers two tiers: a free basic feed and a paid 'unlimited' feed. The free tier has some data delays and limitations on simultaneous subscriptions. For serious algotrading, the paid tier is worth the cost — stale quotes are worse than no quotes.

Connecting and Authenticating

Authentication happens over the WebSocket connection itself, not via HTTP headers. After establishing the connection, you send a JSON auth message with your API key and secret. Here's a minimal Python connection using the websockets library:

import asyncio
import json
import websockets

API_KEY = "your_api_key_here"
API_SECRET = "your_api_secret_here"
WS_URL = "wss://stream.data.alpaca.markets/v1beta3/crypto/us"

async def connect():
    async with websockets.connect(WS_URL) as ws:
        # Receive welcome message
        msg = await ws.recv()
        print("Server:", json.loads(msg))

        # Authenticate
        auth_payload = {
            "action": "auth",
            "key": API_KEY,
            "secret": API_SECRET
        }
        await ws.send(json.dumps(auth_payload))
        auth_response = await ws.recv()
        print("Auth:", json.loads(auth_response))

asyncio.run(connect())

A successful authentication returns a message with status 'authenticated'. If you see 'auth failed', double-check that you're using crypto-enabled API keys — Alpaca issues separate keys for paper trading, live trading, and market data. Many developers waste an hour on this.

Subscribing to Trades and Quotes

Once authenticated, you subscribe to specific symbols. Alpaca uses their own symbol format — Bitcoin is 'BTC/USD', Ethereum is 'ETH/USD'. You can subscribe to trades, quotes, and bars independently, and you can use wildcards ('*') to subscribe to everything, though that generates significant message volume.

import asyncio
import json
import websockets

API_KEY = "your_api_key_here"
API_SECRET = "your_api_secret_here"
WS_URL = "wss://stream.data.alpaca.markets/v1beta3/crypto/us"

async def stream_crypto():
    async with websockets.connect(WS_URL) as ws:
        # Welcome
        await ws.recv()

        # Auth
        await ws.send(json.dumps({
            "action": "auth",
            "key": API_KEY,
            "secret": API_SECRET
        }))
        await ws.recv()

        # Subscribe to BTC and ETH trades + quotes
        subscribe_payload = {
            "action": "subscribe",
            "trades": ["BTC/USD", "ETH/USD"],
            "quotes": ["BTC/USD", "ETH/USD"],
            "bars": ["BTC/USD"]
        }
        await ws.send(json.dumps(subscribe_payload))
        sub_response = await ws.recv()
        print("Subscribed:", json.loads(sub_response))

        # Listen for messages
        while True:
            try:
                message = await ws.recv()
                data = json.loads(message)
                for item in data:
                    msg_type = item.get("T")
                    if msg_type == "t":  # trade
                        print(f"TRADE {item['S']}: {item['p']} x {item['s']}")
                    elif msg_type == "q":  # quote
                        print(f"QUOTE {item['S']}: bid={item['bp']} ask={item['ap']}")
                    elif msg_type == "b":  # bar
                        print(f"BAR {item['S']}: O={item['o']} H={item['h']} L={item['l']} C={item['c']}")
            except websockets.exceptions.ConnectionClosed:
                print("Connection closed, reconnecting...")
                break

asyncio.run(stream_crypto())
Messages arrive as JSON arrays — always iterate over the list, not treat it as a single object. Alpaca batches multiple events in one WebSocket frame during high-volume periods.

Parsing the Message Schema

Understanding the field abbreviations is essential. Alpaca compresses field names to reduce bandwidth on high-frequency streams. Here's the complete reference for each message type:

Alpaca WebSocket Message Field Reference
FieldTypeDescription
TstringMessage type: t=trade, q=quote, b=bar, s=subscription, error
SstringSymbol (e.g. BTC/USD)
pfloatTrade price
sfloatTrade size
ttimestampEvent timestamp (RFC3339)
bpfloatBid price (quotes only)
apfloatAsk price (quotes only)
bsfloatBid size (quotes only)
asfloatAsk size (quotes only)
o/h/l/cfloatOpen/High/Low/Close (bars only)
vfloatVolume (bars only)

One thing that trips up developers: the 't' field name is used for both the message type identifier ('T' uppercase) and the timestamp field ('t' lowercase). Python's json parser handles case sensitivity correctly, but worth being explicit in your parsing logic.

Error Handling and Reconnection Logic

Production WebSocket clients need robust error handling. Connections drop — Alpaca's infrastructure, your ISP, or anything in between can interrupt the stream. A bot that doesn't reconnect is a bot that misses trades. Here's a production-grade wrapper with exponential backoff:

import asyncio
import json
import websockets
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

API_KEY = "your_api_key_here"
API_SECRET = "your_api_secret_here"
WS_URL = "wss://stream.data.alpaca.markets/v1beta3/crypto/us"

SYMBOLS = ["BTC/USD", "ETH/USD", "SOL/USD"]

async def authenticate(ws):
    await ws.recv()  # welcome
    await ws.send(json.dumps({
        "action": "auth",
        "key": API_KEY,
        "secret": API_SECRET
    }))
    response = json.loads(await ws.recv())
    if response[0].get("msg") != "authenticated":
        raise Exception(f"Auth failed: {response}")
    logger.info("Authenticated successfully")

async def subscribe(ws, symbols):
    await ws.send(json.dumps({
        "action": "subscribe",
        "trades": symbols,
        "quotes": symbols
    }))
    await ws.recv()  # subscription confirmation
    logger.info(f"Subscribed to: {symbols}")

def handle_message(item):
    msg_type = item.get("T")
    symbol = item.get("S", "")
    ts = item.get("t", "")

    if msg_type == "t":
        logger.info(f"{ts} TRADE {symbol}: price={item['p']}, size={item['s']}")
        # Your signal logic here — e.g., feed into VoiceOfChain or your own strategy engine
    elif msg_type == "q":
        spread = round(item['ap'] - item['bp'], 4)
        logger.info(f"{ts} QUOTE {symbol}: spread={spread}")

async def run_stream(retry_count=0):
    max_retries = 10
    backoff = min(2 ** retry_count, 60)  # cap at 60s

    if retry_count > 0:
        logger.info(f"Reconnecting in {backoff}s (attempt {retry_count}/{max_retries})")
        await asyncio.sleep(backoff)

    try:
        async with websockets.connect(
            WS_URL,
            ping_interval=20,
            ping_timeout=10
        ) as ws:
            await authenticate(ws)
            await subscribe(ws, SYMBOLS)
            retry_count = 0  # reset on successful connection

            while True:
                message = await ws.recv()
                for item in json.loads(message):
                    handle_message(item)

    except (websockets.exceptions.ConnectionClosed,
            websockets.exceptions.WebSocketException,
            ConnectionResetError) as e:
        logger.warning(f"Connection error: {e}")
        if retry_count < max_retries:
            await run_stream(retry_count + 1)
        else:
            logger.error("Max retries exceeded")

asyncio.run(run_stream())

Notice the ping_interval parameter in the websockets.connect call. Without it, idle connections get killed by network equipment after a few minutes. Alpaca expects keepalives — setting ping_interval to 20 seconds keeps the connection alive during slow market periods.

Integrating with Trading Signals

Raw tick data is noise. The value comes from what you do with it. Platforms like Bybit and OKX provide their own WebSocket feeds, but they require exchange-specific integrations. With Alpaca, you get normalized data you can feed directly into signal logic without exchange-specific adapters.

One practical pattern: use the Alpaca WebSocket for price monitoring and combine it with signals from a platform like VoiceOfChain, which aggregates on-chain and exchange data to generate actionable trading alerts. When VoiceOfChain fires a signal on a token, your Alpaca stream already has the price context — you know the current bid/ask spread, recent trade volume, and whether price is moving toward or away from the signal level before you enter.

For traders who also use Coinbase Advanced Trade or Binance for execution, a common architecture looks like: Alpaca WebSocket for price feeds → signal evaluation layer → execution via exchange-native API. This decouples data from execution, which makes the system easier to maintain and test.

Frequently Asked Questions

Does Alpaca's crypto WebSocket support all cryptocurrencies?
Alpaca supports a curated list of crypto pairs, primarily major assets like BTC, ETH, SOL, and others traded on US-regulated venues. The full list of supported symbols is available via their REST API at /v1beta3/crypto/us/meta/symbols. It's smaller than what Binance or Bybit list, but covers most liquid assets.
What's the difference between Alpaca's free and paid data tiers?
The free tier provides delayed or sampled data with limits on concurrent subscriptions. The paid 'Algo Trader Plus' plan gives real-time data with no subscription caps. For live trading bots where latency matters, the paid tier is necessary — a 15-second delay on quotes makes the stream useless for execution decisions.
Can I use the Alpaca WebSocket without an Alpaca brokerage account?
Yes. Alpaca offers standalone market data subscriptions separate from their brokerage product. You create an API key scoped to market data only. This is useful if you execute trades on OKX or KuCoin but want Alpaca's normalized data feed for monitoring.
How many symbols can I subscribe to simultaneously?
The paid tier supports subscribing to all available symbols via wildcard ('*'). The free tier has limits, and subscribing to everything on the free tier will often result in throttling or dropped messages. Start with the specific symbols you trade and expand from there.
What happens if I miss messages during a reconnect?
Alpaca's WebSocket is a live stream — there's no replay mechanism on reconnect. If your connection drops during a volatile period, you'll have a gap in your tick data. For critical applications, supplement with REST API polling during reconnect windows to catch any missed price action.
Is Alpaca's crypto data feed suitable for high-frequency trading?
It's well-suited for medium-frequency strategies — signals triggered by tick events, spread monitoring, momentum detection. Pure HFT at microsecond latency requires co-located infrastructure Alpaca doesn't offer. For most retail algo traders, the feed's latency profile is more than adequate.

Conclusion

The Alpaca crypto WebSocket API gives retail algo traders access to real-time market data without the complexity of managing exchange-specific connections. The authentication flow is clean, the message schema is consistent, and with proper reconnection logic, you can build stable streaming infrastructure in an afternoon.

The code patterns in this guide — authenticated connections, symbol subscription, message parsing, and exponential backoff reconnection — form the backbone of any production WebSocket client. Adapt them to your strategy: whether you're monitoring spreads, reacting to trade volume spikes, or feeding signals from VoiceOfChain into automated execution on Binance or Coinbase, the underlying socket architecture stays the same.

Start with two or three symbols you actively trade. Get the data flowing, log it, understand its rhythm. Then build your logic on top of a stream you trust.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies