Twitter API
Integrate the Twitter/X API v2 into TypeScript applications. Covers OAuth 2.0
You are a Twitter/X API integration specialist who builds applications using the v2 endpoints. You handle OAuth 2.0 PKCE flows, compose and search tweets, manage filtered streams, and work within Twitter's tiered rate limits using the `twitter-api-v2` library. ## Key Points - **Using OAuth 1.0a for new apps** - OAuth 2.0 PKCE is the modern standard; 1.0a is complex and error-prone. - **Not requesting `offline.access` scope** - without it you get no refresh token and users must re-auth when the token expires. - **Polling search instead of using filtered streams** - streams are real-time and more efficient; search has tight rate limits. - **Ignoring tweet field selection** - default v2 responses only include `id` and `text`; you must explicitly request metrics, dates, and expansions. - Building social media dashboards that display tweet metrics and engagement data. - Creating bots that post automated updates or respond to mentions. - Monitoring brand mentions or hashtags in real-time using filtered streams. - Building tools that analyze tweet sentiment or engagement patterns. - Implementing "Sign in with Twitter" for applications targeting social media users. ## Quick Example ```bash npm install twitter-api-v2 ``` ```bash TWITTER_CLIENT_ID=your-oauth2-client-id TWITTER_CLIENT_SECRET=your-oauth2-client-secret TWITTER_CALLBACK_URL=http://localhost:3000/auth/twitter/callback TWITTER_BEARER_TOKEN=your-bearer-token # for app-only auth ```
skilldb get oauth-social-services-skills/Twitter APIFull skill: 179 linesTwitter/X API v2 Integration
You are a Twitter/X API integration specialist who builds applications using the v2 endpoints. You handle OAuth 2.0 PKCE flows, compose and search tweets, manage filtered streams, and work within Twitter's tiered rate limits using the twitter-api-v2 library.
Core Philosophy
Use API v2 and OAuth 2.0 PKCE
Twitter's v1.1 endpoints are deprecated for most use cases. Always use API v2 endpoints which return cleaner JSON with expansions and field selection. For user-context authentication, use OAuth 2.0 with PKCE (Proof Key for Code Exchange) instead of OAuth 1.0a. PKCE eliminates the need for a client secret on the authorization step, making it suitable for public clients while remaining secure for server apps.
Understand Access Levels and Rate Limits
Twitter's API has Free, Basic, and Pro tiers with dramatically different rate limits. Free tier allows only 1,500 tweets/month for posting and 500K tweets/month for reading. Always check x-rate-limit-remaining and x-rate-limit-reset headers. Implement automatic wait-and-retry when approaching limits. Cache search results aggressively since search endpoints have the tightest quotas.
Request Only Needed Fields
API v2 returns minimal data by default. Use tweet.fields, user.fields, and expansions parameters to request exactly what you need. This reduces payload size and avoids unnecessary data processing. For example, getting author details requires expansions=author_id plus user.fields=name,username,profile_image_url rather than a separate user lookup call.
Setup
Install
npm install twitter-api-v2
Environment Variables
TWITTER_CLIENT_ID=your-oauth2-client-id
TWITTER_CLIENT_SECRET=your-oauth2-client-secret
TWITTER_CALLBACK_URL=http://localhost:3000/auth/twitter/callback
TWITTER_BEARER_TOKEN=your-bearer-token # for app-only auth
Key Patterns
1. OAuth 2.0 PKCE Flow - Do use PKCE for user authentication
import { TwitterApi } from "twitter-api-v2";
// Step 1: Generate auth link
const client = new TwitterApi({ clientId: process.env.TWITTER_CLIENT_ID! });
const { url, codeVerifier, state } = client.generateOAuth2AuthLink(
process.env.TWITTER_CALLBACK_URL!,
{ scope: ["tweet.read", "tweet.write", "users.read", "offline.access"] }
);
// Store codeVerifier and state in session, redirect user to url
// Step 2: Handle callback
async function handleCallback(code: string, codeVerifier: string) {
const client = new TwitterApi({ clientId: process.env.TWITTER_CLIENT_ID! });
const { accessToken, refreshToken } = await client.loginWithOAuth2({
code,
codeVerifier,
redirectUri: process.env.TWITTER_CALLBACK_URL!,
});
// Store refreshToken securely for later use
return { accessToken, refreshToken };
}
2. App-Only Bearer Token - Do use for read-only public data
const appClient = new TwitterApi(process.env.TWITTER_BEARER_TOKEN!);
const readOnly = appClient.v2.readOnly;
// Search recent tweets (last 7 days)
async function searchTweets(query: string) {
const result = await readOnly.search(query, {
"tweet.fields": ["created_at", "public_metrics", "author_id"],
"user.fields": ["name", "username"],
expansions: ["author_id"],
max_results: 10,
});
return result;
}
3. Rate Limit Handling - Do not ignore rate limit headers
import { ApiResponseError } from "twitter-api-v2";
async function safeTweet(client: TwitterApi, text: string) {
try {
return await client.v2.tweet(text);
} catch (err) {
if (err instanceof ApiResponseError && err.rateLimitError) {
const resetAt = err.rateLimit?.reset ?? 0;
const waitMs = resetAt * 1000 - Date.now();
console.log(`Rate limited. Retry after ${Math.ceil(waitMs / 1000)}s`);
await new Promise((r) => setTimeout(r, Math.max(waitMs, 1000)));
return client.v2.tweet(text);
}
throw err;
}
}
Common Patterns
Post a Tweet with User Context
async function postTweet(accessToken: string, text: string) {
const userClient = new TwitterApi(accessToken);
const { data } = await userClient.v2.tweet(text);
return data; // { id, text }
}
Filtered Stream (Real-Time)
async function startStream(bearerToken: string) {
const client = new TwitterApi(bearerToken);
// Set stream rules
await client.v2.updateStreamRules({
add: [
{ value: "typescript lang:en -is:retweet", tag: "ts-tweets" },
{ value: "#nodejs -is:retweet", tag: "node-tweets" },
],
});
const stream = await client.v2.searchStream({
"tweet.fields": ["created_at", "public_metrics"],
expansions: ["author_id"],
});
stream.autoReconnect = true;
stream.on("data", (event) => {
console.log(`Tweet: ${event.data.text}`);
});
stream.on("error", (err) => console.error("Stream error:", err));
}
Get User Profile and Tweets
async function getUserTimeline(client: TwitterApi, username: string) {
const user = await client.v2.userByUsername(username, {
"user.fields": ["public_metrics", "description"],
});
const timeline = await client.v2.userTimeline(user.data.id, {
"tweet.fields": ["created_at", "public_metrics"],
max_results: 10,
exclude: ["retweets", "replies"],
});
return { user: user.data, tweets: timeline.data.data };
}
Anti-Patterns
- Using OAuth 1.0a for new apps - OAuth 2.0 PKCE is the modern standard; 1.0a is complex and error-prone.
- Not requesting
offline.accessscope - without it you get no refresh token and users must re-auth when the token expires. - Polling search instead of using filtered streams - streams are real-time and more efficient; search has tight rate limits.
- Ignoring tweet field selection - default v2 responses only include
idandtext; you must explicitly request metrics, dates, and expansions.
When to Use
- Building social media dashboards that display tweet metrics and engagement data.
- Creating bots that post automated updates or respond to mentions.
- Monitoring brand mentions or hashtags in real-time using filtered streams.
- Building tools that analyze tweet sentiment or engagement patterns.
- Implementing "Sign in with Twitter" for applications targeting social media users.
Install this skill directly: skilldb add oauth-social-services-skills
Related Skills
Discord Bot
Build Discord bots using discord.js with slash commands, embeds, permissions,
Github OAUTH
Integrate GitHub OAuth Apps and the GitHub API into TypeScript applications.
Google OAUTH
Integrate Google OAuth 2.0 and Google APIs into TypeScript applications.
Linkedin API
Integrate the LinkedIn API into TypeScript applications. Covers OAuth 2.0
Notion API
Integrate the Notion API into TypeScript applications. Covers OAuth
Slack API
Build Slack integrations using the Bolt framework in TypeScript. Covers