Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 35

Posting to X (Twitter) with the API

Recommended resources

Hello! Welcome back to Module 8.

In our last lesson, we successfully integrated with the LinkedIn API, building a function to publish your agent's generated content. We navigated LinkedIn's 3-legged OAuth 2.0 flow, created a developer application, and wrote the TypeScript code to post articles. This was a major step in bringing your autonomous content system to life.

Today, we'll expand your agent's reach by tackling another major platform. Our learning outcome is to authenticate and post content via the X (Twitter) API. While some concepts, like OAuth, will feel familiar, the X API has its own distinct authentication flows and implementation details. We'll explore the most direct path to get your bot tweeting and set you up for more advanced interactions.

1. Understanding X API Authentication

Just like with LinkedIn, our application needs to get permission to post on your behalf. The X API provides two primary authentication contexts:

  • App-only authentication (OAuth 2.0 Bearer Token): This context authenticates your application itself, not a specific user. It's useful for reading public data (like searching for tweets) but does not have permission to post, follow, or like on behalf of a user.
  • User context authentication: This context allows your app to act on behalf of a specific user who has granted permission. This is what we need to post content. There are two common ways to achieve this with the v2 API:
    1. OAuth 2.0 Authorization Code Flow with PKCE: This is a 3-legged flow, conceptually similar to what you implemented for LinkedIn. It's the modern standard and is essential if you were building an app that other users would log into.
    2. OAuth 1.0a User Context: This is the legacy method but remains fully supported and is often simpler for personal bots or scripts that only need to act on behalf of a single, pre-authorized account (your own). It requires a set of four credentials: App Key, App Secret, User Access Token, and User Access Secret.

For your goal of creating a personal content agent, the OAuth 1.0a flow is the most direct and efficient path to get started. We will focus on that for our implementation, but it's valuable to understand the OAuth 2.0 flow as well.

How I post banger tweets with artificial intelligence // Twitter Bot Tutorial

The video 'How I post banger tweets with artificial intelligence' from Fireship gives an excellent conceptual crash course on the more complex OAuth 2.0 PKCE flow. Understanding this will give you a complete picture of X API authentication, even though we'll use a simpler method for our initial build.

Watch the segment from 00:59 to 02:39. Focus on the three main steps described: Creating an authorization link for the user. Handling the callback from X to get a temporary code. Exchanging the code for an Access Token and Refresh Token. This will reinforce the 3-legged OAuth pattern you saw with LinkedIn.


2. Setting Up Your X Developer Application

Before writing any code, you must register an application on the X Developer Portal. This process will generate the credentials our TypeScript code needs.

Make a Twitter bot with Node.js and the v2 Twitter API in under 20 minutes

The video 'Make a Twitter bot with Node.js...' by JuannyPubs provides a clear, practical walkthrough of the entire setup process on the X Developer Portal. We'll follow these steps to get our credentials.

Please follow along with the video from 01:36 to 05:13 to set up your app. Sign up for a Developer Account: If you haven't already, you'll need to apply for access. Create a Project and an App: Give your app a descriptive name. Set App Permissions: This is the most critical step. In your app's settings, find 'User authentication settings'. Click 'Set up' (or 'Edit'). You must change the permissions from the default 'Read' to 'Read and Write'. Without this, you won't be able to post tweets. Configure App Type: Select 'Automated App or Bot'. Generate Keys: On the 'Keys and tokens' tab, generate your API Key and Secret as well as your Access Token and Secret. Make sure the Access Token and Secret show 'Created with Read and Write permissions'. Securely store all four of these values. We will add them to our project's .env file shortly.

Your User authentication settings page should look similar to this, with "Automated App or bot" selected.

Twitter API User Authentication Settings
This image shows the User authentication settings screen in the X Developer Portal. It highlights the choice between OAuth 2.0 and OAuth 1.0a and correctly shows the 'Type of App' configured as an 'Automated App or bot', which is ideal for our use case.

3. Implementing the X Publisher in TypeScript

Now that we have our credentials, we can start coding. We'll use a popular, lightweight library called twitter-api-v2 that makes interacting with the X API straightforward and is fully typed for TypeScript.

twitter-api-v2 - NPM

First, let's get acquainted with the twitter-api-v2 library by reviewing its NPM page. This will give you a quick overview of its features and installation.

Read the 'Highlights' and 'How to use' sections. Note the command for installation (npm i twitter-api-v2) and the basic usage pattern for instantiating the client. This library's strong typing and support for both v1.1 and v2 of the API make it a great choice.

Step 1: Project Setup

  1. Install the library:
    npm install twitter-api-v2

  2. Update your .env file: Add the four keys you saved from the X Developer Portal.

    
    
    
    # .env
    LINKEDIN_ACCESS_TOKEN="..."
    
    
    
    
    # X API Credentials (OAuth 1.0a)
    X_APP_KEY="your_api_key_here"
    X_APP_SECRET="your_api_key_secret_here"
    X_ACCESS_TOKEN="your_access_token_here"
    X_ACCESS_SECRET="your_access_secret_here"
    

Step 2: Creating the X Client and Posting Logic

We'll create a new file, x.ts, to house our publishing logic. The implementation is remarkably concise thanks to the twitter-api-v2 library.

The documentation for twitter-api-v2 provides excellent code examples for various tasks. We will use these as our guide.

node-twitter-api-v2/doc/examples.md

The GitHub repository for twitter-api-v2 contains a file with practical examples. We will use the examples for client creation and posting a tweet.

Please review two key sections in this document: Under 'Create a client', find the subsection 'With user credentials (act as a logged user)' and look at the 'OAuth 1.0a User context' code block. This shows exactly how to initialize the client with the four keys we just saved. Under 'Tweets', find the subsection 'Post a new tweet with multiple images'. We'll simplify this for a text-only tweet, but notice the client.v2.tweet() method and its simple object-based argument ({ text: '...' }).

Now, let's put it all together in a reusable function. Create a new file src/publishers/x.ts:

import { TwitterApi } from 'twitter-api-v2';

// Instantiate the client with your OAuth 1.0a credentials
const client = new TwitterApi({
  appKey: process.env.X_APP_KEY!,
  appSecret: process.env.X_APP_SECRET!,
  accessToken: process.env.X_ACCESS_TOKEN!,
  accessSecret: process.env.X_ACCESS_SECRET!,
});

// Tell the library that this client will be used for read-write operations.
// This provides better autocompletion and type-checking.
const rwClient = client.readWrite;

export async function shareOnX(text: string): Promise<string> {
  if (!text) {
    throw new Error('Tweet text cannot be empty.');
  }

  try {
    // Use the v2 endpoint to post a tweet
    const { data: createdTweet } = await rwClient.v2.tweet({
      text,
    });
    
    console.log('Successfully posted to X (Twitter).');
    console.log('Tweet ID:', createdTweet.id);
    console.log('Tweet Text:', createdTweet.text);
    
    return createdTweet.id;
  } catch (error) {
    console.error('Error posting to X:', error);
    throw error;
  }
}

This self-contained shareOnX function is now ready to be integrated into your agent. It initializes the client, ensures it has read-write permissions at the type level, and uses the modern v2.tweet endpoint.


4. Integration with Your LangGraph Agent

As in the previous lesson, you can now add a new publishing node to your LangGraph workflow.

  1. Import your new shareOnX function into your main graph definition file.
  2. Create a new node, publishXNode, that calls await shareOnX(state.finalContent);.
  3. You can now update your graph's conditional logic. For example, after the human approval step, you could have the agent decide which platform to post to based on the content type or a specific instruction, and then route to either publishLinkedInNode or publishXNode.

This modular approach allows you to easily add more platforms in the future without altering the core logic of your generation and analysis agents.


Conclusion

Excellent work! You've now configured your agent to publish on a second major social media platform. By navigating the X API's authentication system and using a modern TypeScript library, you've added a powerful new capability to your project.

Key Takeaways:

  • X API has multiple auth flows: For a personal bot, OAuth 1.0a is the simplest way to get user-context permissions (Read and Write). OAuth 2.0 PKCE is the modern standard for apps that serve multiple users.
  • Permissions are crucial: You must explicitly set your app's permissions to "Read and Write" in the developer portal to be able to post content.
  • The twitter-api-v2 library simplifies everything: It handles the complexities of signing requests, providing a clean, typed interface to the v1.1 and v2 APIs.
  • Posting a tweet is a single API call: Once authenticated, client.v2.tweet({ text: '...' }) is all you need.

Preview of the next lesson:
We've tackled LinkedIn (professional, long-form) and X (concise, real-time). Next, we'll turn our attention to a visual-first platform. In the upcoming lesson, "Authenticate and post content via the Instagram Graph API," we will explore the opportunities and significant constraints of automating content for Instagram, which will require a different strategic approach.