The Wayback Machine - https://web.archive.org/web/20160617154445/https://dev.twitter.com/rest/media/uploading-media

Uploading Media

Guide to Uploading Media

There are a few important concepts to understand when using the media upload endpoints. Uploading media with OAuth can be a bit tricky, so this is an outline of some things to keep in mind, as well as a working sample of how to use this endpoint.

Keep in mind

  • Because the method uses multipart POST, OAuth is handled a little differently. POST or query string parameters are not used when calculating an OAuth signature basestring or signature. Only the oauth_* parameters are used.
  • You may attach up to 4 photos, or 1 animated GIF, or 1 video to a Tweet.
  • The image passed should be the raw binary of the image or binary base64 encoded. There is no need to otherwise encode or escape the contents, as long as the Content-Type is set appropriately (when in doubt: application/octet-stream).
  • When posting base64 encoded images, be sure to set “Content-Transfer-Encoding: base64” on the image part of the message.
  • Multi-part message boundaries must be on their own line and terminated by a CRLF.
  • For working examples of how to POST using this endpoint, we recommend testing with twurl. Also take a look at the Twitter Libraries available.
  • Use the media_id_string provided in the API response from Javascript and other languages that cannot accurately represent a long integer.

Video specifications and recommendations

In addition to file size limits, video files must meet all of the following criteria:

  • Duration should be between 0.5 seconds and 30 seconds
  • Dimensions should be between 32x32 and 1280x1024
  • Aspect ratio should be between 1:3 and 3:1
  • Frame rate should be 40fps or less
  • Must not have open GOP
  • Must use Progressive scan (no interlacing)
  • Must have 1:1 pixel aspect ratio
  • Audio should be mono or stereo
  • Audio must be AAC with Low Complexity profile. High-Efficiency AAC is not supported.

Note that the duration, filesize, fps and other constraints may vary based on the media_category parameter.

The media platform is optimized to handle the specifications of video captured on mobile devices. Developers creating their own video (i.e. not uploading video captured directly from a mobile device) should refer to the table below. Each row represents an upload recommendation, but is not a requirement. All uploads are processed for optimization across multiple platforms.

Orientation Width Height Video Bitrate Audio Bitrate 
Landscape12807202048K128K
Landscape640360768K64K
Landscape320180256K64K
Portrait6406401024K96K
Portrait480480768K64K
Portrait240240256K64K

Animated GIF recommendations

A GIF may fail during Tweet creation even if it is within the filesize limit. Adhere to the following constraints to improve success rates.

  • Resolution should be <= 1280x1080 (width x height)
  • Number of frames <= 350
  • Number of pixels (width * height * num_frames) <= 300 million

In order to get better support for larger GIFs, use the chunked upload endpoint with the media_category parameter. This allows the server to process the GIF file asynchronously, which is a requirement for processing larger files. Pass media_category=tweet_gif to enable async upload behavior for Tweets with an animated GIF.

Example of image media/upload

Twurl v0.9.0 or newer (see the command-line Twurl tool) has support for calling the Twitter API with a multipart message for the POST media/upload endpoint. Use this example as a way to debug your code. Note that many of the Twitter Libraries include methods that will handle this POST correctly.

Using the -t option with twurl will show the full trace of the request and response that was POSTed to the Twitter API.

twurl -H upload.twitter.com -X POST "/1.1/media/upload.json" --file "/path/to/media.jpg" --file-field "media"

{
  "media_id": 553656900508606464,
  "media_id_string": "553656900508606464",
  "size": 998865,
  "image": {
    "w": 2234,
    "h": 1873,
    "image_type": "image/jpeg"
  }
}

Example of chunked video media/upload

Using the chunked POST media/upload endpoint requires an adjusted workflow from single image uploads. For chunked uploads, you must:

  • Initialize the upload using the INIT command
  • Upload each chunk of bytes using the APPEND command
  • Complete the upload using the FINALIZE command
  • Optionally, use the STATUS command to poll for processing status updates

A working example is shown below. Using the -t option with twurl will show the full trace of the request and response that was POSTed to the Twitter API.

INIT

twurl -H upload.twitter.com "/1.1/media/upload.json" -d "command=INIT&media_type=video/mp4&total_bytes=4430752"

{
  "media_id": 601413451156586496,
  "media_id_string": "601413451156586496",
  "expires_after_secs": 86400
}

APPEND

twurl -H upload.twitter.com "/1.1/media/upload.json" -d "command=APPEND&media_id=601413451156586496&segment_index=0" --file /path/to/video.mp4 --file-field "media"

HTTP 2XX will be returned with an empty response body on successful upload.

FINALIZE

twurl -H upload.twitter.com "/1.1/media/upload.json" -d "command=FINALIZE&media_id=601413451156586496"

// Sync finalize response which does not require subsequent STATUS command calls

{
  "media_id": 601413451156586496,
  "media_id_string": "601413451156586496",
  "size": 4430752,
  "expires_after_secs": 86400,
  "video": {
    "video_type": "video/mp4"
  }
}

// Async finalize which requires subsequent STATUS command calls

{
  "media_id": 601413451156586496,
  "media_id_string": "601413451156586496",
  "size": 4430752,
  "expires_after_secs": 86400,
  "processing_info": {
    "state": "pending",
    "check_after_secs": 5 // check after 5 seconds for update using STATUS command
  }
}

STATUS

twurl -H upload.twitter.com "/1.1/media/upload.json?command=STATUS&media_id=601413451156586496"

{
  "media_id":601413451156586496,
  "media_id_string":"601413451156586496",
  "expires_after_secs":85400,
  "processing_info":{
    "state":"in_progress", // state transition flow is pending -> in_progress -> [failed|succeeded]
    "check_after_secs":10, // check for the update after 10 seconds
    "progress_percent":33 // Optional [0-100] int value. Do not use as a replacement for the "state" field.
  }
}