WhatsApp video: H.264 High profile with B-frames fails on Android
WhatsApp Cloud API says H.264 High profile with B-frames is unsupported on Android and recommends Main or Baseline. What Sume trim does and does not promise.

A WhatsApp video can play on iPhone and fail on Android if it is H.264 High profile with B-frames. Meta's Cloud API media page says WhatsApp supports H.264 video and AAC audio, with a single audio stream or none, and a 16 MB limit for .mp4 and .3gp; it adds that the High profile with B-frames is unsupported on Android clients and recommends Main or Baseline (read 2026-10-03). Sume's video trim, in its default exact mode, re-encodes with libx264 and yuv420p, but its docs do not name a profile or B-frame setting, so you cannot assume the output meets that advice.
The way to handle that gap is to measure, not guess. Here is what is known, what is not, and a safe workflow.
What Meta's page says
The video rows are short. Supported types are 3GPP (.3gp, video/3gpp) and MP4 (.mp4, video/mp4), each with a 16 MB limit. Codecs are H.264 for video and AAC for audio, and the audio must be a single stream or absent. The page then gives the Android caveat about the High profile with B-frames and recommends Main or Baseline (read 2026-10-03). Images are JPEG and PNG up to 5 MB, and stickers are WebP only.
| Requirement | Meta's page | Sume video trim docs |
|---|---|---|
| Container | MP4 or 3GP | MP4 output |
| Video codec | H.264 | exact re-encodes with libx264, yuv420p |
| Audio codec | AAC, one stream or none | exact remuxes kept audio as AAC; drop removes it |
| Size | 16 MB per file | No size field; measure the result |
| Profile | Main or Baseline recommended; High with B-frames unsupported on Android | Not stated, so verify |
What trim gives you, and what it does not
Video trim has two precision modes. exact, the default, is a frame-accurate re-encode with libx264 and yuv420p; AAC audio is kept or dropped with audio. keyframe copies the stream, so the profile and B-frames of the source pass through unchanged. That makes exact the better choice for conforming, and keyframe the wrong one if your source is High profile. The docs do not publish the profile or the B-frame setting of the exact output, and they reject client codec and crf fields, so you cannot request Main or Baseline.
Test on real devices. Send the trimmed file to an Android phone through a test number and see whether it plays, and measure its size first. The script below cuts 20 seconds at 720 x 1280 with exact precision, keeps AAC audio, and prints the byte size against 16 MB. A short vertical clip at that size usually fits well inside the limit, but the number is yours to check.
import os, time, requests
BASE = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def run(path, body, key):
r = requests.post(BASE + path, headers={**H, "Idempotency-Key": key}, json=body)
r.raise_for_status()
job = r.json()["request_id"]
while True:
s = requests.get(f"{BASE}/v1/jobs/{job}/status", headers=H).json()["status"]
if s in ("completed", "failed", "canceled"):
break
time.sleep(3)
return s, (requests.get(f"{BASE}/v1/jobs/{job}/result", headers=H).json() if s == "completed" else None)
status, res = run("/v1/video-trim", {
"video_url": "https://media.sume.com/artifacts/artf_demo/promo.mp4",
"start": 0, "duration": 20, "precision": "exact",
"output": {"width": 720, "height": 1280, "fps": 30}}, "wa-cut-001")
if res:
size = int(requests.head(res["video_url"], allow_redirects=True).headers["Content-Length"])
print(res["video_url"], size, "OK" if size <= 16 * 1024 * 1024 else "over 16 MB")
else:
print(status)
The audio and size rules are easier
Meta's page asks for a single audio stream or none. A trim in exact mode produces one AAC audio track when audio is kept, and none when audio is drop, so that rule is covered by the documented behaviour. If your source has more than one audio track, such as a commentary track, test the result before you trust it, because the Sume docs do not describe how extra tracks are handled.
The 16 MB limit is the one that most often catches teams. A 20-second clip at 720 x 1280 and 30 fps is likely to fit, but a 60-second clip with fast motion may not. Reduce duration, lower output to 540 x 960, or drop to 24 fps and measure again. Sume has no bitrate field, so these three levers are what you have, and each retry is a separate job at $0.02 under the public rate (confirm in GET /v1/catalog).
A safe workflow
Run the cut, download it, and play it on an Android device before you send it to customers. If it fails there, you have two choices: re-encode it with a tool that lets you set the profile, or send a link instead of an attachment. Keep a note of which source and settings played correctly, so the next batch follows the same path.
Sume's video inspect returns probe facts for a hosted clip. The docs on that page do not list a profile field, so read the probe it returns for your file and see whether it reports the codec profile; if it does not, an external probe is the way to check.
Sources
Related posts
More in Developers
- Where a Recast result lives: the media.sume.com URL and how to keep it
A finished h3-max-recast job returns a media.sume.com video artifact. Read it at /v1/jobs/{id}/result, store the Sume URL and download your own copy.
- Which episode finished? Map a Sume run id to your episode record
A Sume Format run does not echo your episode number. Keep a ledger keyed by queue index and run id, and match webhooks on request_id.
- Which limit stopped my agent: 402, queue_full or spend cap
Six different walls look alike from an agent loop. A diagnosis table that tells wallet, run spend cap, queue, request rate, scope and provider credits apart.
- Which Sume API errors should page an engineer: route by category
Route Sume API failures by category: fix-the-input errors go to the caller, quota to finance, queue to a retry, and only internal or unexpected 5xx to on-call.
Written by Sume