A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The X (Twitter) API is how an app or AI agent works with an X account: creating and looking up posts, searching the public conversation, following accounts, and sending direct messages. Access is granted through a token, where an OAuth 2.0 token carries granular scopes that set what each call can read or write, and an agent is limited to those scopes and to what its access tier allows. Which endpoints and volumes are reachable depends on that tier, and X can push account events to a webhook on its highest tier.
How an app or AI agent connects to X determines what it can reach. There is a route for acting on behalf of a person, a route for reading public data without a person, and a hosted server that exposes X tools to agents, and each is governed by the token behind it and the scopes that token carries.
The version 2 REST API answers at https://api.x.com/2. A call authenticates with an OAuth 2.0 user token, an OAuth 1.0a user token, or an app-only Bearer Token, and returns JSON. It is the current API; the older version 1.1 and legacy hosts are being retired.
X publishes a first-party Model Context Protocol server, xmcp, that turns the X API OpenAPI spec into callable tools for an AI agent, covering posts, users, direct messages, lists, trends, and more, while leaving out streaming and webhook endpoints. It runs locally as a Python application and authenticates with a Bearer Token plus optional OAuth 1.0a or OAuth 2.0 user tokens. It is in developer preview, with no tagged release, at github.com/xdevplatform/xmcp.
The Account Activity API delivers events to a registered webhook URL when something happens on a subscribed account, such as a post being created or deleted, a direct message arriving, or a follow. It is part of the enterprise tier rather than the standard endpoints.
OAuth 2.0 with PKCE lets an app act on behalf of an account with granular scopes chosen per token, such as tweet.write, follows.write, like.write, or dm.write. It is the only supported OAuth 2.0 grant, and adding offline.access returns a refresh token so access persists until the account revokes it. This is the route for least-privilege agent access.
An app-only Bearer Token authenticates the app itself, with no user attached, and reaches only publicly available data. It cannot post, message, or read private data, and it suits read-only work like looking up public posts, users, and search.
OAuth 1.0a user context lets an app act on behalf of an account and access private data or take actions for it. It predates the scope model, granting access by the app's overall read, write, and direct-message permission level rather than per-token scopes, and some endpoints still rely on it.
The X API is split into areas an agent can act on, like posts, search, users, follows, likes, reposts, bookmarks, lists, and direct messages. Each area has its own methods and its own scope, and which areas an account can reach at all depends on its access tier.
Look up posts by ID, create a post, and delete a post on behalf of the authenticated account.
Search recent posts from the last seven days, and search the full archive back to 2006.
Read the posts authored by a user and the posts that mention a user.
Look up an account by ID or username, look up many at once, and read the authenticated account.
List who an account follows and who follows it, and follow or unfollow an account.
List the posts an account has liked, and like or unlike a post.
List the accounts that reposted a post, and repost or undo a repost.
List the authenticated account's bookmarks, and add or remove a bookmark.
Create a list, look one up, and add or remove a member from a list.
Read direct message events, and send a message into a conversation or to a participant.
Look up a live audio Space by ID, and search for Spaces by keyword.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
PostsLook up posts by ID, create a post, and delete a post on behalf of the authenticated account.4 | ||||||
| GET | /2/tweets/:id | Get a single post by its ID, including its details and edit history. | read | tweet.read | Current | |
App-only Bearer Token also works for public posts. The OAuth 1.0a user-context route needs no scope token. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/tweets | Look up multiple posts at once by passing a list of post IDs. | read | tweet.read | Current | |
App-only access allows 3,500 requests per 15 minutes; user access allows 5,000. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limit3,500 per 15 min (app) | 5,000 per 15 min (user) SourceOfficial documentation ↗ | ||||||
| POST | /2/tweets | Create a post on behalf of the authenticated account. | write | tweet.write | Current | |
Also needs tweet.read and users.read on the token. Each created post is billed under the pay-per-use model. Programmatic replies are restricted to self-serve tiers since 23 Feb 2026. Acts onpost Permission (capability) tweet.writeVersionAvailable since the API’s base version Webhook eventNone Rate limit100 per 15 min (user) | 10,000 per 24 hr (app) SourceOfficial documentation ↗ | ||||||
| DELETE | /2/tweets/:id | Delete a post the authenticated account owns. | write | tweet.write | Current | |
Also needs tweet.read and users.read on the token. Irreversible. Acts onpost Permission (capability) tweet.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SearchSearch recent posts from the last seven days, and search the full archive back to 2006.2 | ||||||
| GET | /2/tweets/search/recent | Search posts from the last seven days that match a query. | read | tweet.read | Current | |
App-only Bearer Token also works. The 4 May 2026 search index added min_likes, min_replies, and min_reposts operators. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limit450 per 15 min (app) | 300 per 15 min (user) SourceOfficial documentation ↗ | ||||||
| GET | /2/tweets/search/all | Search the full archive of public posts back to 2006. | read | tweet.read | Current | |
Full-archive search is reachable only on higher access tiers, not on a new pay-per-use account by default. App-only access is limited to 1 request per second. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limit1 per sec, 300 per 15 min (app) SourceOfficial documentation ↗ | ||||||
TimelinesRead the posts authored by a user and the posts that mention a user.2 | ||||||
| GET | /2/users/:id/tweets | Get the posts authored by a user. | read | tweet.read | Current | |
App-only Bearer Token also works for public posts. Reading the authenticated account's own posts is billed as an owned read. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/users/:id/mentions | Get the posts that mention a user. | read | tweet.read | Current | |
App-only Bearer Token also works. Acts onpost Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
UsersLook up an account by ID or username, look up many at once, and read the authenticated account.4 | ||||||
| GET | /2/users/:id | Look up a single account by its user ID. | read | users.read | Current | |
App-only Bearer Token also works. User-lookup endpoints allow 300 requests per 15 minutes app-only and 900 with user access. Acts onuser Permission (capability) users.readVersionAvailable since the API’s base version Webhook eventNone Rate limit300 per 15 min (app) | 900 per 15 min (user) SourceOfficial documentation ↗ | ||||||
| GET | /2/users/by/username/:username | Look up a single account by its username. | read | users.read | Current | |
App-only Bearer Token also works. Acts onuser Permission (capability) users.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/users/by | Look up multiple accounts at once by passing a list of usernames. | read | users.read | Current | |
App-only Bearer Token also works. Acts onuser Permission (capability) users.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/users/me | Get the profile of the authenticated account. | read | users.read | Current | |
User context only; not callable with an app-only Bearer Token. Adding the users.email scope returns the account's email address. Acts onuser Permission (capability) users.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
FollowsList who an account follows and who follows it, and follow or unfollow an account.4 | ||||||
| GET | /2/users/:id/following | List the accounts a user follows. | read | follows.read | Current | |
tweet.read and users.read are also accepted on the token. Acts onuser Permission (capability) follows.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/users/:id/followers | List the accounts that follow a user. | read | follows.read | Current | |
tweet.read and users.read are also accepted on the token. Acts onuser Permission (capability) follows.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /2/users/:id/following | Follow an account on behalf of the authenticated account. The target account is passed in the body. | write | follows.write | Current | |
Also needs tweet.read and users.read on the token. The :id must be the authenticated account. Acts onuser Permission (capability) follows.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /2/users/:source_user_id/following/:target_user_id | Unfollow an account on behalf of the authenticated account. | write | follows.write | Current | |
Also needs tweet.read and users.read on the token. The source must be the authenticated account. Acts onuser Permission (capability) follows.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
LikesList the posts an account has liked, and like or unlike a post.3 | ||||||
| GET | /2/users/:id/liked_tweets | List the posts a user has liked. | read | like.read | Current | |
tweet.read and users.read are also accepted on the token. Acts onpost Permission (capability) like.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /2/users/:id/likes | Like a post on behalf of the authenticated account. The post is passed in the body. | write | like.write | Current | |
Also needs tweet.read and users.read on the token. Acts onpost Permission (capability) like.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /2/users/:id/likes/:tweet_id | Remove a like from a post on behalf of the authenticated account. | write | like.write | Current | |
Also needs tweet.read and users.read on the token. Acts onpost Permission (capability) like.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
RepostsList the accounts that reposted a post, and repost or undo a repost.3 | ||||||
| POST | /2/users/:id/retweets | Repost a post on behalf of the authenticated account. The post is passed in the body. | write | tweet.write | Current | |
Reposting is covered by tweet.write; the token also needs tweet.read and users.read. Acts onpost Permission (capability) tweet.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /2/users/:id/retweets/:source_tweet_id | Undo a repost on behalf of the authenticated account. | write | tweet.write | Current | |
Covered by tweet.write; the token also needs tweet.read and users.read. Acts onpost Permission (capability) tweet.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/tweets/:id/retweeted_by | List the accounts that reposted a post. | read | tweet.read | Current | |
App-only Bearer Token also works. users.read is also accepted. Acts onuser Permission (capability) tweet.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
BookmarksList the authenticated account's bookmarks, and add or remove a bookmark.3 | ||||||
| GET | /2/users/:id/bookmarks | List the authenticated account's bookmarked posts. | read | bookmark.read | Current | |
User context only. tweet.read and users.read are also needed. Bookmarks are an owned read in pay-per-use billing. Acts onpost Permission (capability) bookmark.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /2/users/:id/bookmarks | Bookmark a post for the authenticated account. The post is passed in the body. | write | bookmark.write | Current | |
Also needs tweet.read and users.read on the token. Acts onpost Permission (capability) bookmark.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /2/users/:id/bookmarks/:tweet_id | Remove a bookmark for the authenticated account. | write | bookmark.write | Current | |
Also needs tweet.read and users.read on the token. Acts onpost Permission (capability) bookmark.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
ListsCreate a list, look one up, and add or remove a member from a list.4 | ||||||
| POST | /2/lists | Create a list owned by the authenticated account. | write | list.write | Current | |
Also needs tweet.read and users.read on the token. Acts onlist Permission (capability) list.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/lists/:id | Look up a list by its ID. | read | list.read | Current | |
App-only Bearer Token also works for public lists. tweet.read and users.read are also accepted. Acts onlist Permission (capability) list.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /2/lists/:id/members | Add a member to a list. The account to add is passed in the body. | write | list.write | Current | |
Also needs tweet.read and users.read on the token. Acts onlist Permission (capability) list.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /2/lists/:id/members/:user_id | Remove a member from a list. | write | list.write | Current | |
Also needs tweet.read and users.read on the token. Acts onlist Permission (capability) list.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Direct MessagesRead direct message events, and send a message into a conversation or to a participant.3 | ||||||
| GET | /2/dm_events | List direct message events the authenticated account can see. | read | dm.read | Current | |
User context only. tweet.read and users.read are also needed. Limited to 15 requests per 15 minutes. Acts ondirect message Permission (capability) dm.readVersionAvailable since the API’s base version Webhook eventNone Rate limit15 per 15 min (user) SourceOfficial documentation ↗ | ||||||
| POST | /2/dm_conversations/:dm_conversation_id/messages | Send a direct message into an existing conversation. | write | dm.write | Current | |
Also needs tweet.read and users.read on the token. Acts ondirect message Permission (capability) dm.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /2/dm_conversations/with/:participant_id/messages | Send a direct message to a participant, creating the conversation if needed. | write | dm.write | Current | |
Also needs tweet.read and users.read on the token. Acts ondirect message Permission (capability) dm.writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SpacesLook up a live audio Space by ID, and search for Spaces by keyword.2 | ||||||
| GET | /2/spaces/:id | Look up a live or scheduled audio Space by its ID. | read | space.read | Current | |
App-only Bearer Token also works. tweet.read and users.read are also accepted. Acts onspace Permission (capability) space.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /2/spaces/search | Search for live or scheduled Spaces by keyword. | read | space.read | Current | |
App-only Bearer Token also works. Acts onspace Permission (capability) space.readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
X can notify an app or AI agent when something happens on an account, like a post being created or a direct message arriving, through the Account Activity API. That subscription model is part of the enterprise tier rather than the standard endpoints.
| Event | What it signals | Triggered by |
|---|
X limits how fast an app or AI agent can call, through per-endpoint windows that reset every 15 minutes or every 24 hours, counted separately for app-only and user-authenticated calls. A separate usage cap, billed per request, sits on top of the rate limits.
X meters each endpoint with its own window, counted separately for app-only and user-authenticated calls. Most windows reset every 15 minutes, with some daily caps that reset every 24 hours. Post lookup by IDs allows 3,500 requests per 15 minutes app-only and 5,000 with user access; recent search allows 450 app-only and 300 with user access; user lookup allows 300 app-only and 900 with user access; creating posts is held to 100 per 15 minutes per user and 10,000 per 24 hours per app; full-archive search is capped to 1 request per second; reading direct message events is limited to 15 per 15 minutes. Exceeding a window returns HTTP 429 with x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset headers. Rate limits are separate from billing: under the pay-per-use model a usage cap and per-request charges apply on top, with reads capped at 2 million posts per month before an enterprise plan is required.
List endpoints page with a cursor. A max_results parameter sets the page size within each endpoint's allowed range, and a next_token returned in the response meta is passed back as pagination_token to fetch the following page. Paging stops when no next_token is returned. Full-archive and recent search also support a since_id and until_id, and a start_time and end_time, to bound a query by post.
Results are JSON. A standard post is up to 280 characters, with longer posts available to premium accounts. Search and lookup endpoints cap how many posts or users come back per page through max_results, and the full archive reaches back to X's first posts in 2006. Pay-per-use post reads are capped at 2 million per month.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | Bad Request | The request was malformed: invalid JSON, a malformed search query, or a missing required parameter. The body carries an errors array, and a 400 can hold more than one error object. | Read the errors array, correct the named parameter or query, and resend. The request is not retryable as-is. |
| 401 | Unauthorized | Authentication credentials are missing or invalid. The problem-details body shows type, title 'Unauthorized', detail, and status. | Check the token and the Authorization header, refresh an expired OAuth 2.0 token, and send valid credentials. |
| 403 | Forbidden | Authentication succeeded but the token lacks permission for this resource or action, for example a missing scope, an action the account is not allowed to take, or an endpoint above the current access tier. | Grant the missing scope on the token, or move to an access tier that includes the endpoint. |
| 404 | Not Found | The resource does not exist or has been deleted, such as a post, user, or list that is gone or not visible to the token. | Confirm the ID is correct and the token can see the resource. |
| 429 | Too Many Requests | A rate-limit window was exhausted or a usage cap was hit. The x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset headers report the state, where reset is a Unix timestamp for the next window. | Wait until the x-rate-limit-reset time, then retry, and back off exponentially on repeated limits. |
| 500 | Internal Server Error | An error on X's side, which can also appear as 502, 503, or 504. | Wait and retry with exponential backoff, and check the X API status page if it persists. |
X runs a single current version of its API, version 2, and ships dated changes through its changelog rather than minting new version numbers. Older hosts and the version 1.1 API are being retired in favor of it.
Version 2 is the current X API. Rather than minting new version numbers, X ships dated changes through its changelog, so an integration builds against one live version and tracks the changelog for additions and deprecations. The older version 1.1 and legacy hosts are being retired in favor of it. The entries below are recent dated changes within version 2.
Search endpoints moved to a new index with performance improvements and three new precision operators for filtering by engagement.
Per-request pricing under the pay-per-use model was updated, lowering owned reads and setting per-post creation costs.
The legacy direct message events were consolidated into the Account Activity API, and event type naming moved to a dot-separated lowercase format.
X replaced its tiered pricing model with pay-per-use as the default for new developers, removing the free tier for new sign-ups. Legacy Basic and Pro plans remain for existing subscribers, and enterprise access continues separately.
There is one live version to build against, and changes arrive as dated changelog entries.
X API changelog ↗Bollard AI sits between a team's AI agents and X. Grant each agent exactly the access it needs, read or write, area by area, and every call is checked and logged.