twit and twitter are both npm packages designed to simplify interaction with the Twitter API from Node.js applications. They abstract away the complexities of OAuth authentication, HTTP request construction, and response handling, allowing developers to focus on application logic rather than low-level API mechanics. Both support core Twitter API operations such as posting tweets, reading timelines, searching tweets, and streaming real-time data, but they differ significantly in architecture, maintenance status, and developer experience.
Both twit and twitter aim to help Node.js developers interact with Twitter’s API, but their current states, design philosophies, and suitability for production use differ dramatically. Let’s examine them through the lens of real-world engineering decisions.
twitter is officially deprecated.
The npm page for twitter displays a prominent deprecation warning: "This package is no longer maintained. Please use twitter-api-v2 instead." The GitHub repository also marks the project as archived, with the last meaningful commit dating back several years. Using it in new projects introduces significant risk due to lack of security updates, bug fixes, and compatibility with Twitter’s evolving API.
twit is unmaintained but not formally deprecated.
While twit hasn’t had a release since 2018 and its GitHub repo shows minimal recent activity, it remains installable without deprecation warnings. It works reliably with Twitter API v1.1, which is still operational (though Twitter encourages migration to v2). For non-critical or internal tooling that doesn’t require v2 features, it may suffice — but proceed with caution.
🛑 Bottom line: Never start a new project with
twitonly if you’re constrained to API v1.1 and understand the maintenance risks.
Both packages require Twitter API keys (consumer key, consumer secret, access token, access token secret), but their setup differs.
twit uses a simple constructor with a config object:
const Twit = require('twit');
const client = new Twit({
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
access_token: 'YOUR_ACCESS_TOKEN',
access_token_secret: 'YOUR_ACCESS_TOKEN_SECRET',
timeout_ms: 60 * 1000
});
twitter requires initializing a Twitter instance with similar credentials, but uses callback-style methods:
const Twitter = require('twitter');
const client = new Twitter({
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
access_token_key: 'YOUR_ACCESS_TOKEN', // Note: different property name
access_token_secret: 'YOUR_ACCESS_TOKEN_SECRET'
});
Note the subtle difference: twitter uses access_token_key, while twit uses access_token. This inconsistency can cause frustrating bugs during migration or setup.
This is where the developer experience diverges sharply.
twit returns promises, making it compatible with modern async/await syntax:
// Post a tweet with twit
try {
const result = await client.post('statuses/update', { status: 'Hello from twit!' });
console.log('Tweet posted:', result.data.id_str);
} catch (err) {
console.error('Failed to post tweet:', err);
}
// Search tweets
const searchResults = await client.get('search/tweets', { q: 'javascript', count: 5 });
console.log(searchResults.data.statuses);
twitter uses callbacks, forcing you into error-first callback patterns:
// Post a tweet with twitter
client.post('statuses/update', { status: 'Hello from twitter!' }, (error, tweet, response) => {
if (error) {
console.error('Failed to post tweet:', error);
return;
}
console.log('Tweet posted:', tweet.id_str);
});
// Search tweets
client.get('search/tweets', { q: 'javascript', count: 5 }, (error, tweets, response) => {
if (!error) {
console.log(tweets.statuses);
}
});
In modern JavaScript codebases that rely on async/await, twit’s promise-based interface integrates far more cleanly and reduces cognitive overhead.
Both support Twitter’s streaming API, but again with different ergonomics.
twit exposes a stream method that returns an EventEmitter-like object:
const stream = client.stream('statuses/filter', { track: 'nodejs' });
stream.on('tweet', (tweet) => {
console.log('New tweet:', tweet.text);
});
stream.on('error', (err) => {
console.error('Stream error:', err);
});
// Stop the stream later
setTimeout(() => stream.stop(), 60000);
twitter also uses event emitters, but initialization is callback-based:
client.stream('statuses/filter', { track: 'nodejs' }, (stream) => {
stream.on('data', (tweet) => {
console.log('New tweet:', tweet.text);
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
});
While both approaches work, twit’s direct return of the stream object avoids the extra callback layer, making cleanup and lifecycle management slightly easier.
twit throws standard JavaScript errors that include Twitter’s HTTP status codes and error messages when available:
try {
await client.post('statuses/update', { status: 'x'.repeat(300) }); // Too long
} catch (err) {
console.log(err.statusCode); // e.g., 403
console.log(err.errors[0].message); // "Status is over 280 characters"
}
twitter passes errors as the first argument to callbacks, with similar detail:
client.post('statuses/update', { status: 'x'.repeat(300) }, (error, tweet, response) => {
if (error) {
console.log(error.statusCode); // 403
console.log(error[0].message); // "Status is over 280 characters"
}
});
Error structures are comparable, but twit’s integration with try/catch aligns better with contemporary error handling practices.
Both packages target Twitter API v1.1 exclusively. Neither supports Twitter API v2, which introduced new endpoints, enhanced filtering, and different authentication flows (including OAuth 2.0 Bearer Tokens).
If your project requires v2 features (e.g., conversation threading, pinned tweets, academic research access), neither package is suitable. You should instead consider actively maintained alternatives like twitter-api-v2.
twit due to its simplicity.twitter that haven’t been migrated.twit as a “hello world” for API clients.However, for any new development — especially public-facing or business-critical applications — these packages should be avoided in favor of modern, maintained libraries.
| Concern | twit | twitter |
|---|---|---|
| Maintenance Status | Unmaintained (no recent updates) | Deprecated (do not use) |
| API Version | v1.1 only | v1.1 only |
| Async Style | Promises / async-await | Callbacks |
| Streaming | Clean EventEmitter interface | Callback-based stream init |
| New Projects? | Only if stuck on v1.1 & aware of risks | Never |
For new projects, use twitter-api-v2. It:
Example with twitter-api-v2:
import { TwitterApi } from 'twitter-api-v2';
const client = new TwitterApi({
appKey: '...',
appSecret: '...',
accessToken: '...',
accessSecret: '...'
});
const rwClient = client.readWrite;
const tweet = await rwClient.v1.tweet('Hello from twitter-api-v2!');
In summary: avoid twitter entirely, treat twit as a legacy option only, and adopt a modern alternative for anything new.
Choose twitter (also known as twitter-api) only if you are maintaining a legacy system that already depends on it — do not use it in new projects. The package is officially deprecated on npm and GitHub, with clear notices advising migration to alternatives. It relies on callbacks rather than promises, uses an outdated OAuth implementation, and lacks compatibility with modern Twitter API requirements.
Choose twit if you need a lightweight, promise-based wrapper that simplifies common REST and streaming operations with minimal boilerplate. It is well-suited for scripts, bots, or internal tools where you control the environment and don’t require advanced configuration. However, note that it has not seen active maintenance in recent years and may lack support for newer Twitter API v2 features.
An asynchronous client library for the Twitter REST and Streaming API's.
var Twitter = require('twitter');
var client = new Twitter({
consumer_key: '',
consumer_secret: '',
access_token_key: '',
access_token_secret: ''
});
var params = {screen_name: 'nodejs'};
client.get('statuses/user_timeline', params, function(error, tweets, response) {
if (!error) {
console.log(tweets);
}
});
npm install twitter
You will need valid Twitter developer credentials in the form of a set of consumer and access tokens/keys. You can get these here. Do not forgot to adjust your permissions - most POST request require write permissions.
var Twitter = require('twitter');
var client = new Twitter({
consumer_key: '',
consumer_secret: '',
access_token_key: '',
access_token_secret: ''
});
Add your credentials accordingly. I would use environment variables to keep your private info safe. So something like:
var client = new Twitter({
consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
});
You will need to fetch a bearer token from Twitter as documented Here, once you have it you can use it as follows.
var client = new Twitter({
consumer_key: '',
consumer_secret: '',
bearer_token: ''
});
Add your credentials accordingly. I would use environment variables to keep your private info safe. So something like:
var client = new Twitter({
consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
bearer_token: process.env.TWITTER_BEARER_TOKEN
});
NB - You will not have access to all endpoints whilst using Application Only authentication, but you will have access to higher API limits.
You now have the ability to make GET and POST requests against the API via the convenience methods.
client.get(path, params, callback);
client.post(path, params, callback);
client.stream(path, params, callback);
You simply need to pass the endpoint and parameters to one of convenience methods. Take a look at the documentation site to reference available endpoints.
Example, lets get a list of favorites:
client.get('favorites/list', function(error, tweets, response) {
if(error) throw error;
console.log(tweets); // The favorites.
console.log(response); // Raw response object.
});
How about an example that passes parameters? Let's tweet something:
client.post('statuses/update', {status: 'I Love Twitter'}, function(error, tweet, response) {
if(error) throw error;
console.log(tweet); // Tweet body.
console.log(response); // Raw response object.
});
The REST API convenience methods will also return Promises if:
If those two conditions are met, the above example becomes:
client.post('statuses/update', {status: 'I Love Twitter'})
.then(function (tweet) {
console.log(tweet);
})
.catch(function (error) {
throw error;
})
Note, the raw response object returned by the Request module is not passed through
the fulfilled promise. If you require this, please use the callback pattern.
Using the stream convenience method, you to open and manipulate data via a stream piped directly from one of the streaming API's. Let's see who is talking about javascript:
var stream = client.stream('statuses/filter', {track: 'javascript'});
stream.on('data', function(event) {
console.log(event && event.text);
});
stream.on('error', function(error) {
throw error;
});
// You can also get the stream in a callback if you prefer.
client.stream('statuses/filter', {track: 'javascript'}, function(stream) {
stream.on('data', function(event) {
console.log(event && event.text);
});
stream.on('error', function(error) {
throw error;
});
});
Note twitter stream several types of events, see the docs for more info. There is no canonical way of detecting tweets versus other messages, but some users have had success with the following strategy.
_ = require('lodash')
const isTweet = _.conforms({
contributors: _.isObject,
id_str: _.isString,
text: _.isString,
})
Originally authored by @technoweenie and maintained by @jdub
Currently maintained by @desmondmorris