If you want to post to X (formerly Twitter) from code, you have two choices: build on X's own API, or call a unified API that has already done that work. This guide covers the first: what the X API costs, how to get access, the request that creates a post with an image, and the limits to plan for. The last section covers when a unified API such as Ampost is simpler.
X API tiers and prices
X no longer sells monthly subscription tiers for the X API. Self-serve access is pay-per-use: you buy credits in the Developer Console, and each request takes its price from the balance. There are no contracts and no minimum spend. Above that sits an Enterprise plan, for which X publishes no price.
| Tier | Monthly price | Posts you may write per month | Read limits |
|---|---|---|---|
| Pay-per-use | No subscription: prepaid credits, per request | No monthly cap. Each post costs $0.015, or $0.200 when it contains a URL | Up to 3 million Post reads per monthly billing cycle; Post reads cost $0.005 each and user reads $0.010 |
| Enterprise | Not listed by X (contact X) | Not listed by X | Higher volume than pay-per-use, with custom rate limits |
Prices as listed by X on October 23, 2026, on X's developer pricing page. X notes that prices are subject to change and shows the current rates in its Developer Console. We will update this table when X changes it.
Two details from the same page change what a bill looks like:
- Links are the expensive part. At X's listed prices, 1,000 plain posts cost $15, and 1,000 posts that contain a URL cost $200.
- Reads are deduplicated per day. A resource you already paid for is not charged again within the same 24-hour UTC window. Requests your app makes for your own data ("Owned Reads"), such as your own posts or followers, cost $0.001 per resource.
New accounts can also earn one-time free credits: $20 after the first credit purchase with a saved card, and a match of the first auto-recharge up to $50. You can set a spending limit per billing cycle, after which X blocks requests until the next cycle.
How to get X API access
X's getting access guide has three steps:
- Create a developer account. Sign in at the Developer Console (console.x.com) with your X account, accept the Developer Agreement and Policy, and describe how you will use the API.
- Create an app. Give it a name, description and use case. The console generates the app's credentials. To post for other people, set up OAuth 2.0 in the app settings: choose the app type (a Web App or Automated App is a confidential client and gets a client secret) and register your callback URL, which X matches exactly.
- Save your credentials. You get an API Key and Secret, a Bearer Token for app-only reads, an Access Token and Secret for your own account, and a Client ID and Secret for OAuth 2.0. X shows them only once.
Posting on behalf of users needs OAuth 2.0 with user context. The Bearer Token can only read public data. To post, run the OAuth 2.0 Authorization Code Flow with PKCE: send the user to https://x.com/i/oauth2/authorize with the scopes you need, then exchange the code at https://api.x.com/2/oauth2/token. Creating a post needs tweet.read, tweet.write and users.read; uploading media needs media.write. Access tokens last two hours; add offline.access to get a refresh token.
Keep the API Secret, Client Secret, Access Token Secret and every user's tokens on your server, in environment variables or a secrets vault, never in source control or client-side code. Bill-paying credentials deserve the same care as a payment card: anyone holding them can spend your credits.
Posting a tweet with the X API
X's create-post endpoint is POST https://api.x.com/2/tweets, called with the user's OAuth 2.0 access token. A text post is one request:
curl -X POST "https://api.x.com/2/tweets" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from the X API"}'
For an image, upload it first and pass the returned media ID. An image can go to POST /2/media/upload in one request, base64-encoded in a JSON body with media_category set to tweet_image:
curl -X POST "https://api.x.com/2/media/upload" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"media\": \"$(base64 -w0 chart.png)\", \"media_category\": \"tweet_image\"}"
The response carries the media ID in data.id. Attach it to the post:
curl -X POST "https://api.x.com/2/tweets" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "This week in charts", "media": {"media_ids": ["1880028106020515840"]}}'
Videos and large GIFs use the chunked upload instead: POST /2/media/upload/initialize, then /{id}/append for each chunk, then /{id}/finalize, and you poll GET /2/media/upload until processing ends before you create the post.
Limits to plan for
Rate limits and billing are separate: you can be within the rate limit and still pay for each request. X's rate limit table lists, among others:
| Endpoint | Per user | Per app |
|---|---|---|
POST /2/tweets (create a post) |
100 per 15 minutes | 10,000 per 24 hours |
DELETE /2/tweets/:id |
50 per 15 minutes | — |
POST /2/media/upload |
500 per 15 minutes | 50,000 per 24 hours |
POST /2/media/upload/initialize, append, finalize |
1,875 per 15 minutes each | 180,000 per 24 hours each |
Per-user limits apply when you call with a user's OAuth token; per-app limits apply with the app's Bearer Token. A request over a limit returns HTTP 429; the x-rate-limit-reset header says when the window resets.
X's media limits depend on the posting account, not on your API plan:
- A post can carry up to 4 photos, 1 animated GIF or 1 video.
- Images (JPG, PNG, GIF, WEBP) up to 5 MB; animated GIFs up to 15 MB.
- Video up to 20 minutes and 8 GB, or 125 minutes and 16 GB for X Premium and verified accounts.
X checks media again when the post is created, so an upload that succeeded can still be rejected at POST /2/tweets.
When a unified API is simpler
Everything above is yours to run when you build on X directly: the developer account, prepaid credits and spending controls, the OAuth flow and token refresh, the upload steps and the rate limits. If posting to X is one feature of your product, a unified API can carry most of it.
Ampost publishes to X without an X developer account or pay-per-use billing of your own. Ampost holds the X app, runs the OAuth flow and the media upload, and your posts come out of a monthly X allowance included in the Max and Scale plans. A post can have text, up to 4 images, a GIF or one video. Posts with a link use more of the allowance, because X charges more for them.
The same call that posts to X also posts to the other networks Ampost supports, such as LinkedIn, Threads, Bluesky and Instagram, with one result per platform in the response. Every REST operation has a matching MCP tool, so an AI agent can publish through the MCP server with the same arguments your backend sends to the REST API.
