Skip to main content
This guide walks you through uploading videos and large media files using the chunked upload workflow. For video or large media uploads, you must:
  1. INIT — Initialize the upload and get a media_id
  2. APPEND — Upload each chunk of the file
  3. FINALIZE — Complete the upload
  4. STATUS — (If needed) Wait for processing to complete
See this sample code for a complete Python example.

Step 1: Initialize upload (INIT)

Start the upload session to get a media_id:
cURL
curl -X POST "https://api.x.com/2/media/upload" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: multipart/form-data" \
  -F "command=INIT" \
  -F "media_type=video/mp4" \
  -F "total_bytes=1048576" \
  -F "media_category=amplify_video"
Response:
{
  "data": {
    "id": "1880028106020515840",
    "media_key": "13_1880028106020515840",
    "expires_after_secs": 1295999
  }
}

Step 2: Upload chunks (APPEND)

Upload each chunk of the file. For example, split a 3 MB file into 3 chunks:
cURL
curl -X POST "https://api.x.com/2/media/upload" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: multipart/form-data" \
  -F "command=APPEND" \
  -F "media_id=1880028106020515840" \
  -F "segment_index=0" \
  -F "media=@/path/to/chunk1.mp4"
Chunking advantages:
  • Improved reliability on slow networks
  • Uploads can be paused and resumed
  • Failed chunks can be retried individually

Step 3: Finalize upload (FINALIZE)

Complete the upload after all chunks are sent:
cURL
curl -X POST "https://api.x.com/2/media/upload" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: multipart/form-data" \
  -F "command=FINALIZE" \
  -F "media_id=1880028106020515840"
Response:
{
  "data": {
    "id": "1880028106020515840",
    "media_key": "13_1880028106020515840",
    "size": 1048576,
    "expires_after_secs": 86400,
    "processing_info": {
      "state": "pending",
      "check_after_secs": 1
    }
  }
}
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)

If processing_info was returned, poll for processing completion:
cURL
curl "https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN"
Processing states: pendingin_progresssucceeded or failed

Step 5: Create Post with media

Once processing is complete, create a Post with the media:
cURL
curl -X POST "https://api.x.com/2/tweets" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Check out this video!",
    "media": {
      "media_ids": ["1880028106020515840"]
    }
  }'

Media categories

CategoryDescription
tweet_imageImage for a Post
tweet_gifAnimated GIF for a Post
tweet_videoVideo for a Post
amplify_videoAmplify video

Next steps