TL;DR, Quick Answer
7 min readmedia_category tells X what the file is for. The initialize endpoint accepts eight values, tweet_image, tweet_video, tweet_gif, amplify_video, dm_image, dm_video, dm_gif and subtitles, and X uses the value to pick which size and duration ceiling to enforce. tweet_video and amplify_video share the same caps on a Post, 20 minutes and 8 GB by default and 125 minutes and 16 GB for Premium, while the Ads API page still says promoted video tops out at 10 minutes and 500 MB. Omitting the value makes X guess from the content type.
What is the twitter api media_category parameter?
Eight values exist for the twitter api media_category parameter, and X defines the job it does in one line: "The Media Category parameter defines the use case of the media file to be uploaded, and can affect file size limits or other constraints enforced for media uploads."
Use case, not file type. media_type already tells X that the bytes are an MP4. media_category tells X whether that MP4 is going on a Post, into a Direct Message or into an ad, and X picks a different ceiling for each answer.
The parameter is optional on POST /2/media/upload/initialize and X documents the fallback: "If media category is not specified, the uploaded media is assumed to be media for a Post (tweet_image, tweet_video, or tweet_gif), depending on the content type."
That default covers Posts and nothing else. Anything bound for a DM, an ad or a subtitle track has to say so, because X has no way to infer it from an MP4.
What does each twitter api media_category value permit?
Each value maps to one surface, and X publishes the complete enum on the initialize endpoint schema.
| Value | Surface it permits | Default cap | X Premium cap |
|---|---|---|---|
tweet_image | Image on a Post | 5 MB | 5 MB |
tweet_gif | Animated GIF on a Post | 15 MB | 15 MB |
tweet_video | Video on a Post | 20 min, 8 GB | 125 min, 16 GB |
amplify_video | Ads and promoted video | 20 min, 8 GB | 125 min, 16 GB |
dm_image | Image in a Direct Message | 5 MB | 5 MB |
dm_gif | Animated GIF in a Direct Message | 15 MB | 15 MB |
dm_video | Video in a Direct Message | 140 s, 512 MB | 10 min, 1 GB |
subtitles | Subtitle file | 1 MB | 1 MB |
Minimum video duration is the same across every video category. X states it as "0.5 seconds" and repeats it as a floor rather than a recommendation.
Two values carry an instruction beyond their limits. amplify_video is required rather than optional for anything promoted, and the Ads API has said so since 2015: "When uploading videos to be used in promoted content, the media_category parameter must be set with a value of amplify_video for all INIT command requests." tweet_gif is the switch that turns on asynchronous processing for large animated GIFs, which X spells out on the best practices page: "In order to process 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."

How does media_category change the size and duration caps?
The category selects which row of X's limit table applies, and the account's subscription selects which column. X names both inputs: limits depend on "the authenticated user's X Premium / verified status, not your developer API plan" and on "the media_category you pass when initializing the upload."
The spread between the categories is wide. A video that fits tweet_video at 20 minutes has to be under 140 seconds to go out as dm_video on the same account, and under 10 minutes even with Premium. Same file, same account, same endpoint, and a factor of eight between the two ceilings.
total_bytes is checked against the category too. X writes that "POST /2/media/upload/initialize accepts total_bytes up to 16 GB. Passing a larger total_bytes than the account is allowed to upload fails at initialize or finalize." Two failure points are named and X does not say which one catches it, so treat both INIT and FINALIZE as places a size rejection can surface.
The category does not change the image rules. Images stay at 5 MB and animated GIFs at 15 MB whether they are bound for a Post or a DM, and Premium moves neither number. Only video duration and video file size respond to the subscription, which is a narrower effect than the full set of X Premium entitlements suggests.

Why does X publish two different limits for amplify_video?
Because the Ads API creatives page and the media documentation disagree, and both are live.
The media pages treat amplify_video as an equal of tweet_video. The best practices page says it outright: "For Posts, Premium and default duration/size caps are the same for tweet_video and amplify_video." Its table gives both 20 minutes and 8 GB by default, 125 minutes and 16 GB with Premium.
AdaptlyPost
Start 7-Day FREE Trial
All-platform analytics
Social Inbox
AI-powered assistant
The Ads API creatives page, in the Promoted Video section, gives a different pair of numbers in a single bullet: "The maximum promoted video length currently allowed is 10 mins with a file size of 500MB or less." It adds a format constraint the media pages do not mention either: "Uploaded video should be either mp4 or mov."
Ten minutes against 125. Five hundred megabytes against 16 gigabytes. Nothing on either page acknowledges the other, and neither carries a date beyond the 2015 note attached to the bullet above it. Build for the smaller pair if the video is going into a campaign, because the ads pipeline is the one that would reject it.
A second mismatch shows up one layer along. The upload endpoints take lowercase values. The Ads API media library takes uppercase ones: "There are four possible category values: AMPLIFY_VIDEO, TWEET_GIF, TWEET_IMAGE, and TWEET_VIDEO." Same four concepts, two spellings, and the media library list omits the DM and subtitle categories entirely.
What happens when you pick the wrong media_category?
The upload succeeds and the next call fails. X names this as the common case: "Using the wrong category (for example a DM category on a Post) is a common reason an upload succeeds and Post create then fails."
That is the shape of almost every media_category mistake. INIT accepts the value, APPEND moves the bytes, FINALIZE returns a clean processing_info, and the rejection arrives at POST /2/tweets with a 403 because upload and posting are enforced separately. A 403 reading "This user is not allowed to post a video longer than N minutes" on a video well under the Post limit is usually a dm_video category doing exactly what it was told.
Three smaller traps sit in the same parameter.
The two upload endpoints do not agree on whether the parameter is optional. On POST /2/media/upload/initialize it is optional and the enum has eight values. On the simple POST /2/media/upload endpoint the schema lists media and media_category as required, and its enum has seven values with amplify_video missing. Promoted video therefore cannot go through the simple upload path at all, which matches X's separate advice to use chunked upload for all video regardless.
Omitting the value on a large GIF gives up the asynchronous processing that large GIFs need, because X ties that behaviour to passing tweet_gif rather than to the file itself.
And subtitles is the odd one out. It is the only value in the enum that is not an image, a GIF or a video, it caps at 1 MB, and its media_type values are text/srt and text/vtt. A subtitle file uploaded without it inherits the Post default and is graded against image rules it was never going to meet.
Every network solves this differently, and the category concept does not travel. Instagram splits the same decision across media_type and the reels specification instead, and TikTok resolves duration by account standing rather than by an upload parameter. On X, the whole decision lives in one optional string you send once, at INIT, and never get to change. If you set it wrong, the upload walkthrough has to start over from the beginning.
Frequently asked questions
What values does the twitter api media_category parameter accept?
Eight: tweet_image, tweet_video, tweet_gif, amplify_video, dm_image, dm_video, dm_gif and subtitles. That is the enum published on POST /2/media/upload/initialize.
Is media_category required on the X API?
It depends on the endpoint. POST /2/media/upload/initialize treats it as optional and guesses a Post category from the content type. The simple POST /2/media/upload endpoint lists it as required.
What is the difference between tweet_video and amplify_video?
Purpose, not size. X requires amplify_video for anything used in promoted content and says the duration and size caps for the two are the same on a Post. The Ads API creatives page still states a lower promoted video ceiling of 10 minutes and 500 MB.
How long can a dm_video be on the X API?
140 seconds by default and 10 minutes for an X Premium or verified account, with file size caps of 512 MB and 1 GB. Minimum duration is 0.5 seconds, same as every other video category.
What error do you get from the wrong media_category?
Usually a 403 Forbidden on POST /2/tweets rather than an error at upload time. X notes that a DM category used on a Post is a common reason an upload succeeds and Post creation then fails.
AdaptlyPost
Start 7-Day FREE Trial
All-platform analytics
Social Inbox
AI-powered assistant
Does media_category affect image size limits?
No. Images stay at 5 MB and animated GIFs at 15 MB across Posts and DMs, and X Premium does not raise either. Only video duration and video file size change with the category and the subscription.
What's the difference between media_type and media_category on the X API?
media_type tells X the format of the file, for example video/mp4. media_category tells X what that file is for, a Post, a Direct Message, a subtitle track or an ad. X uses media_category, not media_type, to decide which size and duration ceiling applies.
What's the minimum video length the X API accepts?
The minimum is 0.5 seconds, and X states it as a floor rather than a recommendation. That minimum is the same across every video category, tweet_video, amplify_video, dm_video and the rest, so switching categories does not lower it.
What video format does the X Ads API require for promoted video?
The Ads API creatives page requires mp4 or mov for uploaded video in promoted content. It states this plainly: uploaded video should be either mp4 or mov. The media documentation pages do not repeat this constraint anywhere else.
Does the simple media upload endpoint support promoted video?
Promoted video cannot go through the simple upload endpoint. Its enum lists seven media_category values and leaves amplify_video out, while POST /2/media/upload/initialize keeps all eight. That gap lines up with X's separate advice to use chunked upload for all video regardless of category.
Put this into practice with AdaptlyPost
Was This Article Helpful?
Let us know what you think!
See us more often in Google
One click marks AdaptlyPost as a preferred source, so our articles sit higher in your Top Stories, AI Mode, and AI Overviews.
Before you go...
AdaptlyPost
Schedule your content across all platforms
Manage all your social media accounts in one place with AdaptlyPost.
All-platform analytics
Social Inbox
AI-powered assistant
Related Glossary Terms


The Instagram Resumable Upload Session and the rupload Host
An Instagram resumable upload starts with upload_type=resumable on /media, then a POST to rupload.facebook.com carrying offset and file_size headers.


What the instagram_business_content_publish Scope Actually Grants
The instagram_business_content_publish scope lets an app create organic Instagram posts, and it depends on instagram_business_basic on every call.


Every Limit the Instagram Reels API Puts on Your Video
The Instagram Reels API caps a reel at 15 minutes and 300 MB and rejects anything but MOV or MP4. Every documented spec, plus the error each violation returns.
Related Articles


How TikTok API chunk_size And total_chunk_count Must Add Up
TikTok API chunk_size has a 5 MB floor, a 64 MB ceiling, a 128 MB final chunk, and a total_chunk_count that must round down, not up.


What Instagram's content_publishing_limit Endpoint Returns
Instagram's content_publishing_limit endpoint returns quota_usage plus a config block holding quota_total 50 and quota_duration 86400 seconds.


Why a LinkedIn Access Token Expires After 60 Days
Every LinkedIn access token runs 60 days and expires_in returns 5184000. Refresh token rules, what kills a token early, and how Meta's 60 days differ.

