Build With X

X API Basics

Timeline fetching, posting tweets, and basic operations

Authentication

See Authentication for setup. Most examples use the Bearer token.

Fetch User Timeline

const userId = "12345" // Numeric user ID

const response = await fetch(
  `https://api.twitter.com/2/users/${userId}/tweets?` +
    new URLSearchParams({
      max_results: "10",
      "tweet.fields": "created_at,public_metrics,author_id",
      expansions: "author_id",
      "user.fields": "name,username,profile_image_url",
    }),
  {
    headers: {
      Authorization: `Bearer ${process.env.X_BEARER_TOKEN}`,
    },
  }
)

const data = await response.json()
console.log(data)

Get User by Username

async function getUserByUsername(username: string) {
  const response = await fetch(
    `https://api.twitter.com/2/users/by/username/${username}?` +
      new URLSearchParams({
        "user.fields": "id,name,username,description,public_metrics",
      }),
    {
      headers: {
        Authorization: `Bearer ${process.env.X_BEARER_TOKEN}`,
      },
    }
  )

  return response.json()
}

const user = await getUserByUsername("elonmusk")
console.log(user.data.id) // Use this ID for timeline fetches

Post a Tweet

Posting requires OAuth 2.0 user authentication. See X API OAuth.

async function postTweet(text: string, accessToken: string) {
  const response = await fetch("https://api.twitter.com/2/tweets", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ text }),
  })

  return response.json()
}

// With OAuth 2.0 access token
const result = await postTweet("Hello from my app!", userAccessToken)
console.log(result.data.id) // New tweet ID

Reply to a Tweet

async function replyToTweet(
  text: string,
  replyToId: string,
  accessToken: string
) {
  const response = await fetch("https://api.twitter.com/2/tweets", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      text,
      reply: {
        in_reply_to_tweet_id: replyToId,
      },
    }),
  })

  return response.json()
}

Search Recent Tweets

async function searchTweets(query: string) {
  const response = await fetch(
    `https://api.twitter.com/2/tweets/search/recent?` +
      new URLSearchParams({
        query,
        max_results: "10",
        "tweet.fields": "created_at,public_metrics,author_id",
      }),
    {
      headers: {
        Authorization: `Bearer ${process.env.X_BEARER_TOKEN}`,
      },
    }
  )

  return response.json()
}

const results = await searchTweets("typescript lang:en -is:retweet")

Tweet Fields Reference

FieldDescription
idTweet ID
textTweet content
created_atTimestamp
author_idAuthor's user ID
public_metricsLike/retweet/reply counts
entitiesURLs, mentions, hashtags
attachmentsMedia IDs

Rate Limits

EndpointRate Limit
User timeline900 / 15 min
Search recent450 / 15 min
Post tweet200 / 15 min
User lookup900 / 15 min

Error Handling

interface XAPIError {
  errors?: Array<{
    message: string
    code: number
  }>
  title?: string
  detail?: string
  status?: number
}

async function fetchWithErrorHandling(url: string, options: RequestInit) {
  const response = await fetch(url, options)

  if (!response.ok) {
    const error: XAPIError = await response.json()

    if (response.status === 429) {
      const resetTime = response.headers.get("x-rate-limit-reset")
      console.error(`Rate limited. Reset at: ${resetTime}`)
    }

    throw new Error(error.detail || error.title || "X API error")
  }

  return response.json()
}

On this page