What is media upload?
Media upload is the WhatsApp Cloud API call that stores a file on WhatsApp's servers so you can send it. You POST the file as multipart/form-data to https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/media with three form fields — file (the bytes), type (the MIME type, e.g. image/jpeg) and messaging_product set to whatsapp — and the response is a single Media ID: {"id": "1037543291543636"}. That ID then goes into the media object of a Media Message sent through the messages endpoint.
Each media kind has fixed formats and size caps, its Media File Limits. Images: JPEG or PNG, 8-bit RGB or RGBA, up to 5 MB. Video: MP4 or 3GPP with H.264 video and AAC audio (one audio stream), up to 16 MB. Audio: AAC, AMR, MP3, M4A or OGG with the Opus codec (mono), up to 16 MB. Documents: PDF, DOC(X), XLS(X), PPT(X) and TXT, up to 100 MB. Stickers: WebP only, 100 KB static or 500 KB animated. Uploaded files are encrypted and kept for 30 days unless you delete them sooner.
Upload is one of several ways to get a file into a message. For sending, the alternative is to skip upload and pass a public HTTPS link in the media object so WhatsApp fetches the file itself. The Resumable Upload API is a different tool: it uploads sample media for message template review and returns a file handle for the template definition, not a media ID for sending. The reverse direction — getting a customer's file — is Media Retrieval.
Key takeaways
- Media upload is a multipart POST to /{PHONE_NUMBER_ID}/media with
file,typeandmessaging_product=whatsapp; it returns a media ID. - Limits: images 5 MB (JPEG, PNG), video and audio 16 MB, documents 100 MB, stickers 100 KB static or 500 KB animated (WebP).
- Uploaded media is kept for 30 days, so one upload can serve every send of a campaign in that period.
- A file whose type does not match its declared MIME type fails with error 131053 — detect the real type before uploading.
- Template sample media for review goes through the Resumable Upload API instead, which returns a handle, not a media ID.
Walk through an upload, line by line
import fs from "node:fs"; const MB = 1024 * 1024;const LIMITS = { "image/jpeg": 5 * MB, "image/png": 5 * MB, "video/mp4": 16 * MB, "application/pdf": 100 * MB }; export async function uploadMedia(path, mime) { const size = fs.statSync(path).size; if (!LIMITS[mime] || size > LIMITS[mime]) throw new Error("Unsupported type or too large"); const form = new FormData(); form.append("messaging_product", "whatsapp"); form.append("type", mime); form.append("file", new Blob([fs.readFileSync(path)], { type: mime }), "upload"); const res = await fetch( "https://graph.facebook.com/v26.0/" + process.env.PHONE_NUMBER_ID + "/media", { method: "POST", headers: { Authorization: "Bearer " + process.env.ACCESS_TOKEN }, body: form }, ); const body = await res.json(); if (body.error) throw new Error("Upload failed: " + body.error.code); return { mediaId: body.id, uploadedAt: Date.now() };}Step 5 of 5 · line 21
Success is just an id. Store it with the time: it can be reused in sends for 30 days.
Upload (Node.js), step 5 of 5, line 21: Success is just an id. Store it with the time: it can be reused in sends for 30 days.
How does media upload work?
Check the file first
Confirm the real format and size against the limits for its kind. Convert or compress before uploading: a 7 MB PNG must shrink below 5 MB, and video needs H.264 with AAC audio.
POST multipart to /media
Send
file,typeandmessaging_product=whatsappas form fields to/{PHONE_NUMBER_ID}/mediawith your bearer token. Let the HTTP client build the multipart boundary.Store the media ID with a date
The response is
{"id": "..."}. Save it with the upload time, because it stays usable for 30 days.Send by ID
Reference it on the messages endpoint —
"video": {"id": "{MEDIA_ID}"}— in a session message or as a template's media header parameter.Clean up or refresh
Delete files you no longer need with
DELETE /{MEDIA_ID}, and re-upload anything still in use before the 30 days run out.
Key facts at a glance
Media upload: key facts and limits
- Endpoint
- POST /{PHONE_NUMBER_ID}/media (multipart/form-data)
- Form fields
- file, type, messaging_product=whatsapp
- Images
- JPEG, PNG · up to 5 MB
- Video
- MP4, 3GPP (H.264 + AAC) · up to 16 MB
- Audio
- AAC, AMR, MP3, M4A, OGG Opus · up to 16 MB
- Documents
- PDF, DOC(X), XLS(X), PPT(X), TXT · up to 100 MB
- Stickers
- WebP · 100 KB static, 500 KB animated
- Kept for
- 30 days unless deleted
Media upload in code
curl -X POST "https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/media" \-H "Authorization: Bearer {ACCESS_TOKEN}" \-F "file=@invoice-1043.pdf;type=application/pdf" \-F "type=application/pdf" \-F "messaging_product=whatsapp"# {"id":"1037543291543636"}
-F builds multipart/form-data with the right boundary; don't set Content-Type yourself.
Example: Invoices and a catalogue for a Jaipur wholesaler
A Jaipur handicrafts wholesaler sends each order's invoice as a PDF on WhatsApp. When an invoice is generated, a worker checks it is under the 100 MB document limit (they average 180 KB), uploads it to /{PHONE_NUMBER_ID}/media as application/pdf, and passes the returned media ID straight into a utility template with a document header and the filename invoice-1043.pdf. Its 12 MB seasonal catalogue goes to 6,000 retailers: uploaded once on the 1st, the same media ID serves every send over the following two weeks.
Common wrong turns
Common mistakes with media upload
Setting Content-Type by hand
Writing
Content-Type: multipart/form-datayourself drops the boundary, so the server cannot parse the parts. Let curl's-F,requestsorFormDatabuild the header.A MIME type that doesn't match the file
Declaring
image/pngfor a JPEG, or uploading a file merely renamed to .pdf, fails with error 131053. Detect the type from the file's bytes, not its extension.Right extension, wrong codec
An .mp4 encoded with HEVC, or with two audio tracks, does not meet the H.264 + AAC requirement. Transcode to H.264 video with a single AAC audio stream before uploading.
Using /media for template samples
Template review needs a handle from the Resumable Upload API, not a media ID. Media IDs are for sending messages; handles are for template definitions.
Why does media upload matter?
Uploading once and sending by ID is the dependable way to send files at scale. It avoids WhatsApp fetching a link from your server for every recipient — and failing when your CDN is slow or the URL needs authentication — lets you validate type and size before a campaign starts instead of discovering failures message by message, and keeps private files such as invoices, tickets and statements off public URLs.
Good practice
Best practices for media upload
- Compress images well below 5 MB; large photos also load slowly on customers' phones.
- Cache media IDs by file hash so the same file is uploaded once per 30 days.
- Upload campaign media before the send starts, so any failure surfaces before a customer is messaged.
- Set
filenameon document messages so customers see a meaningful name.