连接、认证与心跳
使用文档列出的区域端点,在升级连接时认证,并保留原生 ping 和 pong 处理。
选择基础 URL
| 接口 | URL | 用途 |
|---|---|---|
| WebSocket(美国) | wss://ws-iad.tweetstream.io/ws | 用于美国境内的连接 |
| WebSocket(全球) | wss://ws-global.tweetstream.io/ws | 用于美国以外的连接 |
| REST | https://api.tweetstream.io | /api/history、/api/me 和账号管理的基础域名 |
| Dashboard | https://tweetstream.io/dashboard | API key、监控列表、账单和 Discord 路由 |
认证请求
实时客户端使用 WebSocket 子协议认证,REST 请求使用 Bearer token。API key 只能保存在服务端,绝不能放入公开的浏览器代码。
| 上下文 | Header 或协议 | 说明 |
|---|---|---|
| WebSocket | tweetstream.v1 + tweetstream.auth.token.<API_KEY> | 首选的实时认证方式 |
| WebSocket fallback | Authorization: Bearer <API_KEY> or ?apiKey=<API_KEY> | 优先使用 Authorization;只有运行时无法设置 Header 时才使用查询参数认证 |
| REST | Authorization: Bearer <API_KEY> | Pro 或 Scale 的 History 请求使用标准 key。有效或试用中的 Ultra 可用拥有该账号的标准 key 或 Ultra key 请求 AFFILIATE。账号管理和 /api/me 可使用任一 key |
认证服务端连接
typescript
const socket = new WebSocket("wss://ws-global.tweetstream.io/ws", [
"tweetstream.v1",
`tweetstream.auth.token.${process.env.TWEETSTREAM_API_KEY}`,
]);
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
console.log(message.t, message.op, message.d);
});连接
美国境内使用 wss://ws-iad.tweetstream.io/ws,其他地区使用 wss://ws-global.tweetstream.io/ws。服务端返回 tweetstream.v1,不会回显携带认证 token 的协议。示例通过指数退避重连。短连接会继续增加等待时间,稳定连接 30 秒后重置。
使用 Node.js 连接
typescript
import WebSocket from "ws";
type StreamEvent = {
t?: string;
op?: string;
d?: {
author?: { handle?: string };
detected?: unknown;
text?: string;
};
};
const apiKey = process.env.TWEETSTREAM_API_KEY;
if (!apiKey) {
throw new Error("Missing TWEETSTREAM_API_KEY");
}
let retry = 0;
let healthyTimer: ReturnType<typeof setTimeout> | undefined;
let reconnectTimer: ReturnType<typeof setTimeout> | undefined;
function scheduleReconnect(reason: string) {
if (reconnectTimer) return;
const delayMs = Math.min(30_000, 1_000 * 2 ** retry) + Math.floor(Math.random() * 500);
retry += 1;
console.warn(`Reconnecting in ${delayMs}ms: ${reason}`);
reconnectTimer = setTimeout(() => {
reconnectTimer = undefined;
connect();
}, delayMs);
}
function connect() {
const ws = new WebSocket("wss://ws-global.tweetstream.io/ws", [
"tweetstream.v1",
`tweetstream.auth.token.${apiKey}`,
]);
ws.on("open", () => {
if (reconnectTimer) clearTimeout(reconnectTimer);
reconnectTimer = undefined;
healthyTimer = setTimeout(() => {
retry = 0;
healthyTimer = undefined;
}, 30_000);
console.log("TweetStream connected");
});
ws.on("message", (raw) => {
const event = JSON.parse(raw.toString()) as StreamEvent;
if (event.t === "tweet" && event.op === "content") {
const tweet = event.d;
console.log(tweet?.author?.handle, tweet?.text);
}
if (event.t === "tweet" && event.op === "meta") {
console.log("enrichment", event.d?.detected);
}
});
ws.on("close", (code, reason) => {
if (healthyTimer) clearTimeout(healthyTimer);
healthyTimer = undefined;
scheduleReconnect(`close ${code}: ${reason.toString()}`);
});
ws.on("unexpected-response", (_request, response) => {
console.error("Connection rejected", response.statusCode, response.statusMessage);
response.resume();
if (response.statusCode === 429 || response.statusCode === 503) {
scheduleReconnect(`HTTP ${response.statusCode}`);
return;
}
process.exitCode = 1;
});
ws.on("error", (error) => {
console.error("WebSocket error", error.message);
});
}
connect();使用 Python 连接
使用任何能发送这两个子协议的 WebSocket 运行时。正常关闭和传输错误都会通过指数退避重连。短连接会继续增加等待时间,稳定连接 30 秒后重置。
Python 重连客户端
python
import asyncio
import json
import os
import websockets
API_KEY = os.environ["TWEETSTREAM_API_KEY"]
URI = "wss://ws-global.tweetstream.io/ws"
PROTOCOLS = ["tweetstream.v1", f"tweetstream.auth.token.{API_KEY}"]
async def main():
loop = asyncio.get_running_loop()
retry = 0
while True:
connected_at = None
reason = "connection closed"
try:
async with websockets.connect(URI, subprotocols=PROTOCOLS) as ws:
connected_at = loop.time()
async for raw in ws:
event = json.loads(raw)
if event["t"] == "tweet" and event["op"] == "content":
tweet = event["d"]
print(tweet.get("author", {}).get("handle"), tweet.get("text"))
except Exception as error:
reason = str(error)
if connected_at is not None and loop.time() - connected_at >= 30:
retry = 0
wait = min(30, 2 ** retry)
retry = min(retry + 1, 5)
print(f"reconnecting in {wait}s after {reason}")
await asyncio.sleep(wait)
asyncio.run(main())处理心跳和断开
TweetStream 每 30 秒发送一次原生 WebSocket ping。标准 Node.js 和 Python 客户端会自动回复 pong。服务端会关闭无响应的连接。重连后,使用 History API 回补已存内容、资料、关注和关联账号事件。
处理限制和重试
套餐设定活跃 WebSocket、监控账号和 History API 限制。收到 429 时暂停对应流程;如果响应包含 retryAfterSeconds,按其等待,否则使用常规退避。
| 接口 | 限制信号 | 推荐处理 |
|---|---|---|
| WebSocket | 连接升级时返回 429 | 关闭不用的连接,或升级到更多连接数的套餐 |
| History API | 限流时返回 retryAfterSeconds | 等待后再回放下一个窗口 |
| 监控账号 | /api/me 返回套餐用量 | 批量添加前检查 count 和 limit |