Replay Missed Trading Signals with History API
Use History API to backfill stored content, profile, follow, and affiliate events after reconnecting. You can also inspect a specific time window or reconcile events consumed by bots and Discord routes.
Request
History is available on Pro and Scale for every event type. With active or trialing Ultra, request `AFFILIATE` with the standard or Ultra key that owns the account. An unfiltered request searches all active accounts tracked by that key. A filtered request can include only handles currently tracked by that key. History returns stored `TWEET`, `PROFILE`, `FOLLOW`, and `AFFILIATE` rows. Delete, pin, and unpin lifecycle events arrive on the live stream.
| Parameter | Required | Notes |
|---|---|---|
| handle, handles, handle[], handles[] | No | One handle, repeated handles, or comma-separated handles |
| startDate | No | ISO datetime lower bound |
| endDate | No | ISO datetime upper bound |
| limit | No | Defaults to 100; maximum 1000 |
| type | No | TWEET, PROFILE, FOLLOW, or AFFILIATE. Defaults to TWEET |
const response = await fetch(
"https://api.tweetstream.io/api/history?limit=25&type=AFFILIATE",
{
headers: {
Authorization: `Bearer ${process.env.TWEETSTREAM_API_KEY}`,
},
},
);
console.log(await response.json());Backfill windows
History returns the newest results first and caps each request at 1000 rows. Split large replays into bounded `startDate` and `endDate` windows. Process each window idempotently, then continue from the oldest event you processed. Keep live lifecycle handlers active during reconnect recovery so state changes do not get mixed into historical content replay.
Response
Results are ordered newest first. The response includes `metadata.count` and any requested time bounds or handle filters. For `AFFILIATE`, the top-level account is the organization and `content` matches the live `affiliate_update` payload.
type VerifiedType = 'blue' | 'business' | 'government' | 'none';
type TweetVerifiedLabel = {
badge: string | null;
description: string;
url: string | null;
};
type TweetAuthor = {
banner?: string;
bio?: string;
followersCount?: number;
followingCount?: number;
id?: string;
joinedAt?: number;
location?: string;
metrics?: {
likes?: number;
tweets?: number;
};
// Includes a leading @ when present, for example "@elonmusk".
handle?: string;
name?: string;
platform?: 'twitter' | 'truth_social';
profileImage?: string;
url?: string;
verifiedLabel?: TweetVerifiedLabel;
verifiedType?: VerifiedType;
};
type AccountEventActor = TweetAuthor & {
websiteUrl?: string;
};
type ProfileUpdateEvent = {
kind: 'PROFILE';
eventId: string;
observedAt: number;
receivedAt?: number;
actor: AccountEventActor;
changes: {
avatar?: string;
banner?: string;
bio?: string;
handle?: string;
location?: string;
name?: string;
verifiedLabel?: TweetVerifiedLabel | null;
websiteUrl?: string | null;
};
previous?: {
avatar?: string;
banner?: string;
bio?: string;
handle?: string;
location?: string;
name?: string;
verifiedLabel?: TweetVerifiedLabel | null;
websiteUrl?: string | null;
};
};
type FollowEvent = {
kind: 'FOLLOW' | 'UNFOLLOW';
eventId: string;
observedAt: number;
receivedAt?: number;
actor: AccountEventActor;
target: AccountEventActor;
};
type AffiliateAccountIdentity = AccountEventActor & {
id: string;
};
type AffiliateUpdateEvent = {
action: 'added' | 'removed';
eventId: string;
observedAt: number;
receivedAt?: number;
organization: AffiliateAccountIdentity;
member: AffiliateAccountIdentity;
};
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 HistoryMedia = {
url: string;
type?: 'image' | 'video' | 'gif';
thumbnail?: string;
};
type TweetUrl = {
url: string;
name?: string;
tco?: string;
};
type TweetMention = {
handle?: string;
id?: string;
name?: string;
};
type HistoryTweetArticle = {
description?: string;
id?: string;
publishedAt?: number;
text?: string;
thumbnail?: string;
title?: string;
updatedAt?: number;
url?: string;
};
type HistoryTweetPollChoice = {
id?: string;
image?: string;
label?: string;
votes?: number;
};
type HistoryTweetPoll = {
choices: HistoryTweetPollChoice[];
endsAt?: number;
totalVotes?: number;
updatedAt?: number;
};
type TweetContentKind = 'post' | 'reply' | 'quote' | 'retweet';
type HistoryTweetReference = {
article?: HistoryTweetArticle;
type: 'reply' | 'quote' | 'retweet';
tweetId?: string;
text?: string;
translatedText?: string;
author?: TweetAuthor;
media?: HistoryMedia[];
poll?: HistoryTweetPoll;
quoted?: HistoryTweetReference;
};
type TweetContent = {
tweetId: string;
kind: TweetContentKind;
// Original tweet text when the stored content includes both original and translated text.
text: string;
translatedText?: string;
createdAt: number;
author: TweetAuthor;
article?: HistoryTweetArticle;
link?: string;
media?: HistoryMedia[];
mentions?: TweetMention[];
poll?: HistoryTweetPoll;
// Epoch ms from the realtime payload when the stored content includes it.
receivedAt?: number;
urls?: TweetUrl[];
ref?: HistoryTweetReference;
};
type HistoricalContent = TweetContent | ProfileUpdateEvent | FollowEvent | AffiliateUpdateEvent;
type HistoricalTweetResponse = {
tweetId: string;
twitterId: string;
twitterHandle: string | null;
body: string;
time: string;
// ISO persistence receive time for the history row.
receivedTime: string;
link: string;
messageType: 'TWEET' | 'PROFILE' | 'FOLLOW' | 'AFFILIATE';
content: HistoricalContent;
meta?: TweetMeta;
};
type HistoryResult = {
data: HistoricalTweetResponse[];
metadata: {
count: number;
handle?: string;
handles?: string[];
startDate?: string;
endDate?: string;
type?: 'TWEET' | 'PROFILE' | 'FOLLOW' | 'AFFILIATE';
};
};{
"data": [
{
"tweetId": "account:affiliate:aff_01JQ8YQ5K8B8QKH6M0P8A1V2WX",
"twitterId": "123",
"twitterHandle": "organization",
"body": "Added New Member (@newmember) to affiliate list",
"time": "2025-04-09T00:00:00.000Z",
"receivedTime": "2025-04-09T00:00:00.123Z",
"link": "https://x.com/newmember",
"messageType": "AFFILIATE",
"content": {
"action": "added",
"eventId": "aff_01JQ8YQ5K8B8QKH6M0P8A1V2WX",
"observedAt": 1744156800000,
"receivedAt": 1744156800123,
"organization": {
"id": "123",
"handle": "@organization",
"name": "Organization",
"verifiedType": "business"
},
"member": {
"id": "456",
"handle": "@newmember",
"name": "New Member",
"profileImage": "https://pbs.twimg.com/profile_images/newmember_normal.jpg"
}
}
}
],
"metadata": {
"count": 1,
"type": "AFFILIATE"
}
}Errors
| Status | Body | Meaning |
|---|---|---|
| 400 | { "error": "Invalid query parameters" } | Malformed dates, limit, type, or handle |
| 400 | { "error": "Invalid handle provided", "handle": "..." } | Handle validation failed |
| 400 | { "error": "startDate must be before endDate" } | Date range is reversed |
| 401 | { "error": "Missing or invalid API key" } | Missing or malformed bearer token |
| 401 | { "error": "Invalid API key" } | Bearer token was well-formed but does not match an active API key |
| 403 | { "error": "History is available on Pro and Scale; Ultra includes affiliate history" } | Plan does not include the requested History type |
| 403 | { "error": "Your subscription is not active", "message": "Please ensure your subscription is active", "status": "PAST_DUE" } | Subscription state does not allow history |
| 403 | { "error": "Handle ... is not among your tracked accounts" } | Requested replay handle is outside your watchlist |
| 429 | { "error": "Too many history requests", "retryAfterSeconds": 60 } | Rate limit exceeded |