Reels ad safe zone at 1080x1920 in pixels and the caption anchor
Meta says leave 14% top, 35% bottom and 6% per side clear. At 1080x1920 that is 269, 672 and 65 px; here is the anchor_ratio that keeps a caption inside.
Meta's Instagram Reels ad specification says to leave roughly 14% of the top, 35% of the bottom and 6% of each side free of text and logos. On a 1080x1920 frame that is about 269 px at the top, 672 px at the bottom and 65 px on each side, leaving a 950 px wide by about 979 px tall box. A burned-in caption line centred at anchor_ratio 0.55 sits inside that box. The numbers below are my arithmetic on Meta's percentages.
What Meta says
The percentages come from Meta's Instagram Reels ad guide, read 2026-10-04: leave roughly 14% of the top, 35% of the bottom and 6% on each side free, so the profile icon, call to action and other interface elements do not cover key creative. The page lists the image spec as 9:16 at 1440x2560 with a 30 MB cap; the safe-zone sentence applies to any asset you place in a Reels ad. The percentages are relative, so they hold at 1080x1920 as well as at 1440x2560.
| Edge | Meta's share | Pixels at 1080x1920 | Keep clear |
|---|---|---|---|
| Top | 14% | 269 px (268.8) | Rows 0 to 268 |
| Bottom | 35% | 672 px | Rows 1248 to 1919 |
| Left and right | 6% each | 65 px (64.8) | Columns 0 to 64 and 1015 to 1079 |
| Usable box | - | 950 x 979 px | Rows 269 to 1247 |
Turning that into an anchor_ratio
Video captions takes design.placement.anchor_ratio, defined in the docs as the line centre as a fraction of frame height. The renderer's own bounds are 0.05 to 0.95. Your line centre must satisfy: centre minus half the line block height is at least 269 px, and centre plus half is at most 1247 px. For a two-line block about 150 px tall, that range is a centre between 344 and 1172 px, or 0.18 to 0.61. Pick 0.55 for a margin: centre 1056 px, block from 981 to 1131 px.
Width has a sibling knob, design.typography.safe_width_ratio: 950 / 1080 is 0.88. Treat that as a starting value and test it, because the docs list the field without a range and an out-of-range number is a 400 at request time. design is not supported on the punch or tiktok-green styles.
def safe_box(w=1080, h=1920):
top, bottom, side = round(h * 0.14), round(h * 0.35), round(w * 0.06)
return {"top": top, "bottom": bottom, "side": side,
"box_w": w - 2 * side, "box_h": h - top - bottom}
def anchor_range(block_px, w=1080, h=1920):
z = safe_box(w, h)
lo = (z["top"] + block_px / 2) / h
hi = (h - z["bottom"] - block_px / 2) / h
return round(lo, 2), round(hi, 2)
print(safe_box())
print(anchor_range(150)) # (0.18, 0.61)Verify on the real file
Do not trust arithmetic alone. Burn the caption, then pull a still with video inspect at an explicit timestamp (frames: { at: [2.5] }, max_edge up to 2160) and look at it with the 35% band masked. Use seek: precise, the default, so the timestamp matches. If the geometry is wrong, restyle with source_caption_id; the docs say that re-burn reuses the word timings, with no second speech-to-text, though a restyle is still a billed render.
Meta's page covers its own placements. TikTok and YouTube publish their own safe-zone templates; do not reuse these numbers there.
Sources
Related posts
More in Media tools
- Reference ingest coverage: how many frames OCR read
The reference-ingest coverage block reports frames decoded, frames OCR read and the OCR rate. Use it to decide when on-screen text needs a second look.
- Reference ingest purpose: qa or remix decides who transcribes
Reference ingest purpose defaults to reference_remix, which transcribes speech. brief_format, face_swap and qa do not. An explicit allow_billed_stt wins.
- Reference ingest shot record: cut types, palette, luma, motion
Each reference-ingest shot carries cut_out type (hard, gradual, end), palette, luma, contrast and a motion class. Turn them into shot length and pacing numbers.
- Reference ingest source block: rotation, vfr and aspect
Before you remake a reference, read the manifest source block: display_aspect, rotation, fps, vfr, codec and has_audio_track. What each field should change.
Written by Sume