Skip to content

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

ParameterTypeRequiredDescription
optionsMediaUploadOptionsYesMedia upload configuration

MediaUploadOptions Interface

PropertyTypeRequiredDescription
filePathstringNo*Path to media file on disk
bufferBufferNo*Raw buffer data of media file
mediaType'image' | 'video' | 'gif'NoMedia type (auto-detected if not provided)
altTextstringNoAccessibility text for screen readers (max 1000 chars)

*Either filePath or buffer must be provided.

Returns

TypeDescription
Promise<MediaUploadResult>Media upload result with ID and type

MediaUploadResult Interface

PropertyTypeDescription
mediaIdstringTwitter media ID for attaching to tweets
mediaTypestringMedia 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 TypeDescription
MediaUploadErrorMedia upload failed
InvalidMediaErrorInvalid media format or size
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Upload Image from File

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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

  1. Optimize Media - Compress images and videos before uploading
  2. Add Alt Text - Always include accessibility text for images
  3. Validate Format - Check file format and size before uploading
  4. Handle Errors - Media uploads can fail, implement retry logic
  5. Rate Limit - Add delays between multiple uploads
  6. Use Media IDs Quickly - Media IDs expire after 24 hours

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

ParameterTypeRequiredDescription
optionsPostTweetOptionsYesTweet creation options

PostTweetOptions Interface

PropertyTypeRequiredDescription
textstringYesTweet text (max 280 characters)
mediaIdsstring[]NoUploaded media IDs to attach (max 4 images or 1 video)
replyToTweetIdstringNoTweet ID to reply to (creates a thread)
quoteTweetIdstringNoTweet ID to quote
pollPollOptionsNoPoll configuration
dryRunbooleanNoTest mode - validate without posting (default: false)

PollOptions Interface

PropertyTypeRequiredDescription
optionsstring[]YesPoll choices (2-4 options)
durationMinutesnumberYesPoll duration in minutes (5-10080)

Returns

TypeDescription
Promise<PostTweetResult>Created tweet information

PostTweetResult Interface

PropertyTypeDescription
idstringCreated tweet ID
textstringTweet 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 TypeDescription
TweetTooLongErrorText exceeds 280 characters
DuplicateContentErrorTwitter rejected duplicate tweet
ValidationErrorInvalid parameters or constraints violated
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Basic Tweet

typescript
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

typescript
// 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

typescript
// 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

typescript
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

typescript
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

typescript
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)

typescript
// 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)

typescript
// 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 data

Error Handling

typescript
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

  1. Validate Length - Check text length before posting
  2. Avoid Duplicates - Don't post identical content repeatedly
  3. Rate Limit Protection - Add delays between posts
  4. Use Dry Run - Test with dryRun: true before production
  5. Handle Errors - Implement retry logic for transient failures
  6. Optimize Media - Compress images/videos before uploading

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

ParameterTypeRequiredDescription
optionsPostReplyOptionsYesReply options

PostReplyOptions Interface

PropertyTypeRequiredDescription
tweetIdstringYesTweet ID to reply to
textstringYesReply text (max 280 characters)
mediaIdsstring[]NoUploaded media IDs to attach (max 4 images or 1 video)
dryRunbooleanNoTest mode - validate without posting (default: false)

Returns

TypeDescription
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 TypeDescription
TweetTooLongErrorText exceeds 280 characters
NotFoundErrorParent tweet not found or deleted
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Basic Reply

typescript
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

typescript
// 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

typescript
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

typescript
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

  1. Validate Input - Check text length and parent tweet exists
  2. Avoid Spam - Don't auto-reply to everything
  3. Rate Limit Protection - Add delays between replies
  4. Track Replied Tweets - Avoid duplicate replies
  5. Use Webhooks - Monitor rate limits via webhooks

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to like
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Like operation result

EngagementResult Interface

PropertyTypeDescription
successbooleanWhether operation succeeded
tweetIdstringTweet ID that was liked

Errors

Error TypeDescription
NotFoundErrorTweet not found or deleted
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Like a Tweet

typescript
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

typescript
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

  1. Rate Limit - Add delays between likes (3-5 seconds)
  2. Avoid Spam - Don't like everything indiscriminately
  3. Quality Filter - Like tweets that meet quality criteria
  4. Track Liked Tweets - Store tweet IDs to avoid duplicate likes

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to unlike
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unlike operation result

Examples

Unlike a Tweet

typescript
const result = await client.unlikeTweet('1234567890123456789');

if (result.success) {
  console.log('Tweet unliked successfully!');
}

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to retweet
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Retweet operation result

Errors

Error TypeDescription
NotFoundErrorTweet not found or deleted
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Retweet a Tweet

typescript
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

typescript
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

  1. Rate Limit - Add delays between retweets (5+ seconds)
  2. Curate Content - Only retweet quality, relevant content
  3. Avoid Spam - Don't retweet indiscriminately
  4. Track Retweets - Store tweet IDs to avoid duplicates

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to unretweet
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unretweet operation result

Examples

Unretweet a Tweet

typescript
const result = await client.unretweet('1234567890123456789');

if (result.success) {
  console.log('Retweet removed successfully!');
}

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

ParameterTypeRequiredDescription
usernamestringYesUsername to follow (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Follow operation result

EngagementResult Interface

PropertyTypeDescription
successbooleanWhether operation succeeded
userIdstringUser ID that was followed

Errors

Error TypeDescription
NotFoundErrorUser not found
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Follow a User

typescript
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

typescript
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

typescript
// 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

  1. Rate Limit - Add delays between follows (3-5 seconds minimum)
  2. Daily Limit - Stay well below 400 follows per day
  3. Quality Over Quantity - Follow relevant, quality accounts
  4. Avoid Spam - Don't use aggressive follow/unfollow tactics
  5. Track Follows - Store usernames to manage relationships

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

ParameterTypeRequiredDescription
usernamestringYesUsername to unfollow (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unfollow operation result

Examples

Unfollow a User

typescript
const result = await client.unfollowUser('username');

if (result.success) {
  console.log('Successfully unfollowed @username!');
}

Cleanup Inactive Follows

typescript
// 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

  1. Rate Limit - Add delays between unfollows (3-5 seconds)
  2. Be Selective - Only unfollow accounts that no longer provide value
  3. Avoid Churn - Don't use aggressive follow/unfollow tactics

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to delete
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<DeleteTweetResult>Delete operation result

DeleteTweetResult Interface

PropertyTypeDescription
successbooleanWhether deletion succeeded
deletedTweetIdstringID of deleted tweet

Errors

Error TypeDescription
NotFoundErrorTweet not found or already deleted
AuthenticationErrorCannot delete tweet (not yours or no permission)
RateLimitErrorAPI rate limit exceeded

Examples

Delete a Tweet

typescript
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

typescript
// 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

typescript
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

  1. Confirm Before Delete - Deletion is permanent
  2. Rate Limit - Add delays between deletes (2+ seconds)
  3. Backup First - Export tweet data before bulk deletion
  4. Be Selective - Consider editing or leaving instead of deleting

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to bookmark
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Bookmark operation result

Errors

Error TypeDescription
NotFoundErrorTweet not found or deleted
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Bookmark a Tweet

typescript
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

typescript
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

  1. Rate Limit - Add delays between bookmarks (2+ seconds)
  2. Organize Later - Use bookmarks as a temporary save mechanism
  3. Regular Cleanup - Review and remove old bookmarks periodically

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

ParameterTypeRequiredDescription
tweetIdstringYesTweet ID to unbookmark
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unbookmark operation result

Examples

Unbookmark a Tweet

typescript
const result = await client.unbookmarkTweet('1234567890123456789');

if (result.success) {
  console.log('Bookmark removed successfully!');
}

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

ParameterTypeRequiredDescription
usernamestringYesUsername to block (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Block operation result

Errors

Error TypeDescription
NotFoundErrorUser not found
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Block a User

typescript
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

typescript
// 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

  1. Use Sparingly - Reserve blocking for genuine abuse or spam
  2. Rate Limit - Add delays between blocks (3+ seconds)
  3. Review First - Verify before blocking to avoid false positives

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

ParameterTypeRequiredDescription
usernamestringYesUsername to unblock (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unblock operation result

Examples

Unblock a User

typescript
const result = await client.unblockUser('username');

if (result.success) {
  console.log('User unblocked successfully!');
}

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

ParameterTypeRequiredDescription
usernamestringYesUsername to mute (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Mute operation result

Errors

Error TypeDescription
NotFoundErrorUser not found
RateLimitErrorAPI rate limit exceeded
AuthenticationErrorAPI credentials invalid or missing

Examples

Mute a User

typescript
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

typescript
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

  1. Prefer Mute Over Block - Muting is less aggressive than blocking
  2. Rate Limit - Add delays between mutes (3+ seconds)
  3. Temporary Solution - Consider muting as temporary, review periodically

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

ParameterTypeRequiredDescription
usernamestringYesUsername to unmute (with or without @)
dryRunbooleanNoTest mode (default: false)

Returns

TypeDescription
Promise<EngagementResult>Unmute operation result

Examples

Unmute a User

typescript
const result = await client.unmuteUser('username');

if (result.success) {
  console.log('User unmuted successfully!');
}

Common Patterns

Rate Limit Handling

All write operations handle rate limits automatically with webhook notifications:

typescript
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:

typescript
// Test without posting
const result = await client.postTweet({
  text: 'Test tweet',
  dryRun: true
});

console.log('Dry run successful:', result.id); // Mock ID

Batch Operations with Rate Limiting

typescript
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

typescript
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:

typescript
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:

bash
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET=your_api_secret
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_SECRET=your_access_secret

Error Handling Best Practices

typescript
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);
  }
}

Released under the MIT License.