Write Operations
Operations for creating, modifying, and interacting with Twitter content.
Note: All write operations require Twitter API credentials and always use the Twitter API v2, regardless of the configured mode. Web scraping is not supported for write operations due to security and reliability concerns.
uploadMedia
Upload media (images, videos, GIFs) to Twitter for use in tweets and replies.
Description
Uploads media files to Twitter and returns a media ID that can be attached to tweets. Supports images (PNG, JPG, GIF), videos (MP4), and animated GIFs. Optional accessibility text can be added for screen readers.
Note: Media must be uploaded separately before attaching to tweets. Media IDs expire after 24 hours if not used.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
options | MediaUploadOptions | Yes | Media upload configuration |
MediaUploadOptions Interface
| Property | Type | Required | Description |
|---|---|---|---|
filePath | string | No* | Path to media file on disk |
buffer | Buffer | No* | Raw buffer data of media file |
mediaType | 'image' | 'video' | 'gif' | No | Media type (auto-detected if not provided) |
altText | string | No | Accessibility text for screen readers (max 1000 chars) |
*Either filePath or buffer must be provided.
Returns
| Type | Description |
|---|---|
Promise<MediaUploadResult> | Media upload result with ID and type |
MediaUploadResult Interface
| Property | Type | Description |
|---|---|---|
mediaId | string | Twitter media ID for attaching to tweets |
mediaType | string | Media type: 'photo', 'video', or 'animated_gif' |
Constraints
- Images: Max 5MB, formats: PNG, JPG, WEBP, GIF (static)
- Videos: Max 512MB, formats: MP4, MOV
- Animated GIFs: Max 15MB
- Alt text: Max 1000 characters
- Max attachments per tweet: 4 images OR 1 video/GIF
Errors
| Error Type | Description |
|---|---|
MediaUploadError | Media upload failed |
InvalidMediaError | Invalid media format or size |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Upload Image from File
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const media = await client.uploadMedia({
filePath: './images/chart.png',
altText: 'Bar chart showing cryptocurrency prices over time'
});
console.log('Media uploaded:', media.mediaId);
console.log('Media type:', media.mediaType); // 'photo'Upload Image from Buffer
import fs from 'fs';
const imageBuffer = fs.readFileSync('./logo.png');
const media = await client.uploadMedia({
buffer: imageBuffer,
mediaType: 'image',
altText: 'Company logo'
});
console.log('Uploaded media ID:', media.mediaId);Upload Video
const video = await client.uploadMedia({
filePath: './videos/demo.mp4',
mediaType: 'video',
altText: 'Product demo video showing key features'
});
console.log('Video uploaded:', video.mediaId);Upload Multiple Images
const imagePaths = [
'./screenshot1.png',
'./screenshot2.png',
'./screenshot3.png',
'./screenshot4.png'
];
const mediaIds: string[] = [];
for (const path of imagePaths) {
const media = await client.uploadMedia({
filePath: path,
altText: `Screenshot ${mediaIds.length + 1}`
});
mediaIds.push(media.mediaId);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log(`Uploaded ${mediaIds.length} images`);Error Handling
try {
const media = await client.uploadMedia({
filePath: './image.png'
});
console.log('Success:', media.mediaId);
} catch (error) {
if (error instanceof InvalidMediaError) {
console.error('Invalid media file:', error.message);
} else if (error instanceof MediaUploadError) {
console.error('Upload failed:', error.message);
} else {
console.error('Unexpected error:', error);
}
}Best Practices
- Optimize Media - Compress images and videos before uploading
- Add Alt Text - Always include accessibility text for images
- Validate Format - Check file format and size before uploading
- Handle Errors - Media uploads can fail, implement retry logic
- Rate Limit - Add delays between multiple uploads
- Use Media IDs Quickly - Media IDs expire after 24 hours
Related
postTweet
Create a new tweet with optional media, polls, or quote tweets.
Description
Posts a new tweet to Twitter. Supports text, media attachments, polls, quote tweets, and reply threads. Returns the created tweet's ID and text.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PostTweetOptions | Yes | Tweet creation options |
PostTweetOptions Interface
| Property | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Tweet text (max 280 characters) |
mediaIds | string[] | No | Uploaded media IDs to attach (max 4 images or 1 video) |
replyToTweetId | string | No | Tweet ID to reply to (creates a thread) |
quoteTweetId | string | No | Tweet ID to quote |
poll | PollOptions | No | Poll configuration |
dryRun | boolean | No | Test mode - validate without posting (default: false) |
PollOptions Interface
| Property | Type | Required | Description |
|---|---|---|---|
options | string[] | Yes | Poll choices (2-4 options) |
durationMinutes | number | Yes | Poll duration in minutes (5-10080) |
Returns
| Type | Description |
|---|---|
Promise<PostTweetResult> | Created tweet information |
PostTweetResult Interface
| Property | Type | Description |
|---|---|---|
id | string | Created tweet ID |
text | string | Tweet text content |
Constraints
- Text: 1-280 characters (required)
- Media: Max 4 images OR 1 video/GIF
- Poll options: 2-4 choices, max 25 characters each
- Poll duration: 5 minutes to 7 days (10080 minutes)
- Cannot combine: Polls with media attachments
Errors
| Error Type | Description |
|---|---|
TweetTooLongError | Text exceeds 280 characters |
DuplicateContentError | Twitter rejected duplicate tweet |
ValidationError | Invalid parameters or constraints violated |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Basic Tweet
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.postTweet({
text: 'Just launched our new Web3 SDK! Check it out 🚀'
});
console.log('Tweet posted!');
console.log('Tweet ID:', result.id);
console.log('URL:', `https://x.com/status/${result.id}`);Tweet with Image
// Upload image first
const media = await client.uploadMedia({
filePath: './chart.png',
altText: 'Price chart showing growth'
});
// Post tweet with image
const result = await client.postTweet({
text: 'Here are our Q4 results! 📊',
mediaIds: [media.mediaId]
});
console.log('Tweet with image posted:', result.id);Tweet with Multiple Images
// Upload multiple images
const media1 = await client.uploadMedia({ filePath: './img1.png' });
const media2 = await client.uploadMedia({ filePath: './img2.png' });
const media3 = await client.uploadMedia({ filePath: './img3.png' });
// Post tweet with all images
const result = await client.postTweet({
text: 'Product showcase thread 🧵',
mediaIds: [media1.mediaId, media2.mediaId, media3.mediaId]
});
console.log('Tweet with 3 images posted!');Tweet with Video
const video = await client.uploadMedia({
filePath: './demo.mp4',
mediaType: 'video',
altText: 'Product demo video'
});
const result = await client.postTweet({
text: 'Watch our product demo! 🎥',
mediaIds: [video.mediaId]
});
console.log('Tweet with video posted:', result.id);Tweet with Poll
const result = await client.postTweet({
text: 'Which blockchain do you prefer? 🗳️',
poll: {
options: ['Ethereum', 'Solana', 'Polygon', 'Other'],
durationMinutes: 1440 // 24 hours
}
});
console.log('Poll tweet posted:', result.id);Quote Tweet
const result = await client.postTweet({
text: 'Great insight! This aligns with our findings.',
quoteTweetId: '1234567890123456789'
});
console.log('Quote tweet posted:', result.id);Thread (Reply to Own Tweet)
// Post first tweet
const tweet1 = await client.postTweet({
text: '1/ Thread about Web3 development 🧵'
});
// Wait a moment
await new Promise(resolve => setTimeout(resolve, 2000));
// Reply to create thread
const tweet2 = await client.postTweet({
text: '2/ First, let\'s talk about smart contracts...',
replyToTweetId: tweet1.id
});
await new Promise(resolve => setTimeout(resolve, 2000));
const tweet3 = await client.postTweet({
text: '3/ Next, consider gas optimization...',
replyToTweetId: tweet2.id
});
console.log('Thread created!');
console.log('View:', `https://x.com/status/${tweet1.id}`);Dry Run (Test Mode)
// Test tweet without actually posting
const result = await client.postTweet({
text: 'Test tweet - will not be posted',
dryRun: true
});
console.log('Dry run successful!');
console.log('Mock ID:', result.id); // Returns mock dataError Handling
try {
const result = await client.postTweet({
text: 'Hello Twitter!'
});
console.log('Success:', result.id);
} catch (error) {
if (error instanceof TweetTooLongError) {
console.error(`Tweet too long: ${error.details.length} chars`);
} else if (error instanceof DuplicateContentError) {
console.error('Duplicate tweet detected');
} else if (error instanceof RateLimitError) {
console.error('Rate limited! Wait until:', error.resetTime);
} else {
console.error('Unexpected error:', error);
}
}Rate Limits
Twitter API v2 rate limits:
- User context: 300 tweets per 3 hours
- Daily limit: 2400 tweets per day
Best Practices
- Validate Length - Check text length before posting
- Avoid Duplicates - Don't post identical content repeatedly
- Rate Limit Protection - Add delays between posts
- Use Dry Run - Test with dryRun: true before production
- Handle Errors - Implement retry logic for transient failures
- Optimize Media - Compress images/videos before uploading
Related
- uploadMedia - Upload media for tweets
- postReply - Reply to existing tweets
- deleteTweet - Delete your tweets
postReply
Post a reply to an existing tweet with optional media attachments.
Description
Posts a reply to a specified tweet. The reply appears in the tweet's conversation thread and notifies the original author. Supports text and media attachments.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PostReplyOptions | Yes | Reply options |
PostReplyOptions Interface
| Property | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to reply to |
text | string | Yes | Reply text (max 280 characters) |
mediaIds | string[] | No | Uploaded media IDs to attach (max 4 images or 1 video) |
dryRun | boolean | No | Test mode - validate without posting (default: false) |
Returns
| Type | Description |
|---|---|
Promise<PostTweetResult> | Created reply information |
Constraints
- Text: 1-280 characters (required)
- Media: Max 4 images OR 1 video/GIF
- Parent tweet: Must exist and be accessible
Errors
| Error Type | Description |
|---|---|
TweetTooLongError | Text exceeds 280 characters |
NotFoundError | Parent tweet not found or deleted |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Basic Reply
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.postReply({
tweetId: '1234567890123456789',
text: 'Great insight! Thanks for sharing 🚀'
});
console.log('Reply posted:', result.id);Reply with Image
// Upload image
const media = await client.uploadMedia({
filePath: './chart.png',
altText: 'Supporting data chart'
});
// Post reply with image
const result = await client.postReply({
tweetId: '1234567890123456789',
text: 'Here are the numbers to back this up:',
mediaIds: [media.mediaId]
});
console.log('Reply with image posted:', result.id);Reply with Mentions
const result = await client.postReply({
tweetId: '1234567890123456789',
text: '@vitalikbuterin @gavinwood What are your thoughts on this?'
});
console.log('Reply with mentions posted!');Auto-Reply Bot
async function autoReplyBot() {
// Search for mentions
const mentions = await client.searchTweets('@yourbotname', {
maxResults: 10,
filter: 'latest'
});
for (const tweet of mentions) {
// Skip if already replied
if (await hasAlreadyReplied(tweet.id)) {
continue;
}
try {
const replyText = generateReply(tweet.text);
await client.postReply({
tweetId: tweet.id,
text: replyText
});
console.log(`Replied to ${tweet.author.username}`);
await markAsReplied(tweet.id);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 5000));
} catch (error) {
console.error(`Failed to reply to ${tweet.id}:`, error.message);
}
}
}
function generateReply(tweetText: string): string {
if (tweetText.toLowerCase().includes('help')) {
return 'Hi! Here are some helpful resources: https://docs.example.com';
}
return 'Thanks for reaching out! 👋';
}
// Run bot every 5 minutes
setInterval(autoReplyBot, 5 * 60 * 1000);Best Practices
- Validate Input - Check text length and parent tweet exists
- Avoid Spam - Don't auto-reply to everything
- Rate Limit Protection - Add delays between replies
- Track Replied Tweets - Avoid duplicate replies
- Use Webhooks - Monitor rate limits via webhooks
Related
- uploadMedia - Upload media for replies
- searchTweets - Find tweets to reply to
likeTweet
Like (favorite) a tweet.
Description
Adds a like to the specified tweet. The like is attributed to the authenticated user and appears publicly on their profile.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to like |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Like operation result |
EngagementResult Interface
| Property | Type | Description |
|---|---|---|
success | boolean | Whether operation succeeded |
tweetId | string | Tweet ID that was liked |
Errors
| Error Type | Description |
|---|---|
NotFoundError | Tweet not found or deleted |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Like a Tweet
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.likeTweet('1234567890123456789');
if (result.success) {
console.log('Tweet liked successfully!');
}Auto-Like Relevant Tweets
async function autoLikeBot() {
// Search for relevant tweets
const tweets = await client.searchTweets('Web3 OR blockchain', {
maxResults: 20,
filter: 'latest'
});
for (const tweet of tweets) {
// Filter for quality content
if (tweet.metrics.likes > 10 && tweet.metrics.retweets > 2) {
try {
await client.likeTweet(tweet.id);
console.log(`Liked: ${tweet.text.substring(0, 50)}...`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to like ${tweet.id}:`, error.message);
}
}
}
}
await autoLikeBot();Rate Limits
Twitter API v2 rate limits:
- User context: 1000 likes per 24 hours
Best Practices
- Rate Limit - Add delays between likes (3-5 seconds)
- Avoid Spam - Don't like everything indiscriminately
- Quality Filter - Like tweets that meet quality criteria
- Track Liked Tweets - Store tweet IDs to avoid duplicate likes
Related
- unlikeTweet - Remove like from tweet
- retweet - Retweet a tweet
unlikeTweet
Remove a like (unfavorite) from a tweet.
Description
Removes a like from the specified tweet that was previously liked by the authenticated user.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to unlike |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unlike operation result |
Examples
Unlike a Tweet
const result = await client.unlikeTweet('1234567890123456789');
if (result.success) {
console.log('Tweet unliked successfully!');
}Related
- likeTweet - Like a tweet
retweet
Retweet (share) a tweet to your followers.
Description
Retweets the specified tweet, sharing it to all your followers. The retweet appears on your profile timeline.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to retweet |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Retweet operation result |
Errors
| Error Type | Description |
|---|---|
NotFoundError | Tweet not found or deleted |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Retweet a Tweet
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.retweet('1234567890123456789');
if (result.success) {
console.log('Tweet retweeted successfully!');
}Auto-Retweet Quality Content
async function autoRetweetBot() {
const tweets = await client.searchTweets('from:vitalikbuterin OR from:ethereum', {
maxResults: 10,
filter: 'latest'
});
for (const tweet of tweets) {
// Only retweet high-quality tweets
if (tweet.metrics.likes > 100) {
try {
await client.retweet(tweet.id);
console.log(`Retweeted: ${tweet.text.substring(0, 50)}...`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 5000));
} catch (error) {
console.error(`Failed to retweet ${tweet.id}:`, error.message);
}
}
}
}
await autoRetweetBot();Rate Limits
Twitter API v2 rate limits:
- User context: 300 retweets per 3 hours
Best Practices
- Rate Limit - Add delays between retweets (5+ seconds)
- Curate Content - Only retweet quality, relevant content
- Avoid Spam - Don't retweet indiscriminately
- Track Retweets - Store tweet IDs to avoid duplicates
Related
unretweet
Remove a retweet from your timeline.
Description
Removes a retweet that was previously shared by the authenticated user.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to unretweet |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unretweet operation result |
Examples
Unretweet a Tweet
const result = await client.unretweet('1234567890123456789');
if (result.success) {
console.log('Retweet removed successfully!');
}Related
- retweet - Retweet a tweet
followUser
Follow a Twitter user.
Description
Follows the specified user. The follow relationship allows you to see their tweets in your timeline and enables direct messaging.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to follow (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Follow operation result |
EngagementResult Interface
| Property | Type | Description |
|---|---|---|
success | boolean | Whether operation succeeded |
userId | string | User ID that was followed |
Errors
| Error Type | Description |
|---|---|
NotFoundError | User not found |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Follow a User
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.followUser('vitalikbuterin');
if (result.success) {
console.log('Successfully followed @vitalikbuterin!');
}Follow Multiple Users
const usersToFollow = [
'vitalikbuterin',
'ethereum',
'gavinwood'
];
for (const username of usersToFollow) {
try {
await client.followUser(username);
console.log(`Followed @${username}`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to follow @${username}:`, error.message);
}
}Auto-Follow KOLs
// Find influential users in your niche
const kols = await client.identifyKOLs('Web3 development');
for (const kol of kols.slice(0, 10)) {
if (kol.metrics.followersCount > 10000) {
try {
await client.followUser(kol.username);
console.log(`Followed KOL: @${kol.username} (${kol.metrics.followersCount} followers)`);
await new Promise(resolve => setTimeout(resolve, 5000));
} catch (error) {
console.error(`Failed to follow @${kol.username}:`, error.message);
}
}
}Rate Limits
Twitter API v2 rate limits:
- User context: 400 follows per 24 hours
Best Practices
- Rate Limit - Add delays between follows (3-5 seconds minimum)
- Daily Limit - Stay well below 400 follows per day
- Quality Over Quantity - Follow relevant, quality accounts
- Avoid Spam - Don't use aggressive follow/unfollow tactics
- Track Follows - Store usernames to manage relationships
Related
- unfollowUser - Unfollow a user
- getFollowing - Get following list
unfollowUser
Unfollow a Twitter user.
Description
Unfollows the specified user, removing them from your following list and their tweets from your timeline.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to unfollow (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unfollow operation result |
Examples
Unfollow a User
const result = await client.unfollowUser('username');
if (result.success) {
console.log('Successfully unfollowed @username!');
}Cleanup Inactive Follows
// Get accounts you follow
const following = await client.getFollowing('your_username', {
maxResults: 1000
});
// Unfollow inactive accounts (no tweets in 6+ months)
const sixMonthsAgo = Date.now() - (180 * 24 * 60 * 60 * 1000);
for (const user of following) {
const tweets = await client.getUserTweets(user.username, {
maxResults: 1
});
if (tweets.length === 0 || tweets[0].createdAt < new Date(sixMonthsAgo)) {
try {
await client.unfollowUser(user.username);
console.log(`Unfollowed inactive user: @${user.username}`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to unfollow @${user.username}:`, error.message);
}
}
}Best Practices
- Rate Limit - Add delays between unfollows (3-5 seconds)
- Be Selective - Only unfollow accounts that no longer provide value
- Avoid Churn - Don't use aggressive follow/unfollow tactics
Related
- followUser - Follow a user
- getFollowing - Get following list
deleteTweet
Delete one of your tweets.
Description
Permanently deletes a tweet that was posted by the authenticated user. The tweet cannot be recovered after deletion.
Note: This operation requires API credentials and always uses Twitter API v2. You can only delete your own tweets.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to delete |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<DeleteTweetResult> | Delete operation result |
DeleteTweetResult Interface
| Property | Type | Description |
|---|---|---|
success | boolean | Whether deletion succeeded |
deletedTweetId | string | ID of deleted tweet |
Errors
| Error Type | Description |
|---|---|
NotFoundError | Tweet not found or already deleted |
AuthenticationError | Cannot delete tweet (not yours or no permission) |
RateLimitError | API rate limit exceeded |
Examples
Delete a Tweet
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.deleteTweet('1234567890123456789');
if (result.success) {
console.log('Tweet deleted successfully!');
}Delete Old Tweets
// Get your recent tweets
const tweets = await client.getUserTweets('your_username', {
maxResults: 100
});
// Delete tweets older than 1 year
const oneYearAgo = Date.now() - (365 * 24 * 60 * 60 * 1000);
for (const tweet of tweets) {
if (tweet.createdAt < new Date(oneYearAgo)) {
try {
await client.deleteTweet(tweet.id);
console.log(`Deleted old tweet: ${tweet.text.substring(0, 50)}...`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error(`Failed to delete tweet ${tweet.id}:`, error.message);
}
}
}Clean Up Low-Performing Tweets
const tweets = await client.getUserTweets('your_username', {
maxResults: 50
});
// Delete tweets with low engagement
for (const tweet of tweets) {
const engagement = tweet.metrics.likes + tweet.metrics.retweets;
if (engagement < 5 && tweet.createdAt < new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)) {
try {
await client.deleteTweet(tweet.id);
console.log(`Deleted low-engagement tweet: ${tweet.text.substring(0, 40)}...`);
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error(`Failed to delete tweet ${tweet.id}:`, error.message);
}
}
}Best Practices
- Confirm Before Delete - Deletion is permanent
- Rate Limit - Add delays between deletes (2+ seconds)
- Backup First - Export tweet data before bulk deletion
- Be Selective - Consider editing or leaving instead of deleting
Related
- postTweet - Create new tweets
- getUserTweets - Get your tweets
bookmarkTweet
Bookmark a tweet for later reference.
Description
Adds a tweet to your private bookmarks collection. Bookmarks are visible only to you and provide a way to save tweets for later reading.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to bookmark |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Bookmark operation result |
Errors
| Error Type | Description |
|---|---|
NotFoundError | Tweet not found or deleted |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Bookmark a Tweet
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.bookmarkTweet('1234567890123456789');
if (result.success) {
console.log('Tweet bookmarked successfully!');
}Auto-Bookmark Quality Content
const tweets = await client.searchTweets('Web3 tutorial', {
maxResults: 20,
filter: 'top'
});
for (const tweet of tweets) {
if (tweet.metrics.likes > 100) {
try {
await client.bookmarkTweet(tweet.id);
console.log(`Bookmarked: ${tweet.text.substring(0, 50)}...`);
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error(`Failed to bookmark ${tweet.id}:`, error.message);
}
}
}Best Practices
- Rate Limit - Add delays between bookmarks (2+ seconds)
- Organize Later - Use bookmarks as a temporary save mechanism
- Regular Cleanup - Review and remove old bookmarks periodically
Related
- unbookmarkTweet - Remove bookmark
unbookmarkTweet
Remove a tweet from your bookmarks.
Description
Removes a tweet from your private bookmarks collection.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tweetId | string | Yes | Tweet ID to unbookmark |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unbookmark operation result |
Examples
Unbookmark a Tweet
const result = await client.unbookmarkTweet('1234567890123456789');
if (result.success) {
console.log('Bookmark removed successfully!');
}Related
- bookmarkTweet - Add bookmark
blockUser
Block a Twitter user.
Description
Blocks the specified user, preventing them from following you, seeing your tweets, or interacting with you on Twitter.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to block (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Block operation result |
Errors
| Error Type | Description |
|---|---|
NotFoundError | User not found |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Block a User
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.blockUser('spamaccount');
if (result.success) {
console.log('User blocked successfully!');
}Auto-Block Spam Accounts
// Search for spam patterns
const tweets = await client.searchTweets('@youraccount', {
maxResults: 50,
filter: 'latest'
});
for (const tweet of tweets) {
const isSpam =
tweet.text.toLowerCase().includes('click here') ||
tweet.text.toLowerCase().includes('free money') ||
tweet.author.metrics.followersCount < 10;
if (isSpam) {
try {
await client.blockUser(tweet.author.username);
console.log(`Blocked spam account: @${tweet.author.username}`);
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to block @${tweet.author.username}:`, error.message);
}
}
}Best Practices
- Use Sparingly - Reserve blocking for genuine abuse or spam
- Rate Limit - Add delays between blocks (3+ seconds)
- Review First - Verify before blocking to avoid false positives
Related
- unblockUser - Unblock a user
- muteUser - Mute instead of block
unblockUser
Unblock a Twitter user.
Description
Unblocks a previously blocked user, allowing them to follow you and interact with your content again.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to unblock (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unblock operation result |
Examples
Unblock a User
const result = await client.unblockUser('username');
if (result.success) {
console.log('User unblocked successfully!');
}Related
- blockUser - Block a user
muteUser
Mute a Twitter user.
Description
Mutes the specified user, hiding their tweets from your timeline while still allowing them to follow you and interact with your content. Muting is private - the muted user is not notified.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to mute (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Mute operation result |
Errors
| Error Type | Description |
|---|---|
NotFoundError | User not found |
RateLimitError | API rate limit exceeded |
AuthenticationError | API credentials invalid or missing |
Examples
Mute a User
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient();
const result = await client.muteUser('noisyaccount');
if (result.success) {
console.log('User muted successfully!');
}Mute Inactive Followers
const following = await client.getFollowing('your_username', {
maxResults: 500
});
for (const user of following) {
const tweets = await client.getUserTweets(user.username, {
maxResults: 50
});
// Mute if they post too frequently (more than 20 tweets/day avg)
const avgTweetsPerDay = tweets.length / 7;
if (avgTweetsPerDay > 20) {
try {
await client.muteUser(user.username);
console.log(`Muted high-volume account: @${user.username}`);
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to mute @${user.username}:`, error.message);
}
}
}Best Practices
- Prefer Mute Over Block - Muting is less aggressive than blocking
- Rate Limit - Add delays between mutes (3+ seconds)
- Temporary Solution - Consider muting as temporary, review periodically
Related
- unmuteUser - Unmute a user
- blockUser - Block instead of mute
unmuteUser
Unmute a Twitter user.
Description
Unmutes a previously muted user, allowing their tweets to appear in your timeline again.
Note: This operation requires API credentials and always uses Twitter API v2.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username to unmute (with or without @) |
dryRun | boolean | No | Test mode (default: false) |
Returns
| Type | Description |
|---|---|
Promise<EngagementResult> | Unmute operation result |
Examples
Unmute a User
const result = await client.unmuteUser('username');
if (result.success) {
console.log('User unmuted successfully!');
}Related
- muteUser - Mute a user
Common Patterns
Rate Limit Handling
All write operations handle rate limits automatically with webhook notifications:
try {
await client.postTweet({ text: 'Hello!' });
} catch (error) {
if (error instanceof RateLimitError) {
console.error('Rate limited until:', error.resetTime);
console.error('Used:', error.used, '/', error.limit);
// Wait until reset
const waitMs = error.resetTime.getTime() - Date.now();
await new Promise(resolve => setTimeout(resolve, waitMs));
// Retry
await client.postTweet({ text: 'Hello!' });
}
}Dry Run Testing
Test write operations without actually posting:
// Test without posting
const result = await client.postTweet({
text: 'Test tweet',
dryRun: true
});
console.log('Dry run successful:', result.id); // Mock IDBatch Operations with Rate Limiting
async function batchLike(tweetIds: string[]) {
for (const tweetId of tweetIds) {
try {
await client.likeTweet(tweetId);
console.log(`Liked ${tweetId}`);
// Rate limit protection
await new Promise(resolve => setTimeout(resolve, 3000));
} catch (error) {
console.error(`Failed to like ${tweetId}:`, error.message);
if (error instanceof RateLimitError) {
// Wait until reset and retry
const waitMs = error.resetTime.getTime() - Date.now();
await new Promise(resolve => setTimeout(resolve, waitMs));
// Retry this tweet
await client.likeTweet(tweetId);
}
}
}
}Media Upload and Post
async function postTweetWithImage(text: string, imagePath: string) {
// Upload image
const media = await client.uploadMedia({
filePath: imagePath,
altText: 'Descriptive alt text for accessibility'
});
// Post tweet with image
const result = await client.postTweet({
text,
mediaIds: [media.mediaId]
});
return result;
}
await postTweetWithImage('Check out this chart!', './chart.png');Configuration Requirements
All write operations require Twitter API credentials:
import { XTwitterClient } from '@blockchain-web-services/bws-x-sdk-node';
const client = new XTwitterClient({
mode: 'hybrid',
// API credentials required for write operations
api: {
accounts: [{
name: 'main',
apiKey: process.env.TWITTER_API_KEY,
apiSecret: process.env.TWITTER_API_SECRET,
accessToken: process.env.TWITTER_ACCESS_TOKEN,
accessSecret: process.env.TWITTER_ACCESS_SECRET,
}]
},
// Crawler for read operations (optional)
crawler: {
accounts: [/* crawler accounts */]
}
});Environment Variables:
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET=your_api_secret
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_SECRET=your_access_secretError Handling Best Practices
import {
TweetTooLongError,
DuplicateContentError,
MediaUploadError,
RateLimitError,
AuthenticationError,
NotFoundError
} from '@blockchain-web-services/bws-x-sdk-node';
try {
await client.postTweet({ text: 'Hello Twitter!' });
} catch (error) {
if (error instanceof TweetTooLongError) {
console.error(`Text too long: ${error.details.length} chars`);
} else if (error instanceof DuplicateContentError) {
console.error('Duplicate tweet detected');
} else if (error instanceof MediaUploadError) {
console.error('Media upload failed:', error.message);
} else if (error instanceof RateLimitError) {
console.error('Rate limited until:', error.resetTime);
} else if (error instanceof AuthenticationError) {
console.error('API credentials invalid:', error.message);
} else if (error instanceof NotFoundError) {
console.error('Resource not found:', error.message);
} else {
console.error('Unexpected error:', error);
}
}