- INIT —
POST /2/media/upload/initialize— start the session and get amedia_id - APPEND —
POST /2/media/upload/{id}/append— upload each chunk - FINALIZE —
POST /2/media/upload/{id}/finalize— complete the upload - STATUS —
GET /2/media/upload— wait for processing whenprocessing_infois returned
Do not send
command=INIT, command=APPEND, or command=FINALIZE to POST /2/media/upload. Those command-style parameters were the previous upload protocol. The v2 flow uses the dedicated paths above. command=STATUS is still used only on the status GET.media_category. See size and duration limits. A successful upload can still be rejected when you attach the media_id to POST /2/tweets.
Step 1: Initialize upload (INIT)
Start the upload session. Send a JSON body — not multipart form fields.cURL
tweet_video for a regular Post. Use amplify_video for Ads creatives. See media categories.
Step 2: Upload chunks (APPEND)
Upload each chunk toPOST /2/media/upload/{id}/append. Keep each segment at or below 5 MB (the server maximum is 8 MB). Segments are indexed from 0 and increase by one for each chunk.
An upload session accepts up to 10,000 segments, so valid
segment_index values are 0 through 9999. This applies to every media category and account tier. A 16 GB video takes about 2,048 APPEND requests at 8 MB per segment, or about 3,277 at 5 MB.cURL
Chunking advantages:
- Improved reliability on slow networks
- Uploads can be paused and resumed
- Failed chunks can be retried individually
Pace APPEND requests for large files
APPEND requests count against thePOST /2/media/upload/:id/append rate limit. For self-serve tiers (Basic, Pro, and pay-per-use), the limit is 1,875 requests per 15 minutes per user. Enterprise packages have higher limits. Treat the x-rate-limit-limit header as the authoritative limit for your plan. A multi-GB upload can reach this limit before it finishes.
An upload session stays valid for 24 hours, and each APPEND response returns the session’s expires_at. Pausing until x-rate-limit-reset doesn’t expire the upload.
- Read the
x-rate-limit-limit,x-rate-limit-remaining, andx-rate-limit-resetheaders on each APPEND response. - If you receive a
429, wait until thex-rate-limit-resettime, then retry the samesegment_index. - Don’t restart from segment
0. Resume from the first segment that didn’t succeed, then call FINALIZE after the last segment. - Use larger segments (up to 8 MB) to reduce the number of APPEND requests. At 8 MB, a 16 GB upload spans roughly two 15-minute windows at the 1,875-request limit.
Step 3: Finalize upload (FINALIZE)
Complete the upload after all chunks are sent:cURL
Example response
If
processing_info is returned, proceed to Step 4 to wait for processing. If not, the media is ready to use.Step 4: Check status (STATUS)
Ifprocessing_info was returned, poll until processing completes:
cURL
pending → in_progress → succeeded or failed
Step 5: Create Post with media
Once processing is complete, create a Post with the media. Duration and size are checked again against the posting user’s entitlement.cURL
N is the posting user’s duration cap (20 minutes by default, 125 minutes for Premium / verified).
Media categories
If you omit
media_category, the upload is treated as Post media (tweet_image, tweet_video, or tweet_gif) based on content type.
Next steps
Best practices
File constraints, codecs, and duration limits
Create Posts
Post with media
Initialize
INIT endpoint reference
Create Post
POST /2/tweets reference