Meta Ads Video Specs: All Four Ratios From One Master File

You've got one master render and Meta wants it in shapes you didn't export. Every spec page you find lists the same dimension table and stops there, which leaves you back in the editor doing four exports with four sets of text repositioning. The dimensions aren't the hard part. Deriving all four from one file without breaking your text placement is.
Quick answer: The meta ads video specs that matter reduce to four aspect ratios and one encode: 4:5 at 1080x1350 for Facebook and Instagram feed, 9:16 at 1080x1920 for Stories and Reels, 1:1 at 1080x1080 as the square fallback, and 16:9 at 1920x1080 for in-stream, all H.264 video plus AAC audio in an MP4 under 4 GB at 30 fps with square pixels. Render one 1080x1920 master and cut the rest out of it with center crops (crop=1080:1350:0:285for 4:5,crop=1080:1080:0:420for 1:1) instead of re-exporting from an editor four times. If you'd rather not maintain the filter graph, the Listing Kit blueprint on FFmpeg Micro emits all four variants from one upload.
Meta has 20+ placements and four shapes
Meta's placement list runs past twenty entries once you count Facebook Feed, Instagram Feed, Explore, Profile Feed, Stories, Reels, Reels overlay, in-stream, Marketplace, Search results, right column, Messenger inbox, and Audience Network. The creative you upload only has to exist in four aspect ratios, because Meta maps every placement onto one of them.
| Ratio | Pixels | Where it serves | How you get it from a 9:16 master |
|---|---|---|---|
| 4:5 | 1080x1350 | Facebook and Instagram feed, Explore | `crop=1080:1350:0:285` |
| 9:16 | 1080x1920 | Stories, Reels, Reels overlay | the master itself |
| 1:1 | 1080x1080 | Marketplace, Search, right column, Messenger | `crop=1080:1080:0:420` |
| 16:9 | 1920x1080 | In-stream video | scale to 608 wide, pad onto a blurred fill |
Three of those four are a single crop expression with a y offset. The offsets are just centering math on a 1920-pixel-tall frame: (1920 - 1350) / 2 = 285 and (1920 - 1080) / 2 = 420. Nothing else changes, which is why the whole set can come out of one decode.
The encode envelope that all four placements share
Meta's accepted encode has been stable for years, and it's the same for every placement: H.264 in an MP4 or MOV container, stereo AAC audio at 128 kbps or higher, square pixels, progressive scan, a fixed frame rate at or below 30 fps, and a file under 4 GB. Set -pix_fmt yuv420p or your ad will preview fine locally and show up as a black rectangle in Ads Manager.
Fixed frame rate is the one people skip. Screen recorders, OBS in some configurations, and most phone screen captures produce variable frame rate, and VFR through a crop filter drifts audio out of sync over a 30-second spot. -fps_mode cfr -r 30 pins it.
Bitrate isn't worth agonizing over at these sizes. A 30-second 1080x1920 ad at -crf 20 -preset veryfast typically lands between 4 and 6 Mbps, so roughly 15 to 22 MB per variant. You are three orders of magnitude under the 4 GB cap, and Meta re-encodes on ingest anyway. Send it clean rather than small.
Why the master should be 9:16, not 16:9
A 9:16 master is the right source because every other Meta ratio is a subset of it. Crop a 1080x1920 frame down and you keep full horizontal resolution in the 4:5 and 1:1 outputs. Go the other way, from a 1920x1080 master to 9:16, and you're throwing away 68% of the width and guessing which third of the frame holds the subject. FFmpeg won't guess for you, which is the whole reason auto-reframing to vertical isn't a filter.
This lines up with where ad creative comes from now. The Gemini API exposes exactly two aspect ratios for Veo, 16:9 and 9:16, and Sora's size options resolve to the same two shapes. If you're generating ad footage, 4:5 and 1:1 are not available as a parameter at any price. They're a post-generation step every single time, so you may as well choose 9:16 at generation and derive the rest.
The 16:9 in-stream variant is the one that genuinely can't be cropped out of a vertical master. Scale the whole frame to 1080 tall (608 wide after rounding to an even number) and pad it onto a blurred, scaled-up copy of itself. It looks intentional, and in-stream is the lowest-volume placement of the four, so the compromise lands where it costs least.
The safe band is the center square
Text and logos that survive every crop and every piece of platform UI live between y=420 and y=1500 of a 1080x1920 master. That band is exactly the 1:1 crop, and here's the derivation: Meta advises keeping key elements out of the top 14% and bottom 20% of a Stories or Reels frame, which at 1920 tall is 269 pixels off the top and 384 off the bottom, leaving y=270 to y=1536. Intersect that with the 4:5 crop (y=285 to 1635) and the 1:1 crop (y=420 to 1500), and the center square is what's left.
The trap is that the 4:5 crop is more forgiving than Reels, so a lower third placed at y=1560 looks perfectly centered in the feed version and sits underneath the caption and CTA chrome in Reels. Compose to the square, check the tall one.
This is the same conversion problem as TikTok's safe zone margins: platforms publish percentages and UI mockups, FFmpeg wants integers, and nobody publishes the bridge.
One decode, four files
FFmpeg can write all four variants from a single input in one pass by splitting the decoded video inside a filter graph and mapping each branch to its own output file. You decode once instead of four times, which on a one-minute master is the difference between about eight seconds and about thirty.
ffmpeg -i master_1080x1920.mp4 \
-filter_complex "\
[0:v]split=3[feed][sq][wide]; \
[feed]crop=1080:1350:0:285,setsar=1[v45]; \
[sq]crop=1080:1080:0:420,setsar=1[v11]; \
[wide]split=2[wfg][wbg]; \
[wfg]scale=-2:1080,setsar=1[fg]; \
[wbg]scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,boxblur=20:2,setsar=1[bg]; \
[bg][fg]overlay=(W-w)/2:0[v169]" \
-map "[v45]" -map 0:a? -c:v libx264 -preset veryfast -crf 20 -profile:v high \
-pix_fmt yuv420p -fps_mode cfr -r 30 -c:a aac -b:a 128k -ac 2 \
-movflags +faststart meta_feed_4x5.mp4 \
-map "[v11]" -map 0:a? -c:v libx264 -preset veryfast -crf 20 -profile:v high \
-pix_fmt yuv420p -fps_mode cfr -r 30 -c:a aac -b:a 128k -ac 2 \
-movflags +faststart meta_square_1x1.mp4 \
-map "[v169]" -map 0:a? -c:v libx264 -preset veryfast -crf 20 -profile:v high \
-pix_fmt yuv420p -fps_mode cfr -r 30 -c:a aac -b:a 128k -ac 2 \
-movflags +faststart meta_instream_16x9.mp4 \
-map 0:v -map 0:a? -c copy -movflags +faststart meta_reels_9x16.mp4
The last output stream-copies the master into the 9:16 deliverable with no re-encode, because the copy restriction is per output stream, not per command. You can filter three outputs and copy a fourth in the same invocation, which is the useful half of the filtering-and-streamcopy rule. If your master isn't already H.264/AAC/CFR, swap that last -c copy for the same encoder settings as the others.
Running this across a catalog means hosting FFmpeg somewhere, keeping the binary current, and babysitting jobs that outlive a serverless timeout. If you'd rather skip that, FFmpeg Micro's Listing Kit blueprint takes one upload and returns all four platform-ready variants, and the same job runs as one REST call from n8n, Make, Zapier, or an AI agent over MCP. There's a free tier, so you can put a real master through it before deciding.
Where this breaks
Four failure modes account for almost every broken batch, and none of them announce themselves clearly.
- The master already has black bars. Cropping a letterboxed source gives you bars inside your new frame. Detect and strip them first, per file, because cropdetect isn't a one-shot value you can reuse across a library.
- Odd dimensions. libx264 with yuv420p needs even width and height. A crop to 1080x1351 dies with
width not divisible by 2. Use-2in scale expressions and keep crop heights even. - Non-square pixels. A source with SAR 4:3 will crop at the wrong visual proportions and Meta wants square pixels regardless.
setsar=1after every geometry filter. - No audio stream. Generated clips and silent product loops often have none, and a bare
-map 0:afails the whole command with stream map matches no streams. The?in-map 0:a?makes it optional. If a placement needs audio, add-f lavfi -i anullsrc=r=48000:cl=stereoas a second input.
Verify before upload with ffprobe -v error -show_entries stream=width,height,sample_aspect_ratio,avg_frame_rate,codec_name -of default=nw=1 meta_feed_4x5.mp4. Thirty seconds of checking beats a rejected ad set at 6 a.m.
When you should let Meta crop instead
Meta's automatic cropping inside Advantage+ placements is fine for one case: footage with no burned-in text, no logo, and a subject parked in the middle of the frame. Meta will fit it to each placement and nothing important gets clipped.
The moment you burn in a hook, a price, a lower third, or a logo, automatic cropping becomes the reason your CTA ends up behind the Reels caption bar. Pre-rendering the four variants is also what lets you run them as separate ad sets and read performance per ratio, which you can't do when one asset is being reshaped server side.
And if your creative needs different copy per placement rather than the same frame cropped differently, this pipeline is the wrong tool. That's a job for a template-driven render system where each variant has its own layout, not a crop expression.
FAQ
What size should a Facebook ad video be?
A Facebook ad video should be 1080x1350 (4:5) for feed placements, which is the shape that takes the most vertical space in the feed without triggering a crop. Export 1080x1920 (9:16) alongside it for Stories and Reels, and 1080x1080 (1:1) for Marketplace and right-column placements. All three use H.264 video with AAC audio in an MP4.
Are 4:5 video ad dimensions still the default for Meta feed?
The 4:5 ratio at 1080x1350 pixels remains Meta's recommended feed dimension for both Facebook and Instagram. Instagram stopped supporting taller-than-4:5 crops in the feed years ago, so 9:16 creative uploaded as a feed asset gets center-cropped to 4:5 anyway. Doing that crop yourself means you control which part of the frame survives.
Can I upload one video and let Meta resize it for every placement?
Meta will auto-crop a single upload across placements through Advantage+ placements, and it works acceptably when the video has no burned-in text or logo. Any on-screen text positioned for one ratio can land under platform UI or outside the frame in another, so creative with graphics should be rendered per ratio.
What's the maximum file size for a Meta video ad?
Meta accepts video ad files up to 4 GB. In practice a 1080x1920 ad encoded at CRF 20 runs about 0.5 MB per second of footage, so a 60-second spot lands near 30 MB and file size is never the binding constraint. Duration limits per placement will bite you long before size does.
Do I need a 16:9 version for Meta ads?
You need 16:9 at 1920x1080 only for in-stream video placements, and in-stream carries far less volume than feed, Stories, and Reels. If you're cutting scope, ship 4:5, 9:16, and 1:1 first and add the in-stream variant once the other three are running.
Point your master at the Listing Kit blueprint and you'll have all four placement files back without installing anything. Sign up free and run your next ad through it.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

Extract the Last Frame in FFmpeg to Chain Veo 3 Clips Cleanly
Extract last frame FFmpeg commands return the wrong frame after a fast seek. Use -sseof with -update 1, then join Veo 3 and Sora 2 clips with no seam.

Veo and Sora Lock AI Video Aspect Ratio. Crop 4:5 and 1:1.
Veo and Sora render only 16:9 and 9:16. Get the AI video aspect ratio math and FFmpeg crops that turn one 9:16 generation into 4:5, 1:1, and 16:9 files.

The Telegram Bot Video Size Limit Isn't 50 MB. It's Three Caps.
The telegram bot video size limit is 50 MB, or 20 MB by URL. Measure with ffprobe, compute a bitrate budget for a 48 MB ceiling, then re-encode or split.
Skip the command line
The Listing Video Kit blueprint runs the same job for you: upload, done.
Run it (free)