What a good link looks like
A link works when opening it in a private browser window downloads the file itself with no login and no intermediate page.Works
Fails
The rules, exactly
Every refusal gives the same message —
Field "url" must be a publicly reachable http(s) URL to the file. — regardless of which rule was broken. That is on purpose: it is actionable either way, and a per-reason message would let someone map a private network from outside by watching which hosts produce which wording.
The two failures, and how to tell them apart
400 invalid_media_url — we refused to even try
400 invalid_media_url — we refused to even try
The URL failed a check before anything was fetched: wrong scheme, malformed, too long, credentials in it, or a private address.Fix the URL. Retrying the same one will never work.
details.field names the field — url, or buttons[N].url for a link button.422 media_fetch_failed — we tried and could not get the file
422 media_fetch_failed — we tried and could not get the file
The URL looked fine but the file could not be downloaded: a 404, an expired signed link, a hostname that does not resolve, a TLS failure, or a page where a file was expected.Open the link in a private browser window. If it does not download the raw file for you, it will not for us either.
Which endpoint for which file
Formats
We do not restrict file types — WhatsApp decides what it can render. In practice:- Images — JPEG and PNG are safest.
- Video — MP4 with H.264 video and AAC audio.
- Audio — MP3 and OGG/Opus arrive as playable voice notes.
- Stickers — WebP. Other formats may arrive as an ordinary image or not render at all.
- Documents — anything. PDF is the most predictable across devices.
mime_type is an optional hint you can send with image, video and document. It is checked for the shape type/subtype only — we never verify it matches the actual file, so a wrong hint is worse than none.
Sizes
There is no size limit in this API, but there are two real ones underneath:- WhatsApp’s own attachment limits. A file that is too big for WhatsApp will fail however it was sent.
- Time. The file has to be fetched during your request. A large file over a slow link can exhaust the request budget and come back as
502 upstream_unavailable.
Keeping links alive
The file is fetched during your API call, not later. So:- A signed URL must still be valid at the moment you send. Generate it right before the call, not the night before.
- Do not delete the file immediately after the API returns
200. The fetch has already happened by then, but keeping it around a while makes debugging far easier. - If your storage is private by default, generate a short-lived public link for the send.