X (Twitter) API Posting: Integration Guide
Understanding OAuth authentication, the chunked media upload flow, and API endpoints for posting to X.
Posting to X via API requires OAuth 2.0 with PKCE (or legacy OAuth 1.0a) and tweet.write scope. POST to api.x.com/2/tweets with text up to 280 characters. Media uses a three-step chunked upload: INIT, APPEND segments under 5 MB each, then FINALIZE, polling for processing on videos and GIFs before attaching the media_id to the post.
How do you get X (Twitter) API developer access?
Before your app can post to X on behalf of users, you need access through the X Developer Portal:
- Developer Account — Apply at developer.x.com. You’ll need to describe your intended use case
- Project & App — Create a project and app in the Developer Portal to get your API keys
- Access Level — As of February 2026, new developers are placed on pay-per-use by default ($0.01 per post, $0.005 per read). The legacy Basic ($200/month) and Pro ($5,000/month) tiers remain only for existing subscribers. See the full breakdown in X API pricing in 2026.
Pay-per-use removes monthly caps but introduces a credit balance to manage. Rate limits still apply at every tier.
How does X API authentication work?
X supports two authentication methods for posting:
- OAuth 1.0a — Three-legged flow using consumer key, consumer secret, access token, and access token secret. The request must include a computed
oauth_signaturein the Authorization header - OAuth 2.0 with PKCE — User authorization flow that returns user access tokens. Requires the
tweet.read,tweet.write,users.read, andoffline.accessscopes
For posting on behalf of users, both require user-level authentication — app-only Bearer tokens cannot create posts.
OAuth 2.0 with PKCE is the recommended approach. The flow:
- Direct the user to
https://x.com/i/oauth2/authorizewith yourclient_id,redirect_uri,scope, and a PKCEcode_challenge - User authorizes your app and is redirected back with an authorization
code - Exchange the code for an access token via POST to
https://api.x.com/2/oauth2/token - Use the access token in the
Authorization: Bearerheader for subsequent requests
Access tokens expire after 2 hours. Use the refresh_token to get a new one.
How do you create a post on X via API?
POST to https://api.x.com/2/tweets:
{ "text": "Your post text here"}Returns:
{ "data": { "id": "1234567890", "text": "Your post text here" }}The text field supports up to 280 characters on the free and basic tiers, or up to 25,000 characters on the Pro tier.
How do you post to X with media?
Media must be uploaded separately before attaching it to a post. The flow is: upload the media, get a media_id, then reference it when creating the post.
POST to https://api.x.com/2/tweets:
{ "text": "Your caption here", "media": { "media_ids": ["<MEDIA_ID>"] }}You can attach up to 4 images, 1 video, or 1 animated GIF per post.
How does the X chunked media upload flow work?
All media uploads use a three-step chunked upload process: INIT, APPEND, FINALIZE.
File size limits
- Images: 5 MB (JPEG, PNG, WebP)
- Animated GIFs: 15 MB
- Videos: 512 MB (MP4, MOV)
Step 1: Initialize
POST to https://api.x.com/2/media/upload/initialize:
{ "media_type": "video/mp4", "total_bytes": 12345678, "media_category": "tweet_video"}The media_category values for posts are:
tweet_image— Imagestweet_gif— Animated GIFstweet_video— Videos
Returns:
{ "data": { "id": "<MEDIA_ID>", "expires_after_secs": 86400 }}Step 2: Append
POST to https://api.x.com/2/media/upload/<MEDIA_ID>/append:
Upload the file in chunks using multipart/form-data with:
media— The binary chunk datasegment_index— Integer starting at 0, incremented for each chunk
Chunk size should be no more than 5 MB per request. For a 12 MB video, you would send three APPEND requests with segment_index values 0, 1, and 2.
Step 3: Finalize
POST to https://api.x.com/2/media/upload/<MEDIA_ID>/finalize
Returns:
{ "data": { "id": "<MEDIA_ID>", "processing_info": { "state": "pending", "check_after_secs": 5 } }}How do you check X media upload status?
Videos and GIFs require server-side processing after finalization. If processing_info is present in the FINALIZE response, you must poll for completion before attaching the media to a post.
GET https://api.x.com/2/media/upload/<MEDIA_ID>:
{ "data": { "processing_info": { "state": "in_progress", "progress_percent": 45, "check_after_secs": 10 } }}Processing states:
pending— Queued for processingin_progress— Currently processing, checkprogress_percentsucceeded— Ready to attach to a postfailed— Processing failed, upload must be retried
Always wait check_after_secs before polling again.
How do you create replies and quote posts on X?
To create a reply, include the reply object:
{ "text": "Your reply", "reply": { "in_reply_to_tweet_id": "<TWEET_ID>" }}To create a quote post, include quote_tweet_id:
{ "text": "Your commentary", "quote_tweet_id": "<TWEET_ID>"}How do you create polls on X via API?
Posts can include a poll:
{ "text": "Which do you prefer?", "poll": { "options": ["Option A", "Option B", "Option C"], "duration_minutes": 1440 }}Duration must be between 5 and 10,080 minutes (7 days).
The same video, through Postproxy
Here’s how it’s done with Postproxy. One simple request with only what matters:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "3 tips that changed how we approach customer onboarding" }, "profiles": ["twitter"], "media": ["https://example.com/video.mp4"] }'One request. Postproxy handles the chunked upload, the processing status polling, and the post creation.
What Postproxy handles
Postproxy manages OAuth tokens and handles the complexity:
- OAuth 2.0 token exchange and automatic refresh
- The three-step chunked media upload (INIT, APPEND, FINALIZE)
- Processing status polling for videos and GIFs
- Unified API for text posts, images, videos, and GIFs
- Rate limit monitoring and request pacing
Your system sends content. Postproxy handles the X-specific implementation.
Connect your X account and start publishing through the Postproxy API.
Postproxy
One API for every social platform
Publish to Instagram, X, LinkedIn, TikTok, YouTube and more with a single request. Free plan, no credit card required.