Skip to main content

How to Download WhatsApp Media with Node.js How to Download WhatsApp Media with Node.js

How to Download WhatsApp Media with Node.js

whatsmeow-node can download any media type — images, videos, audio, documents, and stickers — with a single downloadAny() call. The file is decrypted and saved to a temporary path on disk.

Prerequisites

Step 1: Listen for Incoming Messages

client.on("message", async ({ info, message }) => {
if (info.isFromMe) return;
// Process the message...
});

Step 2: Detect the Media Type

Check which media field is present on the message:

function getMediaType(message: Record<string, unknown>): string | null {
if (message.imageMessage) return "image";
if (message.videoMessage) return "video";
if (message.audioMessage) return "audio";
if (message.documentMessage) return "document";
if (message.stickerMessage) return "sticker";
return null;
}

Step 3: Download with downloadAny()

const mediaType = getMediaType(message);
if (!mediaType) return;

const filePath = await client.downloadAny(message);
console.log(`Downloaded ${mediaType} to: ${filePath}`);

downloadAny() auto-detects the media type from the message and downloads the file. It handles decryption internally and returns the path to a temporary file.

Step 4: Save to a Permanent Location

The temp file may be cleaned up by the OS. Copy it somewhere permanent:

import { copyFile, mkdir } from "node:fs/promises";
import path from "node:path";

const MEDIA_DIR = "./downloads";

async function saveMedia(
tempPath: string,
mediaType: string,
messageId: string,
): Promise<string> {
const dir = path.join(MEDIA_DIR, mediaType);
await mkdir(dir, { recursive: true });

// Get the file extension from the temp path
const ext = path.extname(tempPath) || getDefaultExtension(mediaType);
const dest = path.join(dir, `${messageId}${ext}`);

await copyFile(tempPath, dest);
return dest;
}

function getDefaultExtension(mediaType: string): string {
switch (mediaType) {
case "image": return ".jpg";
case "video": return ".mp4";
case "audio": return ".ogg";
case "sticker": return ".webp";
default: return ".bin";
}
}

Complete Example

A media saver bot that organizes downloads by type:

import { createClient } from "@whatsmeow-node/whatsmeow-node";
import { copyFile, mkdir } from "node:fs/promises";
import path from "node:path";

const client = createClient({ store: "session.db" });
const MEDIA_DIR = "./downloads";

function getMediaType(message: Record<string, unknown>): string | null {
if (message.imageMessage) return "image";
if (message.videoMessage) return "video";
if (message.audioMessage) return "audio";
if (message.documentMessage) return "document";
if (message.stickerMessage) return "sticker";
return null;
}

client.on("message", async ({ info, message }) => {
if (info.isFromMe) return;

const mediaType = getMediaType(message);
if (!mediaType) return;

console.log(`${mediaType} from ${info.pushName}`);

const tempPath = await client.downloadAny(message);

// Save permanently, organized by type
const dir = path.join(MEDIA_DIR, mediaType);
await mkdir(dir, { recursive: true });
const ext = path.extname(tempPath) || ".bin";
const dest = path.join(dir, `${info.id}${ext}`);
await copyFile(tempPath, dest);

console.log(`Saved to ${dest}`);

// Acknowledge receipt
await client.markRead([info.id], info.chat, info.sender);
await client.sendMessage(info.chat, {
conversation: `Saved your ${mediaType}!`,
});
});

async function main() {
const { jid } = await client.init();
if (!jid) {
console.error("Not paired! See: How to Pair WhatsApp");
process.exit(1);
}
await client.connect();
console.log("Listening for media...");

process.on("SIGINT", async () => {
await client.disconnect();
client.close();
process.exit(0);
});
}

main().catch(console.error);

Common Pitfalls

Temp files aren't permanent

downloadAny() saves to a temporary directory. The OS may delete these files at any time. Always copy media to a permanent location if you need to keep it.

Field naming differences

When receiving messages, fields use proto casing: fileSHA256, fileEncSHA256. When passing arguments to downloadMediaWithPath(), use the method's parameter names: fileHash, encFileHash. See Troubleshooting.

Media links expire

WhatsApp media URLs expire after some time. Download media promptly when you receive it — you can't download it later once the link expires.