1322 / developer reference
Connect one feed.
Keep the event contract.
- REST
- Manage tracked accounts
- EVENTS
- Normalized WebSocket delivery
- PRIORITY
- X Ultimate ~100ms / Truth priority ~50ms avg
Selected source
X / Twitter
Technical schema and integration referenceX Ultimate
~100ms avgSelected-account operating averageTwitter API & WebSocket
Integrate Twitter/X tracking, real-time events, and managed accounts via our high-speed global nodes.
Quick links
Base URLs
REST API Base
https://api.1322.ioWebSocket Endpoints
Normal
wss://ws.normal.1322.io/ws/normalUltimate
wss://ws.ultimate.1322.io/ws/ultimateAuthentication
REST API: Send your API key via query key=, header X-API-Key, or Authorization: Bearer <key>.
WebSocket: Same headers, or query token=<key>. Use the key that matches the stream (normal key for normal URL, ultimate key for ultimate URL).
Rate limit
Buckets are keyed on (client IP, API key). Reads: 1200/min (20/s), GET list, health, keys, tweet lookup, resolve. Writes: 600/min (10/s), POST / DELETE add, remove, admin mutations. Errors use 401 / 403 / 429. Bulk add/remove accept up to 200 identifiers per request; more than 200 returns 400.
WebSocket Guide
Connection Behavior
- • Server → client: Text frames only; each frame is one JSON object (UTF-8).
- • One-way for events: You only need to handle incoming messages. Server sends periodic pings; respond to pongs to avoid timeouts.
- • Filtered: You only receive events for Twitter user IDs in your tracked list for the key you used. Manage tracked accounts via the REST API or Discord; the stream updates automatically.
Event Flow (Tweets)
Tweets arrive progressively across stages, each enriches the previous:
- 1.
tweet.mini.updatefirst event (fastest, includes full author profile) - 2.
tweet.updateenrichment (cards, subtweet chain, metrics, poll, grok) - 3.
tweet.update.expandeduntruncated text, full article body - 4.
tweet.fullfullchain: post with entire reply/quote chain resolved recursively
Other events: tweet.deleted, profile.update, profile.pinned.update, profile.unpinned.update, following.update.
Additive Merge (Recommended)
For the best display, merge stages by tweet.id (snowflake):
- • Use the longest
body.text - • Union all media arrays
- • Keep the deepest
subtweetchain - • Take the highest metric values
- • Never overwrite a populated field with null/empty. A later stage may lack data that an earlier stage had
Hybrid Clients
Hybrid accounts should connect to both endpoints simultaneously:
- • Normal stream: tweet events + profile events + following events
- • Ultimate stream: tweet events (profile/following events may only arrive on normal)
Deduplicate by tweet.id across both streams. Merge stages from both feeds additively for the richest card.
Reconnection
- • On disconnect, reconnect with exponential backoff (1s, 2s, 4s, 8s… capped at 30s).
- • Respond to server pings with pongs to keep the connection alive.
- • No events are queued during disconnection, you may miss events while offline.
WS Data Types & Events
All data types the 1322 WebSocket sends you.
export interface WebsocketWorkerEvent {
id: string; // Unique ID for this event (Can be used for debouncing and deduplication across multiple connections)
type: string; // The type of event being sent
source: "1322"; // Always "1322", identifies the event origin
}
Tweet Events
export interface MiniTweetUpdate {
type: 'tweet.mini.update';
tweet: TwitterMiniTweet;
}
This is the main event for tweets, this will be the first event for ANY tweet. Meant to be brief information for speed. If the tweet is quote/retweet etc it won't yet contain subtweet information.
export interface TweetUpdate {
type: 'tweet.update';
tweet: TwitterTweet;
}
Used to provide full information on a tweet. Sent ~60ms after tweet.mini.update if the tweet is a quote, reply, retweet, or contains a URL with a card.
export interface TweetUpdateExpanded {
type: 'tweet.update.expanded';
tweet: TwitterTweet;
}
Includes FULL text of any tweet (tweet.update truncates it) and contains info about articles. Only sent if there's an article or truncated text.
export interface DeletedTweet {
type: 'tweet.deleted';
tweet: TwitterTweet; // metrics may be outdated such as likes, replies, retweets, etc
deleted_at: number;
}
export interface TweetFull {
type: 'tweet.full';
tweet: TwitterTweet; // Fullchain: subtweet forms a recursive chain of full tweets
}
Fullchain, entire reply or quote thread resolved in one payload. The root tweet's subtweet is a full TwitterTweet (not null) that links down the chain. Each node has its own subtweet for the next level.
Reply chain: Root → subtweet → Level 2 → subtweet → Level 3 → null Quote chain: Root → subtweet → Quoted → subtweet → Quoted's quote → null Walk subtweet recursively to traverse the full thread. Each node has: author, body, media, metrics, card, poll, article, grok.
Profile & Misc Events
export interface ProfileUpdate {
type: 'profile.update';
user: TwitterUser; // The new profile information for this user
before: TwitterUser; // The old profile information for this user
}
export interface FollowingUpdate {
type: 'following.update';
change: "unfollowed" | "followed";
following: TwitterUser; // The target user who was followed/unfollowed
user: TwitterUser; // The watched user who was detected to have followed the target user
}
export interface WorkerProfilePinnedUpdateEvent extends WebsocketWorkerEvent {
type: 'profile.pinned.update';
user: TwitterUser; // The user who pinned their tweet(s)
pinned: TwitterTweet[]; // The expanded pinned tweet(s) for this user
}
export interface WorkerProfileUnpinnedUpdateEvent extends WebsocketWorkerEvent {
type: 'profile.unpinned.update';
user: TwitterUser; // The user who pinned their tweet(s)
pinned: TwitterTweet[]; // The expanded pinned tweet(s) for this user
}
Types
// Represents a twitter user's account information in complete format
// The TwitterUser interface represents a complete / detailed version of a Twitter / X user's profile.
export interface TwitterUser {
id: string; // The user's account ID
handle: string; // The user's account handle (Without @)
private: boolean; // If the user's account is currently private or not
verified: boolean | VerificationBadge; // Blue check, gold org badge, or boolean shorthand
sensitive: boolean; // If the user's account is marked to contain sensitive information or not
restricted: boolean; // If the user's account is currently restricted for unusual activity or not
joined_at: number; // The UNIX timestamp of when the user joined Twitter in milliseconds
// Contains the user's profile information
profile: {
name: string; // The user's profile display name
location: null | string; // The user's profile location (if one is set)
avatar: null | string; // The user's custom profile avatar url (if one is set)
banner: null | string; // The user's custom profile banner url (if one is set)
pinned: string[]; // The list of pinned tweet IDs (if any are pinned) on the user's profile
// Contains the user's profile custom link information (if one is set)
url: null | {
name: string; // The display name of the link
url: string; // The original URL of the link
tco: string; // The Twitter t.co wrapped URL variant of the link
};
// Contains the user's profile description information
description: {
text: string; // The plain text of the description
// The list of clickable URLs contained in the description
urls: {
name: string; // The display name of the URL
url: string; // The original URL value of the URL
tco: string; // The Twitter t.co wrapped URL variant of the URL
}[];
};
};
// Contains the user's profile metrics information
metrics: {
likes: number; // The number of liked tweets by the user
media: number; // The number of media (images, videos, gifs) posted by the user
tweets: number; // The number of tweets posted by the user
friends: number; // The number of friends of the user (People who follow the user and the user follows them back)
followers: number; // The number of followers of the user
following: number; // The number of accounts followed by the user
};
}
// When verified is an object (instead of boolean), it carries badge tier and optional org linkage
interface VerificationBadge {
type: 'none' | 'blue' | 'gold'; // none=unverified, blue=standard checkmark, gold=organization
label: null | {
description: string; // Affiliated org or business name
badge: string | null; // Badge image URL
url: string | null; // Link to org profile
};
}
// Treat verified: true or type !== 'none' as having a checkmark.
// Use label only when showing org-linked badges.
interface TwitterMiniUser {
id: string;
handle: string;
name?: string;
avatar?: string;
verified?: boolean | VerificationBadge; // boolean for simple check, or object for org badges
profile?: { name: string; avatar: string | null; };
metrics?: { following: number; followers: number; };
}
// Represents a twitter tweet's information in a minimal format
// The TwitterMiniTweet interface represents a brief / compressed version of a Twitter / X post.
export interface TwitterMiniTweet {
id: string; // The tweet's snowflake ID
type: 'TWEET' | 'RETWEET' | 'QUOTE' | 'REPLY'; // The type of the tweet
created_at: number; // The UNIX timestamp of when the tweet was created on Twitter's servers in milliseconds
// The account information of the tweet's author
author: TwitterMiniUser;
// The subtweet (reply, quote, retweet) referenced by this tweet (if any)
subtweet: null | TwitterMiniTweet;
// Contains information about who the tweet is replying to (if any)
reply: null | {
id: string; // The account ID of the user who was replied to
handle: string; // The account handle (Without @) of the user who was replied to
};
// Contains information about who the tweet is quoting (if any)
quoted: null | {
id: string; // The account ID of the user who was quoted
handle: string; // The account handle (Without @) of the user who was quoted
};
// Contains the tweet's body information
body: {
text: string; // The plain text of the tweet body
// The list of clickable URLs contained in the tweet
urls: {
name: string; // The display name of the URL
url: string; // The original URL value of the URL
tco: string; // The Twitter t.co wrapped URL variant of the URL
}[];
// The list of mentioned users in the tweet
mentions: {
id: string; // The mentioned user's account ID
name: string; // The mentioned user's display name
handle: string; // The mentioned user's account handle (Without @)
}[];
};
// Contains information about the tweet's media
media: {
images: string[]; // The list of image URLs in the tweet
videos: string[]; // The list of video URLs in the tweet
thumbnails: string[];
// Note! Proxied media will only be available in certain scenarios
// Fall back to using normal media URLs if proxied URLs are not available
proxied: null | {
images: string[]; // The list of proxied image URLs in the tweet
};
};
}
// Represents a twitter tweet's information in complete format
// The TwitterTweet interface represents a complete / detailed / rich version of a Twitter / X post.
export interface TwitterTweet {
id: string; // The tweet's snowflake ID
type: 'TWEET' | 'RETWEET' | 'QUOTE' | 'REPLY'; // The type of the tweet
created_at: number; // The UNIX timestamp of when the tweet was created on Twitter's servers in milliseconds
// The account information of the tweet's author
author: TwitterUser;
// The subtweet (reply, quote, retweet) referenced by this tweet (if any)
subtweet: null | TwitterTweet;
// Contains information about who the tweet is replying to (if any)
reply: null | {
id: string; // The account ID of the user who was replied to
handle: string; // The account handle (Without @) of the user who was replied to
};
// Contains information about who the tweet is quoting (if any)
quoted: null | {
id: string; // The account ID of the user who was quoted
handle: string; // The account handle (Without @) of the user who was quoted
};
// Contains the tweet's body information
body: {
text: string; // The plain text of the tweet body
// The list of clickable URLs contained in the tweet
urls: {
name: string; // The display name of the URL
url: string; // The original URL value of the URL
tco: string; // The Twitter t.co wrapped URL variant of the URL
}[];
// The list of mentioned users in the tweet
mentions: {
id: string; // The mentioned user's account ID
name: string; // The mentioned user's display name
handle: string; // The mentioned user's account handle (Without @)
}[];
// The list of structured components contained in the tweet body for expanded rich / long tweets (if any)
components: (
| {
// The `text` component represents represents a text part of a long tweet
type: 'text';
text: string; // The plain text of the component
bold: boolean; // If the text is bold or not
italics: boolean; // If the text is italics or not
}
| {
// The `image` component represents represents an image part of a long tweet
type: 'image';
url: string;
}
| {
// The `video` component represents represents a video part of a long tweet
type: 'video';
url: string;
}
)[];
};
// Contains information about the tweet's media
media: {
images: string[]; // The list of image URLs in the tweet
videos: string[]; // The list of video URLs in the tweet
thumbnails: string[]; // The list of video thumbnail URLs in the tweet
// Note! Proxied media will only be available in certain scenarios
// Fall back to using normal media URLs if proxied URLs are not available
proxied: null | {
images: string[]; // The list of proxied image URLs in the tweet
thumbnails: string[]; // The list of proxied thumbnail URLs in the tweet
};
};
// Contains the embedded Grok xAI conversation preview in the tweet (if any)
grok: null | {
id: string; // The ID of the Grok conversation
// The preview information of the Grok conversation (Supports either an answer or generated images)
conversation: {
from: 'USER' | 'AGENT'; // Who is the sender of the message
message: string; // The text of the message
images: string[]; // The list of image URLs in the message
}[];
};
// Contains information about an embedded card within the tweet (if any)
card: null | {
url: string; // The original URL of the card
image: string; // The thumbnail image URL of the card
title: string; // The title text of the card
description: string; // The description text of the card
};
// Contains information about a poll within the tweet (if any)
poll: null | {
ends_at: number; // The UNIX timestamp of when the poll ends in milliseconds
updated_at: number; // The UNIX timestamp of when the poll was last updated in milliseconds
// The list of poll choices
choices: {
label: string; // The label text of the poll choice
count: number; // The number of votes for the poll choice
}[];
};
// Contains information about an article within the tweet (if any)
article: null | {
id: string; // The article's snowflake ID
title: string; // The title text of the article
thumbnail: null | string; // The thumbnail image URL of the article
created_at: number; // The UNIX timestamp of when the article was created in milliseconds
updated_at: number; // The UNIX timestamp of when the article was last updated in milliseconds
// Contains the article's body information
body: {
text: string; // The plain text of the article body
// The list of components represents the rich structured component within the article
// RENDERING: Each component should be rendered as a separate section with double line vertical spacing
components: (
| {
// The `divider` component represents a divider between article sections
// Twitter / X user interface renders this divider as a horizontal gray line
type: 'divider';
}
// The `text` component represents some textual component within the article
| {
type: 'text';
// The `header-one` variant is a large title which separates the article into sections
// The `header-two` variant is a smaller title which separates the article into subsections
// The `paragraph` variant is just regular paragraph text
// The `blockquote` variant is just paragraph text idented by a line to form a quoted block of text
// The `ordered-list` variant is an ordered list with numbering for each line
// The `unordered-list` variant is an unordered list with bullet points for each line
// The `latex-box` variant is a LaTeX equation box used to render mathematical equations (Ex: `$$a^2 + b^2 = c^2$$`)
// The `markdown-box` variant is a Markdown code block used to render code snippets (Ex: `\\`\\`js\nconsole.log('Hello World!');\n\\`\\`\\`)
variant:
| 'header-one'
| 'header-two'
| 'paragraph'
| 'blockquote'
| 'ordered-list'
| 'unordered-list'
| 'latex-box'
| 'markdown-box';
// The list of lines represents the list items of this component
// RENDERING: Each line should be rendered as a separate line with single line vertical spacing
lines: {
// The plain text of this line
// The `styles` array contains unique styling information for parts of the text
// The `urls` array contains unique URLs for parts of the text
text: string;
// The list of styling applied to parts of the text identified by the `from` and `to` indexes
styles: {
from: number; // The starting index of the text which is styled
to: number; // The ending index of the text which is styled
text: 'bold' | 'italics' | 'strikethrough'; // The type of text styling applied
}[];
// The list of clickable hyperlink URLs identified by the `from` and `to` indexes
urls: {
from: number; // The starting index of the text which is a URL
to: number; // The ending index of the text which is a URL
url: string; // The clickable URL link of the text
}[];
}[];
}
// The `media` component represents some visual media within the article
| {
type: 'media';
// The `image` variant is an image media type
// The `gif` variant is a GIF media type
// The `video` variant is a video media type
variant: 'image' | 'gif' | 'video';
url: string; // The media URL for this variant of the media
thumbnail: string; // The media thumbnail URL for this variant of the media
caption: null | string; // The associated caption text (if any)
}
// The `tweet` component represents an embedded tweet within the article
| {
type: 'tweet';
tweet: {
id: string; // The tweet's snowflake ID
url: string; // The tweet's URL
object: null | TwitterTweet; // The tweet object (May need to be fetched separately if null)
};
}
)[];
};
};
// Contains information about the tweet's metrics
metrics: {
likes: number; // The number of likes on the tweet
quotes: number; // The number of quotes on the tweet
replies: number; // The number of replies on the tweet
retweets: number; // The number of retweets on the tweet
// Contains information about the tweet's advanced metrics
// If this is null, then you need to fetch the expanded version of the tweet to get the advanced metrics
advanced: null | {
views: number; // The number of views on the tweet
};
};
// Community block, present when the tweet belongs to a community (fetched with ?full=true)
community?: {
id: string; // Community snowflake ID
name: string; // Community display name
url: string; // Community URL
};
}
Management API
Get tracked accounts
GET /v1/trackedRetrieve all Twitter accounts you are currently tracking.
{
"success": true, "tier": "normal", "page": 1, "trackedAccounts": [
{
"createdAt": "2025-02-13T12:00:00Z", "twitterId": "44196397", "twitterUsername": "elonmusk"
}
]
}
Get all keys (Hybrid)
GET /v1/keysReturns all keys associated with your account. Essential for hybrid subscriptions to discover both Normal and Ultimate keys.
{
"plan": "hybrid", "keys": [
{
"tier": "normal", "key": "9121dc75-...", "trackedCount": 800, "totalLimit": 1500
}, {
"tier": "ultimate", "key": "a1b2c3d4-...", "trackedCount": 0, "totalLimit": 50
}
]
}
Add tracked accounts
POST /v1/trackedAdd one or more Twitter accounts. You can send multiple accounts as a comma-separated list. Usernames and IDs are resolved automatically.
curl -X POST "https://api.1322.io/v1/tracked" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"identifiers": "elonmusk,@twitter,44196397", "type": "username"}'
{
"success": true, "message": "processed identifiers", "tier": "normal", "results": {
"successful": [
{
"identifier": "elonmusk", "twitterUsername": "elonmusk", "twitterId": "44196397", "trackedAccount": {
"createdAt": "2025-02-13T12:00:00Z", "twitterId": "44196397", "twitterUsername": "elonmusk"
}
}
], "skipped": [], "failed": []
}
}
Remove tracked accounts
DELETE /v1/trackedRemove one or more Twitter accounts from your tracking list.
curl -X DELETE "https://api.1322.io/v1/tracked" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"identifiers": "elonmusk,@twitter", "type": "username"}'
{
"success": true, "message": "processed identifiers", "tier": "normal", "results": {
"successful": [{ "identifier": "elonmusk,@twitter" }], "skipped": [], "failed": []
}
}
Fetch tweet by ID
GET /v1/data/tweet/:idFetch any tweet by its snowflake ID. Use the full or expanded boolean query parameters to request untruncated text. Returns the full TwitterTweet object.
curl "https://api.1322.io/v1/data/tweet/1234567890123456789?full=true" \
-H "X-API-Key: YOUR_API_KEY"
{
"success": true, "tweet": {
"id": "1234567890123456789", "type": "TWEET", "created_at": 1700000000000, "author": {
"id": "44196397", "handle": "elonmusk", "name": "Elon Musk", "avatar": "https://pbs.twimg.com/profile_images/..."
}, "body": {
"text": "Full untruncated tweet text here...", "urls": [], "mentions": []
}, "media": { "images": [], "videos": [], "thumbnails": [] }, "metrics": { "likes": 15200, "quotes": 340, "replies": 2100, "retweets": 4500 }
}
}
Resolve username
GET /v1/resolve/username/:usernameResolve an X username to a Twitter ID. Returns the user's ID, display name, follower count, and account status.
curl "https://api.1322.io/v1/resolve/username/elonmusk" \
-H "X-API-Key: YOUR_API_KEY"
{
"username": "elonmusk", "twitterId": "44196397", "displayName": "Elon Musk", "followers": 210000000, "status": "active"
}
Get API health
GET /v1/healthReturns the health status of the API. No authentication required.
{ "status": "ok" }
Truth Social API & WebSocket
Direct monitoring of Truth Social posts, retruths, quotes, and media streams. Manage tracked accounts, check service health, and receive real-time events via WebSocket.
Quick links
Base URLs
REST API Base
https://truth.1322.ioWebSocket Endpoint
Format
wss://truth.1322.io/{path}?key={ws_key}Path and WS key are provided in your dashboard configuration.
Authentication
All endpoints except /health require the X-Api-Key header with your API key.
WebSocket: Connect with your WS key via the key= query parameter.
Rate limit
Rate limits are per-key and shown in the /v1/limits response. Standard HTTP status codes (401, 403, 429) are used for errors.
Management API
Health Check
GET /healthReturns the health status of the API. No authentication required.
curl "https://truth.1322.io/health"
{ "status": "ok" }
Service Status
GET /v1/statusReturns the current service status including post processing counts and error totals. Authentication required.
curl "https://truth.1322.io/v1/status" \
-H "X-Api-Key: YOUR_API_KEY"
{
"posts_processed": 1234, "errors_total": 0, "status": "running"
}
List Tracked
GET /v1/listList all Truth Social accounts currently being tracked for your server. Authentication required.
curl "https://truth.1322.io/v1/list" \
-H "X-Api-Key: YOUR_API_KEY"
[
{
"handle": "realDonaldTrump", "display_name": "Donald J. Trump", "added_by": "admin", "added_at": "2025-06-15T08:30:00Z"
}, {
"handle": "DanScavino", "display_name": "Dan Scavino", "added_by": "admin", "added_at": "2025-06-15T09:00:00Z"
}
]
Account Limits
GET /v1/limitsReturns your current tracking usage, maximum limits, and rate limits. Authentication required.
curl "https://truth.1322.io/v1/limits" \
-H "X-Api-Key: YOUR_API_KEY"
{
"current_tracked": 3, "max_tracked": 5, "rpm": 60, "tracked": ["danscavino", "elonmusk", "realdonaldtrump"]
}
Track Account
POST /v1/trackAdd a Truth Social account to your tracking list. Send the handle in the JSON body. Authentication required.
curl -X POST "https://truth.1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"handle": "realDonaldTrump"}'
{
"status": "ok", "account": "realDonaldTrump", "uid": "107780257626128497", "display_name": "Donald J. Trump", "tracked": ["danscavino", "elonmusk", "realdonaldtrump"], "current_tracked": 3, "max_tracked": 5
}
Untrack Account
POST /v1/untrackRemove a Truth Social account from your tracking list. Send the handle in the JSON body. Authentication required.
curl -X POST "https://truth.1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"handle": "realDonaldTrump"}'
{
"status": "ok", "account": "realDonaldTrump", "tracked": ["danscavino", "elonmusk"], "current_tracked": 2, "max_tracked": 5
}
WS Data Types
Complete data types delivered via the Truth Social WebSocket stream. Each post event contains the full payload below.
export interface TruthSocialMediaAttachment {
kind: 'image' | 'video' | 'gifv' | 'audio' | string;
url: string;
poster_url: string;
created_at: string;
source?: 'own' | 'quoted' | 'nested_quoted';
}
The source field indicates where the media attachment originates from:
- •
own: Media attached directly to this post. - •
quoted: Media from the post being quoted. - •
nested_quoted: Media from a quote-of-a-quote (the inner quoted post).
export interface TruthSocialAuthor {
id?: string; // original user_id, useful for dedupe
username: string; // handle (e.g. "realDonaldTrump")
display_name?: string; // display name
avatar?: string; // proxied avatar URL (same proxy as user_avatar)
}
export interface TruthSocialPost {
platform: 'truth';
username: string; // on retruths/quotes this is the outer actor
display_name: string;
user_avatar: string;
user_following: number;
user_followers: number;
user_id: string;
key: string;
timestamp: string;
seen_at: string;
text: string;
is_quote?: boolean;
quoted?: {
key: string;
text: string;
url?: string;
author?: TruthSocialAuthor; // NEW - original author of the quoted post
quoted?: {
key: string;
text: string;
url?: string;
author?: TruthSocialAuthor; // NEW - author of the nested quoted post
};
};
is_retruth?: boolean;
retruth_of_id?: string;
retruth_of?: TruthSocialAuthor; // NEW - original author of the retruthed post
card?: {
url: string;
title?: string;
description?: string;
image?: string;
};
media?: TruthSocialMediaAttachment[];
}
Field notes
- •
key: The unique post ID (Mastodon-style snowflake). Use this for deduplication. - •
seen_at: ISO-8601 timestamp of when 1322 first detected the post. - •
is_quote/quoted: Present when the post is quoting another post. Thequotedobject contains the referenced post's key, text, and URL. - •
quoted.quoted: Nested quote support (quote of a quote). The innerquotedobject follows the same shape, enabling two levels of quote resolution. - •
is_retruth/retruth_of_id: Present when the post is a retruth (boost). Theretruth_of_idreferences the original post's key. - •
card: Link preview card attached to the post, containing the URL, title, description, and preview image. - •
media: Array ofTruthSocialMediaAttachmentobjects. Thesourcefield on each item tells you whether the media is from the post itself, a quoted post, or a nested quoted post. - •
retruth_of: Original author of a retruth. The outerusernamestays the retruther. - •
quoted.author/quoted.quoted.author: Original author of the quoted post, and of a quote-of-a-quote. - • Author
avatarURLs are pre-proxied, use them directly in<img src>. A retruth of a quote returnsretruth_ofandquoted.authortogether, so the whole chain is available. - • Compatibility:
retruth_of,quoted.author, andquoted.quoted.authorare new optional fields. Existing clients are unaffected (non-breaking).
Instagram API & WebSocket
Monitor Instagram posts, reels, stories, and carousels in real-time. Manage tracked accounts, check service health, and receive events via WebSocket.
Quick links
Base URLs
REST API Base
https://1322.ioWebSocket Endpoint
wss://1322.io/IG?key={api_key}Authenticate with ?key=<api_key> on the connection URL.
Authentication
Send your API key via header Authorization: Bearer <key> or X-API-Key: <key>.
All endpoints except health require authentication.
Rate limit
Per-key RPM limit returned in the /v1/limits response. Standard HTTP status codes (401, 403, 429) are used for errors.
Management API
Health Check
GET /v1/healthReturns the health status of the API. Authentication required.
curl "https://1322.io/v1/health" \
-H "X-API-Key: YOUR_API_KEY"
{ "ok": true }
Account Limits
GET /v1/limitsReturns your current tracking usage, maximum limits, and request rate limits. Authentication required.
curl "https://1322.io/v1/limits" \
-H "X-API-Key: YOUR_API_KEY"
{
"status": "success", "max_tracked": 10, "current": 3, "rpm": 60
}
List Tracked
GET /v1/listList all Instagram accounts currently being tracked under your subscription. Authentication required.
curl "https://1322.io/v1/list" \
-H "X-API-Key: YOUR_API_KEY"
{
"status": "success", "tracked": ["elonmusk", "nike", "natgeo"], "current": 3, "max_tracked": 10
}
Track Account
POST /v1/trackAdd an Instagram account to tracking. Verifies the account exists before adding. Authentication required.
curl -X POST "https://1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"username": "elonmusk"}'
{
"status": "success", "message": "Now tracking elonmusk", "tracked": ["elonmusk", "nike", "natgeo", "elonmusk"], "current": 4, "max_tracked": 10
}
Error codes
- •
list_full: Your tracking list is at capacity. - •
already_tracked: Account is already being tracked. - •
invalid_user: Account not found or invalid username format. - •
storage_error: Internal storage write failure.
Untrack Account
POST /v1/untrackRemove an Instagram account from your tracking list. Send the username in the JSON body. Authentication required.
curl -X POST "https://1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"username": "elonmusk"}'
{
"status": "success", "message": "Stopped tracking elonmusk", "tracked": ["nike", "natgeo"], "current": 2, "max_tracked": 10
}
WS Data Types
Complete data types delivered via the Instagram WebSocket stream. Each post event contains the full payload below.
export interface InstagramPost {
platform: 'instagram';
username: string;
display_name: string;
user_avatar: string;
user_followers: number;
user_id: string;
key: string; // post id
timestamp: string;
seen_at: string;
text: string;
post_type: 'post' | 'reel' | 'story' | 'carousel';
media_count?: number;
hashtags?: string[];
mentions?: string[]; // includes coauthor usernames even when not @-mentioned
media?: InstagramMediaAttachment[];
is_collab?: boolean; // NEW - coauthor present, or owner != tracked account
collab_with?: string[]; // NEW - other accounts on the collab (excludes tracked)
is_pinned?: boolean; // NEW - pinned to the tracked account's profile
is_paid_partnership?: boolean; // NEW - paid / branded-content flag from IG
location?: { // NEW - place tag when the post has one
name: string;
lat?: number;
lng?: number;
};
}
export interface InstagramMediaAttachment {
// Backward-compatible type: 'image' | 'video'. Video COVER frames stay
// 'image' here so existing clients keep working.
kind: 'image' | 'video';
// Precise type + recommended discriminator (use media_role):
kind_v2?: 'image' | 'video_cover' | 'video';
media_role?: 'image' | 'video_cover' | 'video';
is_video_cover?: boolean; // true only for a video's preview/cover frame
media_index: number; // index within media[]
carousel_index?: number; // original IG carousel index (video + its cover share it)
video_index?: number; // ordinal among video items
cover_media_index?: number; // on a video: matching cover item in media[]
cover_url?: string; // on a video: URL of the matching cover image
covers_video_media_index?: number; // on a cover: matching video item in media[]
covers_video_url?: string; // on a cover: URL of the matching video file
url: string; // CDN/proxy URL (images via wsrv.nl; video via igmedia.1322.io)
width?: number;
height?: number;
created_at: string; // ISO-8601 UTC
}
Field notes
- •
is_collab/collab_with: Set when the post is a collab (a coauthor is present, or the owner differs from the tracked account).collab_withlists the other accounts, excluding the tracked one. - •
is_pinned: True when the tracked account has pinned this post to its profile. - •
is_paid_partnership: Paid / branded-content marker reported by Instagram. - •
location: Place tag from the post when present (name, optionallat/lng). Both mobile and web API shapes are supported. - •
mentionsnow also includes coauthor usernames even when they are not @-mentioned in the caption. - • Compatibility:
is_collab,collab_with,is_pinned,is_paid_partnership, andlocationare new optional fields. Existing clients are unaffected (non-breaking).
News Feed API & WebSocket
Real-time aggregation from 18 major news sources. Articles are processed immediately upon detection with full text extraction, media, and categorization. Delivered via per-server WebSocket streams.
Quick links
Base URLs
REST API Base
https://newsfeed.1322.ioWebSocket Endpoint
Format
wss://newsfeed.1322.io/{ws_path}?key={ws_key}Use the exact ws_url returned by /v1/dashboard.
Path and WS key are provided in your dashboard configuration (e.g. /news_459f182...).
Authentication
All endpoints require the X-Api-Key header with your API key.
WebSocket: Connect with your API key via the key= query parameter.
Rate limit
Per-key rate limits apply. Standard HTTP status codes (401, 403, 429) are used for errors.
Available Feeds (18)
Management API
Dashboard
GET /v1/dashboardReturns full dashboard data including subscribed feeds, service status, and WebSocket connection info. Authentication required.
curl "https://newsfeed.1322.io/v1/dashboard" \
-H "X-Api-Key: YOUR_API_KEY"
{
"status": "ok", "feeds": ["BBC News", "NYPost", "Bloomberg"], "current": 3, "max_feeds": 18, "ws_path": "/v1/ws", "ws_key": "your_ws_key"
}
Subscribe to Feed
POST /v1/subscribeSubscribe to a news feed to receive articles via WebSocket. Send the feed name in the JSON body. Authentication required.
curl -X POST "https://newsfeed.1322.io/v1/subscribe" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"feed": "BBC News"}'
{
"status": "ok", "feed": "BBC News", "feeds": ["BBC News", "NYPost", "Bloomberg"], "current": 3, "max_feeds": 18
}
Unsubscribe from Feed
POST /v1/unsubscribeUnsubscribe from a news feed. Send the feed name in the JSON body. Authentication required.
curl -X POST "https://newsfeed.1322.io/v1/unsubscribe" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"feed": "BBC News"}'
{
"status": "ok", "feed": "BBC News", "feeds": ["NYPost"], "current": 1, "max_feeds": 18
}
WebSocket Connection
WSS /{ws_path}Connect to the real-time article stream using your per-server WebSocket path and key (from your dashboard). Articles from all subscribed feeds are delivered as JSON messages.
// ws_path and ws_key are from your dashboard config (e.g. GET /v1/dashboard)
const ws = new WebSocket("wss://newsfeed.1322.io/{ws_path}?key={ws_key}");
ws.onopen = () => {
console.log("Connected to News Feed stream");
};
ws.onmessage = (event) => {
const article = JSON.parse(event.data);
console.log(`[${article.feed}] ${article.title}`);
};
ws.onclose = () => {
console.log("Disconnected, reconnecting...");
};
WS Data Types
Complete data types delivered via the News Feed WebSocket stream. Each article event contains the full payload below.
export interface WebSocketNewsFeedPayload {
feed: string; // Provider: "BBC News" | "NYPost" | "Fox News" | ...
guid: string; // Unique article ID (typically URL hash)
url: string; // Full article URL
title: string;
publish_time: string; // ISO-8601
modified_time?: string;
primary_category: string;
categories: string[];
author: string;
keywords: string[];
description: string;
summary: string;
media: NewsMediaAttachment[];
full_text: string;
language?: string;
copyright?: string | null;
_event_type: "LIVE_POST";
_sent_time: string; // Dispatch timestamp
}
export interface NewsMediaAttachment {
url: string; // Visual asset URL
type: "image" | "video";
caption?: string;
}
Field notes
- •
feed: The news source name (e.g. "BBC News", "NYPost"). Matches the feed names in the Available Feeds list. - •
guid: Unique article identifier. Use this for deduplication. - •
full_text: Complete extracted article body text. - •
_event_type: Always"LIVE_POST"for real-time article events. - •
_sent_time: ISO-8601 timestamp of when 1322 dispatched the article.
{
"feed": "BBC News", "guid": "https://www.bbc.com/news/videos/cg43xevpvw5o#0", "url": "https://www.bbc.com/news/videos/cg43xevpvw5o", "title": "Flooded streets and tangled power lines", "publish_time": "2025-10-29T04:52:35+00:00", "primary_category": "News", "categories": ["News", "World"], "author": "BBC", "keywords": ["hurricane", "caribbean", "damage"], "summary": "The strongest storm to hit the nation...", "media": [
{
"url": "https://ichef.bbci.co.uk/news/1024/...", "type": "image", "caption": "Flooded streets..."
}
], "full_text": "Full article body text here...", "_event_type": "LIVE_POST", "_sent_time": "2025-10-29T05:35:21.711178+00:00"
}
YouTube API & WebSocket
Track YouTube channels over a simple REST API and stream events over a WebSocket. Base URL https://youtube.1322.io. Every REST call accepts either authentication header — Authorization: Bearer <api_key> or X-Api-Key: <api_key> — so use whichever your HTTP client makes easier. Channels accept a UC… channel ID, an @handle, or a full channel URL.
Quick Start
WebSocketFetch your WebSocket URL and key from /v1/dashboard, then connect to stream upload / upgrade / deletion messages. Your API key, WS URL and WS key are also shown on your dashboard API Access page. The stream takes the WS key either in the query string or as a header — one or the other, not both.
// GET /v1/dashboard returns a complete ws_url. Query-string form:
wss://youtube.1322.io/{ws_path}?key={ws_key}
// Header form, for clients that can set headers on the handshake:
// X-WS-KEY: {ws_key}
wss://youtube.1322.io/{ws_path}
Service Status
GET /v1/statusReturns tracker health rather than account state: the service status, how many posts have been processed, a total error count, and a feed_errors object holding a per-feed-provider error breakdown. Authentication required. For your plan, term and tracked list, use /v1/dashboard instead.
curl "https://youtube.1322.io/v1/status" \
-H "X-Api-Key: YOUR_API_KEY"
{
"status": "ok",
"posts_processed": 1284,
"errors_total": 3,
"feed_errors": { }
}
Dashboard & Tracked List
GET /v1/dashboardReturns your account status, term, slot usage, the channels you currently track, and your WebSocket URL + key. Authentication required. The response is flat — every field sits at the top level and the tracked list is tracked. YouTube returns a complete ws_url here, so no path assembly is needed. (YouTube lists tracked channels on this response as well as on /v1/list; there is no /v1/tracked.)
curl "https://youtube.1322.io/v1/dashboard" \
-H "X-Api-Key: YOUR_API_KEY"
{
"server_id": "yt_xxxxxxxx",
"status": "active",
"is_lifetime": false,
"expires_at": "2026-09-20T00:00:00Z",
"days_remaining": 26,
"max_tracked": 50,
"current": 1,
"current_tracked": 1,
"remaining_slots": 49,
"rpm": 60,
"ws_enabled": true,
"ws_url": "wss://youtube.1322.io/yt_xxxxxxxx",
"ws_key": "YOUR_WS_KEY",
"discord_enabled": false,
"tracked": [
{ "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "name": "MrBeast", "added_at": "2026-08-01T09:14:00Z" }
]
}
Account Limits
GET /v1/limitsReturns your current tracking usage, maximum limit, and rate limit. Authentication required.
curl "https://youtube.1322.io/v1/limits" \
-H "X-Api-Key: YOUR_API_KEY"
{
"current_tracked": 1, "max_tracked": 50, "rpm": 60
}
List Tracked
GET /v1/listReturns the YouTube channels currently tracked on your account on their own, as the same channel_id / name / added_at rows that /v1/dashboard embeds under tracked. Use it when you want the watchlist without the account and WebSocket fields. Authentication required.
curl "https://youtube.1322.io/v1/list" \
-H "X-Api-Key: YOUR_API_KEY"
Add Channel
POST /v1/trackAdd a YouTube channel to your tracking list. Send channel in the JSON body — a UC-prefixed channel ID, an @handle, or a channel URL (all resolve to the same channel). Authentication required.
curl -X POST "https://youtube.1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"channel": "@MrBeast"}'
{
"status": "ok",
"channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
"tracked": ["UCX6OQ3DkcsbYNE6H8uQQuVA"],
"max_tracked": 50
}
Remove Channel
POST /v1/untrackRemove a YouTube channel from your tracking list. Send channel in the JSON body (UC id, @handle, or URL). Authentication required.
curl -X POST "https://youtube.1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"channel": "UCX6OQ3DkcsbYNE6H8uQQuVA"}'
{
"status": "ok",
"channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
"tracked": [],
"max_tracked": 50
}
WebSocket Status
GET /v1/ws/statusReports the state of your stream — whether WebSocket delivery is enabled on your account and how it is currently being used. This is a REST call, so it authenticates with your API key; the connection details themselves (ws_url and ws_key) come from /v1/dashboard. Authentication required.
curl "https://youtube.1322.io/v1/ws/status" \
-H "X-Api-Key: YOUR_API_KEY"
Health & Reference
GET /v1/health/v1/health is a liveness check — point uptime monitors and load-balancer probes at it instead of calling an authenticated endpoint on a timer. /v1/docs serves the tracker's own reference, and /openapi.json serves the machine-readable schema for generating a client.
curl "https://youtube.1322.io/v1/health"
curl "https://youtube.1322.io/v1/docs"
curl "https://youtube.1322.io/openapi.json"
YouTube Events architecture
The 1322 YouTube tracker emits three message types over one stream. They are not a guaranteed three-message sequence per video: upload is the primary event, upgrade is conditional and most videos never produce one, and deletion is an independent later event. Branch on type and treat every message as standalone.
Message Types
New Upload (type: "upload") — primary
The event to build on. Sent on detection of a new video, carrying the channel, the video, and the images resolvable at that moment. subtype separates a video from a short.
Thumbnail Upgrade (type: "upgrade") — conditional
Sent only when a later thumbnail retry improves an image that was still unconfirmed at upload time. Most videos never generate one, so nothing should wait for it. When it does arrive, match upgrade.video_id to the upload you already rendered and swap the image.
Deletion (type: "deletion") — independent
An independent later event, emitted once a previously detected video is explicitly confirmed unavailable. It is not the closing step of a per-video sequence and may follow an upload after any interval, or never at all.
Build your consumer around upload on its own. A video that receives neither an upgrade nor a deletion is the normal case, so no render path should block waiting on either one.
export type YouTubeTrackerMessage =
| UploadMessage
| UpgradeMessage
| DeletionMessage;
// PRIMARY. Sent for every detected video — the one message you can rely on.
export interface UploadMessage {
type: "upload";
subtype: "video" | "short";
detected_at: string; // ISO-8601, when 1322 detected the video
// Every channel field is string | null.
channel: {
id: string | null;
name: string | null;
url: string | null;
avatar: string | null;
};
video: {
id: string;
url: string;
title: string | null;
description: string | null;
published_at: string | null;
updated_at: string | null;
view_count?: number;
duration_display?: string;
metadata?: Record<string, unknown>;
};
// Every images field is string | null. "chosen" is the best image
// available at detection time.
images: {
seed: string | null;
chosen: string | null;
thumbnail_small: string | null;
};
}
// CONDITIONAL. Sent only when a later thumbnail retry improves an image that
// was still unconfirmed at upload time. Most videos never produce one, so do
// not treat this as a step that always follows an upload.
export interface UpgradeMessage {
type: "upgrade";
detected_at: string;
upgrade: {
kind: "image";
video_id: string; // match to an upload you already rendered
url: string; // replacement image
};
}
// INDEPENDENT. Emitted after a previously detected video is explicitly
// confirmed unavailable — at any later time, or never.
export interface DeletionMessage {
type: "deletion";
detected_at: string;
video: {
id: string; // the deleted video's ID
url: string; // original video URL
};
channel: {
id: string | null;
name: string | null;
url: string | null;
avatar: string | null;
};
}
Binance Square API & WebSocket
Real-time tracking of Binance Square creators. Monitors posts, pin changes, polls, quotes, media, sentiment (bullish/bearish), coin pairs, and engagement metrics. Delivered via WebSocket with per-tenant isolation.
Quick links
Base URLs
REST API Base
https://binance.1322.ioWebSocket Endpoint
Format
wss://binance.1322.io/{ws_path}?key={ws_key}Path and WS key are provided in your dashboard configuration.
Authentication
All endpoints require the X-Api-Key header with your API key.
WebSocket: Connect with your API key via the key= query parameter.
Rate limit
Per-key rate limits apply. Standard HTTP status codes (401, 403, 429) are used for errors.
Management API
Dashboard
GET /v1/dashboardReturns full dashboard data including tracked accounts, service status, and WebSocket connection info. Authentication required.
curl "https://binance.1322.io/v1/dashboard" \
-H "X-Api-Key: YOUR_API_KEY"
{
"status": "ok", "tracked": ["CZ_Binance", "binaborz"], "current": 2, "max_tracked": 10, "ws_path": "/v1/ws", "ws_key": "your_ws_key"
}
Track Account
POST /v1/trackAdd a Binance Square user to your tracking list. Send the username in the JSON body. Authentication required.
curl -X POST "https://binance.1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"username": "CZ_Binance"}'
{
"status": "ok", "tracked": {
"username": "CZ_Binance", "square_uid": "123456789", "display_name": "CZ", "avatar_url": "https://...", "added_at": "2025-01-15T12:00:00Z"
}
}
Untrack Account
POST /v1/untrackRemove a Binance Square user from your tracking list. Send the username in the JSON body. Authentication required.
curl -X POST "https://binance.1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{"username": "CZ_Binance"}'
{
"status": "ok", "account": "CZ_Binance", "tracked": ["binaborz"], "current": 1, "max_tracked": 10
}
WebSocket Connection
WSS /{ws_path}Connect to the real-time post stream using your per-server WebSocket path and key (from your dashboard). Posts and pin changes from all tracked accounts are delivered as JSON messages.
// ws_path and ws_key are from your dashboard config (e.g. GET /v1/dashboard)
const ws = new WebSocket("wss://binance.1322.io/{ws_path}?key={ws_key}");
ws.onopen = () => {
console.log("Connected to Binance Square stream");
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "new_post") {
console.log(`[${data.post.username}] ${data.post.text}`);
} else if (data.type === "pin_change") {
console.log(`Pin change for ${data.pin.username}`);
}
};
ws.onclose = () => {
console.log("Disconnected, reconnecting...");
};
WS Data Types
Complete data types delivered via the Binance Square WebSocket stream. Each event contains the full payload below.
export interface BinancePost {
id: string;
username: string;
display_name?: string;
avatar_url?: string;
square_uid?: string;
content_type?: string; // "post" | "video" | "space" | "article"
title?: string;
text?: string;
web_link?: string;
share_link?: string;
is_reply: boolean;
parent_id?: string;
// Engagement
reply_count?: number;
like_count?: number;
comment_count?: number;
share_count?: number;
view_count?: number;
// Sentiment
tendency?: string; // "bullish" | "bearish" | ""
bullish_ratio?: number;
bearish_ratio?: number;
coin_pairs?: string[]; // ["BTCUSDT", "ETHUSDT"]
hashtags?: string[];
mentions?: string[];
// Rich content
media?: MediaItem[];
poll?: PostPoll;
quote?: PostQuote; // Embedded quote/repost
reply_to?: PostQuote; // Parent post context
translation?: PostTranslation;
live_replay?: SpaceLiveReplay;
resolved_links?: PostResolvedLink[];
published_at: string; // ISO 8601
detected_at: string; // ISO 8601
}
// Types referenced by BinancePost. quote, reply_to and nested all share PostQuote.
export interface PostQuote { // the QUOTED or REPLIED-TO post (the parent)
id?: string;
username?: string;
display_name?: string;
avatar_url?: string; // author image, when present
text?: string;
link?: string;
published_at?: string; // ISO 8601, when Binance provides it
tendency?: string; // "bullish" | "bearish" | ""
bullish_ratio?: number;
bearish_ratio?: number;
coin_pairs?: string[];
hashtags?: string[];
mentions?: string[];
links?: PostLink[];
media?: MediaItem[];
poll?: PostPoll;
nested?: PostQuote; // quote-of-quote, when present
translation?: PostTranslation;
}
export interface MediaItem {
kind: "image" | "video" | "gif" | string;
url: string;
preview?: string; // poster / thumbnail
mime_type?: string;
width?: number;
height?: number;
duration_s?: number;
scope?: "post" | "quote" | "quote_nested" | "reply_to"; // which card it belongs to
}
export interface PostPoll {
question?: string;
total_votes?: number;
ends_at?: string; // ISO 8601
closed?: boolean;
options?: { label?: string; value?: string; votes?: number; ratio?: number }[];
}
export interface PostTranslation {
body?: string;
source_language?: string;
target_language?: string;
need_translate?: boolean;
}
export interface PostLink { url: string; kind?: string; domain?: string; label?: string; }
export interface PostResolvedLink { kind: string; label: string; url: string; raw?: string; }
export interface SpaceLiveReplay {
title?: string;
duration_s?: number;
replay_url?: string;
status?: number;
live_type?: number;
live_status?: number;
view_count?: number;
audio_web_url?: string;
content_type?: string;
}
// On a reply: the AUTHOR and their reply text are the main card;
// the post they replied to is under reply_to (a PostQuote).
{
"type": "binance.post",
"data": {
"id": "356349125025858",
"username": "cz_binance",
"display_name": "CZ BNB",
"content_type": "post",
"is_reply": true,
"parent_id": "900000000000000021",
"text": "Building for the long term.",
"reply_to": {
"id": "900000000000000021",
"username": "some_user",
"display_name": "Some User",
"avatar_url": "https://public.bnbstatic.com/static/content/square/images/avatar.png",
"text": "The parent post being replied to",
"link": "https://www.binance.com/en/square/post/900000000000000021",
"published_at": "2026-08-16T14:20:00Z"
},
"reply_count": 3,
"published_at": "2026-08-16T14:28:23Z",
"detected_at": "2026-08-16T14:28:23Z"
}
}
// Every WS frame is { type, data }. Branch on type.
export type WsEventType = "binance.post" | "binance.pin.update";
export interface WsEnvelope {
type: WsEventType;
data: BinancePost | PinUpdate;
}
export interface PinUpdate {
username: string;
display_name?: string;
avatar_url?: string;
square_uid?: string;
added?: BinancePost[]; // newly pinned posts
removed?: BinancePost[]; // unpinned posts
removed_ids?: string[];
note?: string; // hint only; confirm on Square if needed
detected_at: string; // RFC3339 event time
}
Field notes
- •
content_type: The type of post,"post","video","space", or"article". - •
tendency: Sentiment indicator,"bullish","bearish", or empty string. - •
coin_pairs: Array of trading pairs mentioned (e.g.["BTCUSDT", "ETHUSDT"]). - •
pin: Present onpin_changeevents. Contains arrays of added/removed pinned posts. - •
detected_at: ISO-8601 timestamp of when 1322 first detected the post.
TikTok API & WebSocket
Real-time monitoring of TikTok creator accounts. Tracked creators emit new uploads, reposts, live starts, live ends, and a media-ready frame once a tracked video has a fetchable file. Video posts, photo posts and carousels are all covered, and every content frame carries a kind telling you which one it is. Creators are added by username or sec_uid. Clients linked to a Discord server get Discord bot delivery alongside the API and WebSocket; clients without one use the API and WebSocket only.
Quick links
Base URLs
REST API Base
https://tiktok.1322.ioWebSocket Endpoint
Format
wss://tiktok.1322.io{ws_path}The path is opaque and account-specific — a /ws/<32-hex> value. Read it from GET /v1/ws/status (path) or GET /v1/dashboard (websocket.path). There is no query string: authenticate with a header, never by appending a key to the URL.
Handshake failures return before the upgrade: 401 wrong or missing key, 403 expired tenant, WebSocket disabled, or origin denied, 404 wrong path, 429 connection limit reached. A successful handshake is 101.
Authentication
TikTok issues two separate keys. REST uses your API key. WebSocket uses a separate WebSocket key. Do not mix them.
REST — every endpoint except the public health and readiness probes, either header:X-API-Key: <api_key>Authorization: Bearer <api_key>
WebSocket — either header:X-WS-Key: <ws_key>Authorization: Bearer <ws_key>
Sending the API key where the WebSocket key belongs returns 401. Query-string keys are not enabled on either side.
Rate limit
Per-key rate limits apply. Standard HTTP status codes (401, 403, 429) are used for errors.
WebSocket: up to 16 concurrent connections per account and 8 per IP.
Management API
Service Status
GET /v1/statusReturns your client record: status, tracked count against your maximum, and which delivery channels are enabled. Authentication required. client.expires_at is an RFC3339 timestamp that appears once a term has been set; it is absent — not null — on a client with no term yet, which is a real state and not a missing feature.
curl "https://tiktok.1322.io/v1/status" \
-H "X-API-Key: YOUR_API_KEY"
{
"client": {
"client_id": "tt_1a2b3c4d",
"name": "Example Desk",
"status": "active",
"max_accounts": 60,
"tracked_count": 2,
"api_enabled": true,
"websocket_enabled": true,
"discord_enabled": false
},
"ready": true
}
Dashboard
GET /v1/dashboardReturns everything in one call: your client record, your limits, every tracked creator, and your WebSocket path. Authentication required. The response is nested — there is no flat tenant_id or ws_url at the top level, and the tracked list is tracks, not tracked.
curl "https://tiktok.1322.io/v1/dashboard" \
-H "X-API-Key: YOUR_API_KEY"
{
"client": {
"client_id": "tt_1a2b3c4d",
"name": "Example Desk",
"status": "active",
"expires_at": "2026-09-20T00:00:00Z",
"max_accounts": 60,
"tracked_count": 2,
"api_enabled": true,
"websocket_enabled": true,
"discord_enabled": false
},
"limits": {
"accounts": { "maximum": 60, "current": 2, "remaining": 58 },
"ws_max_connections_per_ip": 8,
"ws_max_connections": 16
},
"tracks": [
{
"track_id": "trk_8f21",
"username": "examplecreator",
"sec_uid": "MS4wLjABAAAA...",
"events": ["upload", "repost", "live"],
"state": "active",
"ws_delivery": true,
"discord_delivery": false
}
],
"websocket": {
"enabled": true,
"path": "/ws/3f9c1d7e5a2b48c6910d4e7f2a6b8c31",
"current_connections": 0,
"unique_ips": 0,
"max_connections": 16,
"max_per_ip": 8
}
}
List Tracked
GET /v1/listLists every TikTok creator currently tracked on your account, with the event types and delivery routes enabled for each. Authentication required.
curl "https://tiktok.1322.io/v1/list" \
-H "X-API-Key: YOUR_API_KEY"
{
"tracks": [
{
"track_id": "trk_8f21",
"username": "examplecreator",
"sec_uid": "MS4wLjABAAAA...",
"events": ["upload", "repost", "live"],
"state": "active",
"ws_delivery": true,
"discord_delivery": false
},
{
"track_id": "trk_44c0",
"username": "anothercreator",
"sec_uid": "MS4wLjABAAAB...",
"events": ["upload"],
"state": "active",
"ws_delivery": true,
"discord_delivery": false
}
],
"count": 2
}
Single Tracked Creator
GET /v1/tracks/{id}Returns one tracked creator by the track_id returned from /v1/track or /v1/list. Authentication required.
curl "https://tiktok.1322.io/v1/tracks/trk_8f21" \
-H "X-API-Key: YOUR_API_KEY"
{
"track_id": "trk_8f21",
"username": "examplecreator",
"sec_uid": "MS4wLjABAAAA...",
"events": ["upload", "repost", "live"],
"state": "active",
"ws_delivery": true,
"discord_delivery": false
}
Account Limits
GET /v1/limitsReturns your creator slot usage and your WebSocket connection ceilings. Authentication required.
curl "https://tiktok.1322.io/v1/limits" \
-H "X-API-Key: YOUR_API_KEY"
{
"accounts": { "maximum": 60, "current": 2, "remaining": 58 },
"ws_max_connections_per_ip": 8,
"ws_max_connections": 16
}
Track Creator
POST /v1/trackAdd a TikTok creator to your tracking list. Send username or sec_uid in the JSON body. Authentication required.
curl -X POST "https://tiktok.1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"username": "examplecreator"}'
{
"status": "ok",
"message": "Now tracking examplecreator",
"track_id": "trk_8f21",
"username": "examplecreator",
"sec_uid": "MS4wLjABAAAA...",
"tracked": ["examplecreator", "anothercreator"],
"current": 2,
"max_accounts": 60
}
Untrack Creator
POST /v1/untrackRemove a TikTok creator from your tracking list. Send username, sec_uid, or track_id in the JSON body. Authentication required.
curl -X POST "https://tiktok.1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"username": "examplecreator"}'
{
"status": "ok",
"message": "Stopped tracking examplecreator",
"track_id": "trk_8f21",
"username": "examplecreator",
"current": 1,
"max_accounts": 60
}
WebSocket Status
GET /v1/ws/statusReturns whether the stream is enabled, your opaque connection path, and how many connections you are using against your ceiling. This is a REST call, so it authenticates with your API key; the stream itself authenticates with the separate WebSocket key.
curl "https://tiktok.1322.io/v1/ws/status" \
-H "X-API-Key: YOUR_API_KEY"
{
"enabled": true,
"path": "/ws/3f9c1d7e5a2b48c6910d4e7f2a6b8c31",
"current_connections": 0,
"unique_ips": 0,
"max_connections": 16,
"max_per_ip": 8
}
// path comes from GET /v1/ws/status (path) or GET /v1/dashboard (websocket.path).
// Auth is the WS KEY, not the REST API key, and it goes in a header —
// there is no ?key= query parameter. Either header works:
// X-WS-Key: <ws_key>
// Authorization: Bearer <ws_key>
const ws = new WebSocket("wss://tiktok.1322.io/ws/3f9c1d7e5a2b48c6910d4e7f2a6b8c31", {
headers: { "X-WS-Key": "YOUR_WS_KEY" },
});
ws.onmessage = (event) => {
const e = JSON.parse(event.data);
if (e.type === "tiktok.upload.created") {
console.log(`[${e.account.username}] ${e.object?.url}`);
}
};
ws.onclose = () => {
console.log("Disconnected, reconnecting...");
};
Media Download
GET /v1/media/{video_id}.mp4Downloads the video file for a tracked video. Request it after the tiktok.media.ready frame for that video_id arrives, rather than retrying before the file exists — that frame also carries the same file as object.media_url. Returns the media file, not JSON. Authentication required.
curl "https://tiktok.1322.io/v1/media/7300000000000000000.mp4" \
-H "X-API-Key: YOUR_API_KEY" \
-o 7300000000000000000.mp4
Health & Readiness
GET /healthzFour public probes, none of which take a key: /health, /healthz and /v1/health report liveness, and /readyz reports readiness. Point uptime monitors and load-balancer probes at these rather than polling an authenticated endpoint on a timer.
curl "https://tiktok.1322.io/healthz"
curl "https://tiktok.1322.io/readyz"
curl "https://tiktok.1322.io/v1/health"
WS Data Types
There are two frame schemas. Content events (uploads, reposts, LIVE start/end) arrive as 1322.tiktok.event.v1. The media-ready frame is a different, smaller schema — 1322.tiktok.media.v1. Branch on type.
export type TiktokEventType =
| "tiktok.upload.created" // creator posted a new video, photo or carousel
| "tiktok.repost.created" // creator reposted someone else's video
| "tiktok.live.started" // creator went live
| "tiktok.live.ended"; // live session ended
export interface TiktokEvent {
schema: "1322.tiktok.event.v1";
event_id: string; // unique per event — use for dedup
sequence?: number; // monotonic per stream, when present
type: TiktokEventType;
platform: "tiktok";
account: {
sec_uid?: string;
username?: string;
display_name?: string;
verified?: true; // present only when verified; absent otherwise
};
object?: TiktokEventObject;
// Uploads carry a timestamp. Reposts and LIVE frames send null —
// sort on observed_at if you need a total order across every type.
occurred_at?: string | null; // ISO-8601, when TikTok published it
observed_at: string; // ISO-8601, when 1322 emitted the event
delivery?: { mode: "live_only" }; // live stream only — no backfill or replay
}
// What the post actually is, so you can branch without inspecting media.
export type ContentKind = "video" | "photo" | "carousel";
export interface MusicInfo {
title?: string;
author_name?: string;
original?: true; // the creator's own sound, not a licensed track
duration?: number; // seconds
}
export interface TiktokEventObject {
// upload / repost
video_id?: string;
url?: string;
kind?: ContentKind;
title?: string;
description?: string;
cover_url?: string;
avatar_url?: string;
// Parsed out of the description so you do not have to. hashtags arrive
// WITHOUT the leading #, mentions WITHOUT the leading @.
mentions?: string[];
hashtags?: string[];
duration?: number; // video length in seconds
music?: MusicInfo;
// Photo posts and carousels carry images instead of a video.
image_urls?: string[];
// Playable file. There is NO play_url on a tenant frame — the raw TikTok
// play_url is never included, so reading it always yields undefined.
// media_url arrives on the sibling tiktok.media.ready frame.
media_url?: string;
// On tiktok.repost.created: the reposted creator. This is an OBJECT,
// not a username string — printing it directly gives "[object Object]".
original_author?: {
sec_uid?: string;
username?: string;
display_name?: string;
avatar_url?: string;
verified?: true; // present only when verified; absent otherwise
};
// live.started / live.ended
room_id?: string;
previous_state?: string;
current_state?: string;
session_generation?: number;
}
// A separate, smaller schema — no account, no sequence. It repeats an
// already-announced event_id once the media is fetchable; it is NOT a
// second post, so do not render it as a new item in a feed.
export interface TiktokMediaReadyEvent {
schema: "1322.tiktok.media.v1";
event_id: string; // matches the content event this media belongs to
type: "tiktok.media.ready";
platform: "tiktok";
observed_at: string;
object: {
video_id?: string;
url?: string;
media_url?: string; // the playable/downloadable file
};
}
{
"schema": "1322.tiktok.event.v1",
"event_id": "01J9Z8QX4T7C2V1M0K3R5B6N7P",
"sequence": 4821,
"type": "tiktok.upload.created",
"platform": "tiktok",
"account": {
"username": "examplecreator",
"display_name": "Example Creator",
"sec_uid": "MS4wLjABAAAA...",
"verified": true
},
"object": {
"video_id": "7300000000000000000",
"url": "https://www.tiktok.com/@examplecreator/video/7300000000000000000",
"kind": "video",
"title": "new video",
"description": "new video @friendcreator #launch #demo",
"cover_url": "https://.../cover.jpg",
"mentions": ["friendcreator"],
"hashtags": ["launch", "demo"],
"duration": 27,
"music": {
"title": "original sound - examplecreator",
"author_name": "Example Creator",
"original": true,
"duration": 27
}
},
"occurred_at": "2026-08-22T14:03:11Z",
"observed_at": "2026-08-22T14:03:12Z",
"delivery": { "mode": "live_only" }
}
Field notes
- •
event_id: unique per event. Use it to dedup across reconnects. - •
account: identifies the tracked creator.sec_uidis stable across username changes. - •
verified: onaccountandoriginal_author. It is present astrueonly when the creator is verified and is absent otherwise — test for presence, not forfalse. - •
original_author: only ontiktok.repost.created— the creator whose video was reposted. It is an object (sec_uid,username,display_name,avatar_url,verified), not a string. Readoriginal_author.username. - •
kind:"video","photo"or"carousel". Branch on it to pick a renderer instead of guessing from which media fields happen to be populated. - •
image_urls: photo posts and carousels carry images instead of a video. Check it before assumingvideo_idmeans a playable clip. - •
mentions/hashtags: string arrays parsed out ofdescription, so you do not have to. Hashtags arrive without the leading#and mentions without the leading@— add the symbol back yourself when rendering. - •
duration/music/title: video length in seconds, the sound attached to the post (title,author_name,original,duration), and the post title where TikTok carries one separately fromdescription.music.originalistrueonly for a creator's own sound. - • No
play_url: tenant frames never carry the raw TikTok play URL. Usemedia_urlfrom thetiktok.media.readyframe. - •
occurred_at: uploads carry a timestamp; reposts and LIVE frames sendnull. Sort onobserved_atfor a total order. - •
tiktok.live.started/tiktok.live.endedshare aroom_id, so an alert can be opened and closed on the same session. - •
tiktok.media.readyarrives on its own schema and repeats an already-announcedevent_id— match it to the original event rather than rendering a second post. The file is at/v1/media/{video_id}.mp4. - •
delivery.mode:"live_only"— events are not queued while your client is disconnected. Reconnect with backoff.