Quick start · X (Twitter)
X (Twitter) WebSocket API quick start
Open one WebSocket with your API key and receive every tweet, reply, quote and retweet from the accounts on your list as JSON. This page gets a consumer running; the API reference holds every type.
- WebSocket
wss://ws.normal.1322.io/ws/normal- REST base
https://api.1322.io- Auth
- X-API-Key header
- Updated
What you get
Tweets, replies, quotes and retweets from the X accounts on your list, plus deletes, profile changes, pinned-tweet changes and follows. A tweet arrives in stages that share one tweet.id: tweet.mini.update comes first, and richer updates follow when the tweet has more to add (cards, quoted chains, untruncated text).
Typical delivery is 150-250ms. On X Ultimate, the selected accounts average around 100ms on their own stream.
Price: From $250/month for 100 accounts; WebSocket included. Plans: Starter $250/month (100 accounts), Standard $500/month (250 accounts), Professional $800/month (500 accounts), Ultimate $4,000/month (25 Ultimate + 350 accounts). Pay monthly, or prepay 3 months (10% off), 6 months (15% off) or a year (25% off). See X (Twitter) pricing.
Authentication
- Copy your API key from the API access page in your dashboard. Each stream has its own key: the normal key for
ws.normal, the ultimate key forws.ultimate.GET /v1/keyslists both on a hybrid plan. - Send the key as the
X-API-Keyheader on the WebSocket handshake and on every REST call.Authorization: Bearer <key>works too. - Run the client on your server. Your key is private, and the API is not built for browser (CORS) requests.
Connect to the WebSocket
Set these environment variables, then run either consumer from your server:
X_API_KEY: your normal (or ultimate) API key
# pip install "websockets>=13" (Python 3.9+)
import asyncio, json, os
from websockets.asyncio.client import connect
# Normal plans. X Ultimate's selected accounts: wss://ws.ultimate.1322.io/ws/ultimate
WS_URL = "wss://ws.normal.1322.io/ws/normal"
HEADERS = {"X-API-Key": os.environ["X_API_KEY"]}
def handle(event):
tweet = event.get("tweet") or {}
author = tweet.get("author") or {}
text = (tweet.get("body") or {}).get("text") or ""
# Stages of one tweet share tweet["id"]: merge them instead of posting twice.
print(event.get("type"), tweet.get("id"), author.get("handle"), text[:80])
async def main():
backoff = 1
while True:
try:
async with connect(WS_URL, additional_headers=HEADERS) as ws:
backoff = 1
async for raw in ws:
try:
event = json.loads(raw)
except ValueError:
continue
if isinstance(event, dict):
handle(event)
except Exception as exc:
print("disconnected:", exc)
await asyncio.sleep(backoff)
backoff = min(backoff * 2, 30)
asyncio.run(main())// npm install ws, save as consumer.mjs, run: node consumer.mjs (Node 18+)
import WebSocket from "ws"
// Normal plans. X Ultimate's selected accounts: wss://ws.ultimate.1322.io/ws/ultimate
const WS_URL = "wss://ws.normal.1322.io/ws/normal"
const API_KEY = process.env.X_API_KEY
if (!API_KEY) throw new Error("Set X_API_KEY")
function handle(event) {
const tweet = event.tweet ?? {}
// Stages of one tweet share tweet.id: merge them instead of posting twice.
console.log(event.type, tweet.id, tweet.author?.handle, (tweet.body?.text ?? "").slice(0, 80))
}
let backoff = 1000
function connect() {
const ws = new WebSocket(WS_URL, { headers: { "X-API-Key": API_KEY } })
ws.on("open", () => { backoff = 1000 })
ws.on("message", (raw) => {
let event
try { event = JSON.parse(raw.toString()) } catch { return }
if (event && typeof event === "object" && !Array.isArray(event)) handle(event)
})
ws.on("error", (err) => console.error("socket error:", err.message))
ws.on("close", () => {
setTimeout(connect, backoff)
backoff = Math.min(backoff * 2, 30000)
})
}
connect()- Frames are text, one JSON object each. The server pings; both libraries answer automatically.
- On X Ultimate, run one consumer per stream, each with its own key, and dedupe on
tweet.idacross both. - Events are not queued while you are disconnected, so reconnect promptly with a capped backoff, as above.
One example frame
Illustrative values for fictional accounts, trimmed to the fields most consumers read first. A real frame carries the full set for your tracked accounts.
{
"id": "6f1d2c3b-1322-4a5e-9b7c-0d1e2f3a4b5c",
"type": "tweet.mini.update",
"source": "1322",
"tweet": {
"id": "1978456123456789012",
"type": "TWEET",
"created_at": 1792073001000,
"author": {
"id": "1500000000000000001",
"handle": "exampledesk",
"name": "Example Desk",
"verified": false,
"metrics": {
"following": 210,
"followers": 48210
}
},
"subtweet": null,
"reply": null,
"quoted": null,
"body": {
"text": "Q3 numbers are live on the blog.",
"urls": [],
"mentions": []
},
"media": {
"images": [],
"videos": [],
"thumbnails": []
}
}
}tweet.typeisTWEET,RETWEET,QUOTEorREPLY;created_atis Unix milliseconds.tweet.update,tweet.update.expandedandtweet.fullrepeat the sametweet.idwith more fields. Keep the longestbody.text, union the media and never overwrite a filled field with an empty one.
Manage the list over REST
REST base https://api.1322.io. Each call below sends X-API-Key with your API key.
| Method | Path | What it does |
|---|---|---|
| GET | /v1/tracked | The accounts on this key's list. |
| POST | /v1/tracked | Add accounts: comma-separated usernames or numeric IDs. |
| DELETE | /v1/tracked | Remove accounts, same body. |
| GET | /v1/keys | Every key on your account with its tier and usage (hybrid plans). |
| GET | /v1/resolve/username/exampledesk | Resolve a username to its numeric X ID. |
curl "https://api.1322.io/v1/tracked" \
-H "X-API-Key: $X_API_KEY"
curl -X POST "https://api.1322.io/v1/tracked" \
-H "Content-Type: application/json" \
-H "X-API-Key: $X_API_KEY" \
-d '{ "identifiers": "exampledesk,examplefounder", "type": "username" }'
curl -X DELETE "https://api.1322.io/v1/tracked" \
-H "Content-Type: application/json" \
-H "X-API-Key: $X_API_KEY" \
-d '{ "identifiers": "examplefounder", "type": "username" }'
curl "https://api.1322.io/v1/keys" \
-H "X-API-Key: $X_API_KEY"
curl "https://api.1322.io/v1/resolve/username/exampledesk" \
-H "X-API-Key: $X_API_KEY"- Add and remove take up to 200 identifiers per request; the stream picks up list changes on its own, so there is no need to reconnect.
Full reference
Every field, endpoint and edge case for X (Twitter) lives in the API reference. Jump straight to the part you need:
Common questions
Which X WebSocket URL should I use?
Normal plans connect to wss://ws.normal.1322.io/ws/normal. X Ultimate adds wss://ws.ultimate.1322.io/ws/ultimate for its selected accounts; connect to both, each with its own key, and dedupe on tweet.id.
Why do I get several events for one tweet?
Tweets arrive in stages. tweet.mini.update is first; tweet.update, tweet.update.expanded and tweet.full add cards, quoted chains and untruncated text under the same tweet.id. Merge them into one record.
Do I need an X developer account?
No. Your 1322 API key is all you need; you do not need an X developer app or X API credits of your own.
Other quick starts
- Truth Social WebSocket quick start: Posts, retruths, quotes and media
- Instagram WebSocket quick start: Posts, Reels, carousels and Stories
- YouTube WebSocket quick start: Uploads, Shorts and deletions
- TikTok WebSocket quick start: Uploads, reposts, LIVE start and end
- Binance Square WebSocket quick start: Posts, pin changes and sentiment
- News WebSocket quick start: Full-text articles from news feeds
Related: