Skip to docs content
Docs

History API

Use History API to backfill stored content, profile, and follow events after reconnects, review incident windows, or reconcile downstream bots and Discord routes.

Request

History is available on Pro and Scale. Unfiltered requests search across your active tracked accounts; filtered requests can only include handles you currently track. History returns stored `TWEET`, `PROFILE`, and `FOLLOW` rows; lifecycle events such as delete, pin, and unpin are delivered on the live stream.

ParameterRequiredNotes
handle, handles, handle[], handles[]NoOne handle, repeated handles, or comma-separated handles
startDateNoISO datetime lower bound
endDateNoISO datetime upper bound
limitNoDefaults to 100; maximum 1000
typeNoTWEET, PROFILE, or FOLLOW. Defaults to TWEET
Fetch historytypescript
const response = await fetch(
  "https://api.tweetstream.io/api/history?handles=marketdesk&limit=25&type=TWEET",
  {
    headers: {
      Authorization: `Bearer ${process.env.TWEETSTREAM_API_KEY}`,
    },
  },
);
 
console.log(await response.json());
 

Backfill windows

History returns newest results first and caps each request at 1000 rows. For large replay windows, split by time range: request a bounded `startDate` and `endDate`, process idempotently, then move the next window forward from the oldest processed event. Keep live lifecycle handlers active during reconnect recovery so state changes are not mixed into historical content replay.

Response

Results are newest first and include `metadata.count` plus any requested bounds or handle filters.

Typetypescript
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 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 TweetContentKind = 'post' | 'reply' | 'quote' | 'retweet';
 
type HistoryTweetReference = {
  type: 'reply' | 'quote' | 'retweet';
  tweetId?: string;
  text?: string;
  translatedText?: string;
  author?: TweetAuthor;
  media?: HistoryMedia[];
  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;
  link?: string;
  media?: HistoryMedia[];
  mentions?: TweetMention[];
  // Epoch ms from the realtime payload when the stored content includes it.
  receivedAt?: number;
  urls?: TweetUrl[];
  ref?: HistoryTweetReference;
};
 
type HistoricalContent = TweetContent | ProfileUpdateEvent | FollowEvent;
 
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';
  content: HistoricalContent;
  meta?: TweetMeta;
};
 
type HistoryResult = {
  data: HistoricalTweetResponse[];
  metadata: {
    count: number;
    handle?: string;
    handles?: string[];
    startDate?: string;
    endDate?: string;
    type?: 'TWEET' | 'PROFILE' | 'FOLLOW';
  };
};
 
Examplejson
{
  "data": [
    {
      "tweetId": "follow_1",
      "twitterId": "123",
      "twitterHandle": "tracked",
      "body": "Followed @newaccount",
      "time": "2026-04-09T01:00:00.000Z",
      "receivedTime": "2026-04-09T01:00:00.500Z",
      "link": "https://x.com/newaccount",
      "messageType": "FOLLOW",
      "content": {
        "kind": "FOLLOW",
        "eventId": "follow_1",
        "observedAt": 1744160400000,
        "actor": {
          "id": "123",
          "handle": "tracked",
          "name": "Tracked Account",
          "followersCount": 125000,
          "followingCount": 321,
          "verifiedType": "business"
        },
        "target": {
          "id": "456",
          "handle": "newaccount",
          "name": "New Account",
          "profileImage": "https://pbs.twimg.com/profile_images/newaccount_normal.jpg",
          "websiteUrl": "https://newaccount.example"
        }
      }
    }
  ],
  "metadata": {
    "count": 1,
    "handle": "tracked",
    "handles": ["tracked"],
    "startDate": "2026-04-01T00:00:00.000Z",
    "endDate": "2026-04-30T23:59:59.999Z",
    "type": "FOLLOW"
  }
}
 

Errors

StatusBodyMeaning
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": "This endpoint is only available for Pro and Scale plan users" }Plan does not include History API
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