npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bernierllc/social-media-twitter

v1.2.0

Published

Twitter/X API v2 integration service with OAuth 2.0, content posting, scheduling, and rate limiting

Readme

@bernierllc/social-media-twitter

Twitter/X API v2 integration service with OAuth 2.0 authentication, content posting, scheduling, and rate limit management.

Installation

npm install @bernierllc/social-media-twitter

Features

  • OAuth 2.0 Authentication: Complete OAuth flow with PKCE support
  • Tweet Posting: Post tweets with text, media, polls, and geo
  • Thread Creation: Post multi-tweet threads automatically linked
  • Media Upload: Support for images, GIFs, and videos with chunked upload
  • Tweet Scheduling: Schedule tweets for future posting
  • Interactions: Like, unlike, retweet, quote tweet, and reply
  • Rate Limit Management: Automatic rate limit tracking and handling
  • Token Management: Optional token storage with refresh support

Usage

Basic Setup

import { TwitterService } from '@bernierllc/social-media-twitter';

const twitter = new TwitterService({
  clientId: process.env.TWITTER_CLIENT_ID!,
  clientSecret: process.env.TWITTER_CLIENT_SECRET!,
  callbackUrl: 'https://yourapp.com/auth/twitter/callback',
  scopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
});

OAuth 2.0 Authentication

// 1. Generate authorization URL
const authUrl = await twitter.getAuthorizationUrl();
// Redirect user to authUrl

// 2. Handle callback
const result = await twitter.handleCallback(code, state);
if (result.success) {
  console.log(`Authenticated as @${result.user?.username}`);
}

// 3. Refresh token when needed
const refreshed = await twitter.refreshAccessToken(credentials.refreshToken!);

Posting Tweets

// Simple tweet
const result = await twitter.postTweet({
  text: 'Hello from @bernierllc/social-media-twitter!',
});

console.log(`Tweet posted: ${result.url}`);

// Tweet with media
const mediaResult = await twitter.uploadMedia({
  data: imageBuffer,
  mimeType: 'image/jpeg',
  category: 'tweet_image',
  altText: 'A beautiful sunset',
});

const tweetWithMedia = await twitter.postTweet({
  text: 'Check out this photo!',
  media: [{ mediaId: mediaResult.mediaId! }],
});

// Tweet with poll
const pollTweet = await twitter.postTweet({
  text: 'What do you prefer?',
  poll: {
    options: ['Option A', 'Option B', 'Option C'],
    durationMinutes: 1440, // 24 hours
  },
});

Posting Threads

const thread = await twitter.postThread([
  { text: '1/3 This is the first tweet in a thread...' },
  { text: '2/3 This is the continuation...' },
  { text: '3/3 And this is the conclusion!' },
]);

console.log(`Thread posted with ${thread.tweets?.length} tweets`);
console.log(`Thread ID: ${thread.threadId}`);

Scheduling Tweets

// Schedule for 1 hour from now
const scheduled = await twitter.scheduleTweet(
  { text: 'This is a scheduled tweet!' },
  new Date(Date.now() + 60 * 60 * 1000)
);

console.log(`Tweet scheduled for ${scheduled.scheduledFor}`);
console.log(`Schedule ID: ${scheduled.scheduleId}`);

// List scheduled tweets
const pending = await twitter.listScheduledTweets();

// Cancel scheduled tweet
await twitter.cancelScheduledTweet(scheduled.scheduleId!);

Interactions

// Reply to a tweet
const reply = await twitter.replyToTweet('original_tweet_id', {
  text: 'Great point!',
});

// Quote tweet
const quote = await twitter.quoteTweet('original_tweet_id', {
  text: 'This is an interesting take:',
});

// Retweet
await twitter.retweet('tweet_id');

// Like/Unlike
await twitter.likeTweet('tweet_id');
await twitter.unlikeTweet('tweet_id');

// Delete tweet
await twitter.deleteTweet('tweet_id');

User Information

// Get current authenticated user
const me = await twitter.getCurrentUser();
console.log(`@${me.username} - ${me.followersCount} followers`);

// Get user by username
const user = await twitter.getUserInfo('elonmusk');

Rate Limit Handling

// Configure rate limit strategy
const twitter = new TwitterService({
  // ... other config
  rateLimitConfig: {
    enabled: true,
    strategy: 'wait', // 'wait' | 'queue' | 'fail'
    maxRetries: 3,
  },
});

// Check rate limit status
const status = twitter.getRateLimitStatus('POST /2/tweets');
console.log(`${status.remaining}/${status.limit} requests remaining`);
console.log(`Resets at: ${status.reset}`);

// Manually wait for rate limit
await twitter.waitForRateLimit('POST /2/tweets');

Token Storage

import { TokenStorage } from '@bernierllc/social-media-twitter';

// Implement custom token storage
const tokenStorage: TokenStorage = {
  async store(key, credentials) {
    await myDatabase.set(key, JSON.stringify(credentials));
  },
  async retrieve(key) {
    const data = await myDatabase.get(key);
    return data ? JSON.parse(data) : null;
  },
  async delete(key) {
    await myDatabase.delete(key);
  },
};

const twitter = new TwitterService({
  // ... other config
  tokenStorage,
});

// Or set later
twitter.setTokenStorage(tokenStorage);

Connection Testing

// Verify credentials are valid
const isValid = await twitter.verifyCredentials();

// Full connection test
const test = await twitter.testConnection();
if (test.success) {
  console.log(`Connected as @${test.user?.username}`);
  console.log('Rate limits:', test.rateLimits);
}

API Reference

TwitterServiceConfig

| Property | Type | Description | |----------|------|-------------| | clientId | string | Twitter API client ID | | clientSecret | string | Twitter API client secret | | callbackUrl | string | OAuth callback URL | | scopes | string[] | OAuth scopes (default: tweet.read, tweet.write, users.read, offline.access) | | rateLimitConfig | RateLimitConfig | Rate limit handling configuration | | mediaConfig | MediaConfig | Media upload configuration | | tokenStorage | TokenStorage | Token persistence adapter | | debug | boolean | Enable debug logging |

RateLimitConfig

| Property | Type | Description | |----------|------|-------------| | enabled | boolean | Enable rate limit handling | | strategy | 'wait' | 'queue' | 'fail' | Strategy when rate limit is hit | | maxRetries | number | Maximum retries before failing |

MediaConfig

| Property | Type | Description | |----------|------|-------------| | chunkSize | number | Chunk size for chunked uploads (default: 5MB) | | maxConcurrentUploads | number | Maximum concurrent uploads (default: 3) |

Media Upload Limits

| Type | Max Size | Formats | |------|----------|---------| | Image | 5MB | JPEG, PNG, GIF, WebP | | GIF | 15MB | GIF | | Video | 512MB | MP4, QuickTime |

Rate Limits (Twitter API v2)

| Endpoint | User Context | App Context | |----------|--------------|-------------| | POST /2/tweets | 200/15min | 50/15min | | DELETE /2/tweets/:id | 50/15min | 50/15min | | POST /2/users/:id/retweets | 50/15min | 50/15min | | POST /2/users/:id/likes | 50/15min | 50/15min | | GET /2/users/me | 75/15min | 75/15min |

Security

API Credentials

  • Never hardcode client ID/secret - use environment variables
  • Store tokens securely using encrypted storage
  • Implement token refresh to avoid re-authentication

OAuth Security

  • PKCE support built-in for authorization code flow
  • State verification prevents CSRF attacks
  • Automatic state cleanup for expired auth attempts

Integration Status

  • Logger: Planned for v1.1.0
  • NeverHub: Planned for v1.2.0
  • Docs-Suite: Ready (TypeDoc format)

License

Copyright (c) 2025 Bernier LLC. All rights reserved.