Twitter WebSocket API Quickstart
Connect to TweetStream's real-time Twitter streaming API alternative in minutes
Getting Started
Most clients authenticate via WebSocket subprotocols by including the API key in the protocol list. If your environment cannot set that token, Bearer auth headers and apiKey query parameters are also accepted.
Stream tweet, account, and control events in real time via WebSocket. Evaluating Twitter API alternatives? Start here.
Connection Details
| Endpoint | wss://ws.tweetstream.io/ws |
| Protocol 1 | tweetstream.v1 |
| Protocol 2 | tweetstream.auth.token.YOUR_API_KEY |
Quick Start Example
Copy and paste this code to get connected immediately:
const API_KEY = 'YOUR_API_KEY'; // Get your key from the dashboard
const WS_URL = 'wss://ws.tweetstream.io/ws';
const protocols = [
'tweetstream.v1',
`tweetstream.auth.token.${API_KEY}`,
];
const ws = new WebSocket(WS_URL, protocols);
ws.onopen = () => {
console.log('Connected to TweetStream!');
};
ws.onmessage = (event) => {
const envelope = JSON.parse(event.data);
if (envelope.t === 'tweet' && envelope.op === 'content') {
const tweet = envelope.d;
const author = tweet.author?.handle ?? tweet.author?.name ?? 'unknown';
console.log(`[${author}] ${tweet.text}`);
}
};
ws.onclose = () => {
console.log('WebSocket closed');
};
ws.onerror = (error) => {
console.error('WebSocket error', error);
};Production Example
For production use, add reconnection handling:
type VerifiedType = 'blue' | 'business' | 'government' | 'none';
type TweetAuthor = {
id?: string;
handle?: string;
name?: string;
platform?: 'twitter' | 'truth_social';
profileImage?: string;
followersCount?: number;
verifiedType?: VerifiedType;
};
type Media = {
url: string;
type?: 'image' | 'video' | 'gif';
thumbnail?: string;
};
type TweetContent = {
tweetId: string;
text: string;
createdAt: number;
author: TweetAuthor;
link?: string;
media?: Media[];
ref?: {
type: 'reply' | 'quote' | 'retweet';
tweetId?: string;
text?: string;
author?: TweetAuthor;
media?: Media[];
};
};
type MetaSource = 'text' | 'ocr';
type DetectedCexMarket = {
exchange: 'bybit' | 'binance' | 'hyperliquid';
symbol?: string;
priceUsd?: number;
url?: string;
baseAsset?: string;
quoteAsset?: string;
sources: MetaSource[];
};
type DetectedPredictionMarket = {
exchange: 'polymarket' | 'kalshi';
marketId?: string;
title?: string;
priceUsd?: number;
url?: string;
sources: MetaSource[];
};
type DetectedToken = {
symbol?: string;
name?: string;
contract?: string;
chain?: string;
networkId?: number;
priceUsd?: number;
sources: MetaSource[];
};
type TweetMeta = {
tweetId: string;
ocr?: {
text: string;
};
detected?: {
tokens?: Array<{
symbol?: string;
name?: string;
contract?: string;
chain?: string;
networkId?: number;
priceUsd?: number;
sources: Array<'text' | 'ocr'>;
}>;
cex?: Array<{
exchange: 'bybit' | 'binance' | 'hyperliquid';
symbol?: string;
priceUsd?: number;
url?: string;
baseAsset?: string;
quoteAsset?: string;
sources: Array<'text' | 'ocr'>;
}>;
prediction?: Array<{
exchange: 'polymarket' | 'kalshi';
marketId?: string;
title?: string;
priceUsd?: number;
url?: string;
sources: Array<'text' | 'ocr'>;
}>;
};
};
type Media = {
url: string;
type?: 'image' | 'video' | 'gif';
thumbnail?: string;
};
type TweetUpdate = {
tweetId: string;
text?: string;
media?: Media[];
ref?: TweetContent['ref'];
};
type AccountEventActor = TweetAuthor & {
banner?: string;
bio?: string;
location?: string;
url?: string;
websiteUrl?: string;
followingCount?: number;
};
type ProfileUpdateEvent = {
kind: 'PROFILE';
eventId: string;
observedAt: number;
actor: AccountEventActor;
changes: {
avatar?: string;
banner?: string;
bio?: string;
handle?: string;
location?: string;
name?: string;
};
previous?: {
avatar?: string;
banner?: string;
bio?: string;
handle?: string;
location?: string;
name?: string;
};
};
type FollowEvent = {
kind: 'FOLLOW';
eventId: string;
observedAt: number;
actor: AccountEventActor;
target: AccountEventActor;
};
type Envelope<T extends object> = {
v: 1;
t: 'tweet' | 'account' | 'control';
op:
| 'content'
| 'meta'
| 'update'
| 'delete'
| 'profile_update'
| 'follow'
| 'auth_ping'
| 'auth_pong'
| 'twitter_handles_result';
id?: string;
ts: number;
d: T;
};
type TwitterHandlesResult = {
action: 'follow' | 'unfollow';
requestId: string | null;
results: Array<{
input: string;
normalizedHandle?: string;
twitterId?: string;
state: string;
message?: string;
}>;
error: string | null;
};
const API_KEY = process.env.TWEETSTREAM_API_KEY ?? 'YOUR_API_KEY';
const WS_URL = 'wss://ws.tweetstream.io/ws';
const PROTOCOLS = ['tweetstream.v1', `tweetstream.auth.token.${API_KEY}`];
let ws: WebSocket | null = null;
let reconnectTimer: ReturnType<typeof setTimeout> | undefined;
function connect() {
ws = new WebSocket(WS_URL, PROTOCOLS);
ws.onopen = () => {
console.log('Connected to TweetStream');
};
ws.onmessage = (event) => {
const envelope = JSON.parse(event.data) as Envelope<Record<string, unknown>>;
if (envelope.t === 'tweet') {
if (envelope.op === 'content') {
const tweet = envelope.d as TweetContent;
const author = tweet.author?.handle ?? tweet.author?.name ?? 'unknown';
const platform = tweet.author?.platform ?? 'twitter';
console.log(`[CONTENT] ${author} (${platform}): ${tweet.text}`);
} else if (envelope.op === 'update') {
const update = envelope.d as TweetUpdate;
console.log(`[UPDATE] ${update.tweetId}`, update);
} else if (envelope.op === 'meta') {
const meta = envelope.d as TweetMeta;
console.log(`[META] ${meta.tweetId}`, meta);
}
return;
}
if (envelope.t === 'account') {
if (envelope.op === 'profile_update') {
const profile = envelope.d as ProfileUpdateEvent;
const actor = profile.actor.handle ?? profile.actor.name ?? 'unknown';
console.log(`[PROFILE] ${actor}`, profile.changes);
} else if (envelope.op === 'follow') {
const follow = envelope.d as FollowEvent;
const actor = follow.actor.handle ?? follow.actor.name ?? 'unknown';
const target = follow.target.handle ?? follow.target.name ?? 'unknown';
console.log(`[FOLLOW] ${actor} -> ${target}`);
}
return;
}
if (envelope.t === 'control' && envelope.op === 'twitter_handles_result') {
const payload = envelope.d as TwitterHandlesResult;
console.log('[HANDLES RESULT]', payload);
}
};
ws.onclose = (event) => {
console.warn('WebSocket closed', event.code, event.reason);
scheduleReconnect();
};
ws.onerror = (error) => {
console.error('WebSocket error', error);
ws?.close();
};
}
function scheduleReconnect() {
if (reconnectTimer) return;
reconnectTimer = setTimeout(() => {
reconnectTimer = undefined;
connect();
}, 5_000);
}
connect();Envelope Format
All messages use a consistent envelope structure:
v- Protocol version (always 1)t- Message family: tweet, account, or controlop- Operation such as content, meta, update, profile_update, follow, or twitter_handles_resultts- Unix timestamp in millisecondsd- Payload data (varies by type and operation)
Common Issues
- Authentication failed: Verify your API key is from an active subscription
- Connection limit reached: Close existing connections or upgrade your plan
- No tweets appearing: Check that accounts are tracked in your dashboard
Start real-time Twitter WebSocket alerts today
WebSocket delivery, OCR, and token detection - no infrastructure to build.
Start 3-Day TrialFrom $199/mo · Basic/Elite 3-day trial · OCR + token detection included
