Drei Code-Panels nebeneinander zeigen denselben WhatsApp-Cloud-API-Sendecode in Node.js, Python und PHP auf dunklem Terminal-Hintergrund
Abschnitte: API-Grundlagen · Textnachricht · Vorlage mit Variablen · Bild & Dokument · Interaktive Buttons · Standort · Massenversand mit Rate-Limiting · Fehlerbehandlung & Retry · Vollständige Klassen-Wrapper (Node / Python / PHP)

API-Grundlagen: der eine Endpunkt, durch den alles läuft

Jede ausgehende WhatsApp-Nachricht — unabhängig vom Typ — läuft über einen HTTP-POST-Endpunkt. Der Nachrichtentyp und der Inhalt stehen im JSON-Body. Das war's. Keine WebSockets, kein Streaming, kein Long-Polling.

Drei Werte, die Sie brauchen, bevor irgendein Code läuft:

  • Phone Number ID — eine numerische ID für Ihre WhatsApp-Business-Nummer (sieht aus wie 123456789012345). Unterschiedlich von Ihrer tatsächlichen Telefonnummer.
  • Access Token — ein permanenter System-User-Token mit der Berechtigung whatsapp_business_messaging. Verwenden Sie niemals einen temporären User-Token in der Produktion.
  • Telefonnummer des Empfängers — im E.164-Format: Ländercode + Nummer, ohne Leerzeichen, ohne Bindestriche, ohne Pluszeichen im JSON-Wert (z. B. 15550001234 für eine US-Nummer, oder mit + ebenfalls akzeptiert: +15550001234).

Ein erfolgreicher Versand gibt HTTP 200 mit einem Body wie diesem zurück:

Nachrichtentypen: Session vs. Vorlage

Bevor Sie einen Nachrichtentyp wählen, müssen Sie wissen, ob Sie sich in einem Service-Fenster befinden. Das Session-Fenster (24 Std. nachdem ein Kunde Ihnen geschrieben hat, oder 72 Std. nach einem Click-to-WhatsApp-Anzeigenklick) bestimmt, welche Nachrichtentypen kostenlos und uneingeschränkt sind.

text
Reiner Text. Markdown-ähnliches WhatsApp-Format unterstützt. Bis zu 4.096 Zeichen.
Frei im Fenster
template
Vorab genehmigtes Format mit Variablen-Platzhaltern. Erforderlich zum Initiieren außerhalb des Fensters.
Genehmigung nötig
image
JPEG oder PNG. Max. 5 MB. URL oder Media ID. Optionale Bildunterschrift.
Frei im Fenster
document
PDF, DOCX, XLSX. Max. 100 MB. URL oder Media ID. Dateiname sichtbar.
Frei im Fenster
video
MP4 oder 3GPP. Max. 16 MB. URL oder Media ID. Optionale Bildunterschrift.
Frei im Fenster
interactive
Button-Antworten (max. 3) oder Listennachrichten (max. 10 Einträge). Kunde tippt zum Antworten.
Frei im Fenster
location
Sendet lat/lng mit optionalem Namen und Adresse. Wird als Karte in WhatsApp gerendert.
Frei im Fenster
audio
MP3 oder AAC. Max. 16 MB. URL oder Media ID. Keine Bildunterschrift unterstützt.
Frei im Fenster

Eine Textnachricht senden

Der einfachste Versand. Funktioniert im 24-Stunden-Service-Fenster ohne Meta-Gebühren (innerhalb des Kontingents von 1.000 kostenlosen Service-Konversationen/Monat). Außerhalb des Fensters verwenden Sie stattdessen eine Vorlage.

Node.js (native fetch)
sendText.js
async function sendText(to, message) { const url = `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`; const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', recipient_type: 'individual', to, type: 'text', text: { body: message, preview_url: false }, }), }); if (!res.ok) throw new Error(`WhatsApp error: ${await res.text()}`); return (await res.json()).messages[0].id; // wamid — für Status-Tracking speichern } // Verwendung const messageId = await sendText('+15550001234', 'Hello from WhatsApp Cloud API! 👋');
Python (requests)
send_text.py
import os, requests def send_text(to: str, message: str) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "recipient_type": "individual", "to": to, "type": "text", "text": { "body": message, "preview_url": False }, } ) res.raise_for_status() return res.json()["messages"][0]["id"] # Verwendung message_id = send_text("+15550001234", "Hello from WhatsApp Cloud API! 👋")
PHP (cURL)
send_text.php
function sendText(string $to, string $message): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $body = [ 'messaging_product' => 'whatsapp', 'recipient_type' => 'individual', 'to' => $to, 'type' => 'text', 'text' => ['body' => $message, 'preview_url' => false], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $response = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) { throw new RuntimeException('WhatsApp error: ' . json_encode($response)); } return $response['messages'][0]['id']; // für Status-Tracking speichern } // Verwendung $messageId = sendText('+15550001234', 'Hello from WhatsApp Cloud API! 👋');

Eine Vorlagen-Nachricht mit Variablen senden

Vorlagen sind erforderlich für die erste Nachricht einer neuen Konversation oder jede Nachricht, die außerhalb des 24-Stunden-Service-Fensters gesendet wird. Vorlagen werden im WhatsApp Manager definiert, von Meta genehmigt und können Variablen-Platzhalter {{1}} {{2}} enthalten. Ihr API-Aufruf füllt diese Variablen zum Sendezeitpunkt aus.

Zeitrahmen der Vorlagengenehmigung: Einfache Utility-Vorlagen (Bestellbestätigungen, Versand-Updates) werden typischerweise in 1–24 Stunden genehmigt. Marketing-Vorlagen brauchen 1–5 Werktage. Sie können den Genehmigungsstatus per Webhook-Abonnement message_template_status_update oder im WhatsApp Manager prüfen. Vorlagen mit abgelehntem Status können nicht gesendet werden — das Senden einer nicht-APPROVED-Vorlage gibt einen 400-Fehler zurück.
Node.js
sendTemplate.js
async function sendTemplate(to, templateName, languageCode, variables = []) { const url = `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`; const body = { messaging_product: 'whatsapp', to, type: 'template', template: { name: templateName, language: { code: languageCode }, // z. B. 'en', 'pt_BR', 'de' components: variables.length ? [{ type: 'body', parameters: variables.map(v => ({ type: 'text', text: v })), }] : [], }, }; const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); if (!res.ok) throw new Error(`Template send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Beispiel: Vorlage "order_confirmation" mit Body-Text {{1}}="order #1234" {{2}}="2h" await sendTemplate( '+15550001234', 'order_confirmation', 'en', ['order #1234', '2 hours'] );
Python
send_template.py
def send_template(to: str, template_name: str, language_code: str, variables: list = []) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" components = [] if variables: components.append({ "type": "body", "parameters": [{ "type": "text", "text": v } for v in variables], }) res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": "template", "template": { "name": template_name, "language": { "code": language_code }, "components": components, }, } ) res.raise_for_status() return res.json()["messages"][0]["id"] # Verwendung send_template("+15550001234", "order_confirmation", "en", ["order #1234", "2 hours"])
PHP
send_template.php
function sendTemplate(string $to, string $templateName, string $langCode, array $variables = []): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $components = []; if (!empty($variables)) { $components[] = [ 'type' => 'body', 'parameters' => array_map( fn($v) => ['type' => 'text', 'text' => $v], $variables ), ]; } $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'template', 'template' => [ 'name' => $templateName, 'language' => ['code' => $langCode], 'components' => $components, ], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; }

Ein Bild oder Dokument senden

Medien-Nachrichten verweisen auf eine Datei entweder per URL (die Datei muss öffentlich über HTTPS erreichbar sein) oder per Media ID (erhalten nach dem Hochladen der Datei auf Metas Medien-Endpunkt). URL ist für die meisten Anwendungsfälle einfacher — die Cloud API holt sie und cacht sie. Media IDs sind besser für häufig wiederverwendete Assets wie Produktbilder oder gebrandete PDFs.

Node.js
sendMedia.js
// Bild per öffentlicher URL senden async function sendImage(to, imageUrl, caption = '') { return sendMedia(to, 'image', { link: imageUrl, caption }); } // Dokument per öffentlicher URL senden async function sendDocument(to, docUrl, filename, caption = '') { return sendMedia(to, 'document', { link: docUrl, filename, caption }); } // Generischer Medien-Sender — funktioniert für image, video, audio, document async function sendMedia(to, type, mediaObj) { const res = await fetch( `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', to, type, [type]: mediaObj, // { link, caption } ODER { id, caption } }), } ); if (!res.ok) throw new Error(`Media send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Verwendung await sendImage('+15550001234', 'https://yourdomain.com/promo.jpg', 'Check out our new product! 🚀'); await sendDocument('+15550001234', 'https://yourdomain.com/invoice.pdf', 'invoice-2026.pdf');
Python
send_media.py
def send_media(to: str, media_type: str, media_obj: dict) -> str: url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": media_type, media_type: media_obj, # z. B. {"link": url, "caption": "..."} } ) res.raise_for_status() return res.json()["messages"][0]["id"] def send_image(to: str, url: str, caption: str = "") -> str: return send_media(to, "image", { "link": url, "caption": caption }) def send_document(to: str, url: str, filename: str, caption: str = "") -> str: return send_media(to, "document", { "link": url, "filename": filename, "caption": caption })
PHP
send_media.php
function sendMedia(string $to, string $type, array $mediaObj): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => $type, $type => $mediaObj, // ['link' => url, 'caption' => '...'] ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; } // Verwendung sendMedia('+15550001234', 'image', ['link' => 'https://yourdomain.com/img.jpg', 'caption' => 'New product!']); sendMedia('+15550001234', 'document', ['link' => 'https://yourdomain.com/invoice.pdf', 'filename' => 'invoice.pdf']);

Eine interaktive Nachricht mit Buttons senden

Interaktive Button-Nachrichten erlauben Kunden, mit einem Tap statt mit Tippen zu antworten. Maximal 3 Buttons pro Nachricht. Button-Titel sind auf 20 Zeichen begrenzt. Wenn ein Kunde einen Button tippt, empfängt Ihr Webhook eine Nachricht vom Typ interactive mit der id des Buttons in msg.interactive.button_reply.id.

Node.js
sendButtons.js
async function sendButtons(to, bodyText, buttons) { // buttons: [{ id: 'btn_yes', label: 'Yes' }, { id: 'btn_no', label: 'No' }] const res = await fetch( `https://graph.facebook.com/v21.0/${process.env.WA_PHONE_ID}/messages`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.WA_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', to, type: 'interactive', interactive: { type: 'button', body: { text: bodyText }, action: { buttons: buttons.map(b => ({ type: 'reply', reply: { id: b.id, title: b.label.slice(0, 20) }, // Limit 20 Zeichen })), }, }, }), } ); if (!res.ok) throw new Error(`Button send failed: ${await res.text()}`); return (await res.json()).messages[0].id; } // Verwendung await sendButtons( '+15550001234', 'Would you like a callback from our team?', [ { id: 'yes_callback', label: 'Yes, call me!' }, { id: 'no_thanks', label: 'No thanks' }, ] );
Python
send_buttons.py
def send_buttons(to: str, body_text: str, buttons: list) -> str: # buttons: [{"id": "btn_yes", "label": "Yes"}, ...] max. 3 Buttons url = f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages" res = requests.post(url, headers={ "Authorization": f"Bearer {os.environ['WA_TOKEN']}", "Content-Type": "application/json", }, json={ "messaging_product": "whatsapp", "to": to, "type": "interactive", "interactive": { "type": "button", "body": { "text": body_text }, "action": { "buttons": [ { "type": "reply", "reply": { "id": b["id"], "title": b["label"][:20] }} for b in buttons ] }, }, } ) res.raise_for_status() return res.json()["messages"][0]["id"]
PHP
send_buttons.php
function sendButtons(string $to, string $bodyText, array $buttons): string { $url = "https://graph.facebook.com/v21.0/" . getenv('WA_PHONE_ID') . "/messages"; $btns = array_map(fn($b) => [ 'type' => 'reply', 'reply' => ['id' => $b['id'], 'title' => mb_substr($b['label'], 0, 20)], ], $buttons); $body = [ 'messaging_product' => 'whatsapp', 'to' => $to, 'type' => 'interactive', 'interactive' => [ 'type' => 'button', 'body' => ['text' => $bodyText], 'action' => ['buttons' => $btns], ], ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('WA_TOKEN'), 'Content-Type: application/json', ], ]); $res = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) throw new RuntimeException(json_encode($res)); return $res['messages'][0]['id']; }

Einen Standort senden

Standort-Nachrichten werden als Karten-Pin in WhatsApp gerendert mit optionalem Namen und Adresse darunter. Kunden können tippen, um die Karten-App zu öffnen. Keine URL oder Media ID nötig — nur Koordinaten.

Massenversand: Rate-Limits und das richtige Muster

Die WhatsApp Cloud API erlaubt standardmäßig 80 Nachrichten pro Sekunde pro Telefonnummer. In diesem Tempo dauert das Senden von 10.000 Nachrichten etwa 2 Minuten. Ohne Rate-Limiting erschöpft eine naive Schleife das Limit fast sofort, und Sie erhalten mitten in der Kampagne 429-Fehler. Das richtige Muster: einen festen Delay zwischen Sendungen hinzufügen und exponentielles Backoff bei 429 implementieren.

Node.js — Massen-Sender mit Rate-Limiting
bulkSend.js
const DELAY_MS = 20; // 20 ms zwischen Sendungen = 50 msg/s — sicher unter dem 80-msg/s-Limit const MAX_RETRIES = 4; async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } async function sendWithRetry(to, message, attempt = 0) { try { return await sendText(to, message); } catch (err) { const is429 = err.message.includes('429'); if (!is429 || attempt >= MAX_RETRIES) throw err; const waitMs = Math.min(1000 * 2 ** attempt + Math.random() * 500, 60000); console.warn(`Rate limited — waiting ${Math.round(waitMs)}ms (attempt ${attempt + 1})`); await sleep(waitMs); return sendWithRetry(to, message, attempt + 1); } } async function bulkSend(contacts, messageBody) { const results = { sent: 0, failed: [] }; for (const { phone, name } of contacts) { try { const personalised = messageBody.replace('{{name}}', name); await sendWithRetry(phone, personalised); results.sent++; } catch (err) { results.failed.push({ phone, error: err.message }); } await sleep(DELAY_MS); // Abstand für Rate-Limit } console.log(`Sent: ${results.sent}, Failed: ${results.failed.length}`); return results; } // Verwendung await bulkSend( [{ phone: '+15550001234', name: 'Alice' }, { phone: '+15550005678', name: 'Bob' }], 'Hi {{name}}, your order is ready! 🎉' );

Fehlerbehandlung: was jeder Status-Code bedeutet

HTTP StatusHäufige UrsacheLösung
200 OKNachricht von Meta akzeptiert — noch nicht zugestelltDen zurückgegebenen wamid speichern und auf Zustellstatus-Webhooks lauschen
400 Bad RequestUngültiges JSON, fehlendes Pflichtfeld, Vorlage nicht genehmigt, Anzahl der Vorlagenvariablen passt nichtBody der Fehlerantwort prüfen — Meta gibt eine detaillierte error.message zurück, die das genaue fehlgeschlagene Feld oder die Einschränkung erklärt
401 UnauthorizedUngültiger oder abgelaufener Access-Token; fehlender Authorization-HeaderEinen permanenten System-User-Token neu generieren. Niemals temporäre User-Tokens in der Produktion verwenden — sie laufen nach 60 Tagen ab
404 Not FoundFalsche Phone Number ID in der URL; Nummer nicht auf der Cloud API registriertDie Phone Number ID im Meta Developer Dashboard → WhatsApp → Phone Numbers prüfen. Bestätigen, dass die Nummer registriert und verifiziert ist.
429 Too Many Requests80-msg/s-Rate-Limit überschritten oder API-Aufrufrate-LimitExponentielles Backoff mit Jitter implementieren. 15–20 ms Delay zwischen Sendungen bei Massenoperationen hinzufügen. Nicht sofort wiederholen.
460 (Meta-intern)Empfängernummer nicht auf WhatsApp registriertDie Telefonnummer ist kein WhatsApp-Nutzer. Aus Ihrer Liste entfernen. Das ist keine zustellbare Nummer.
131026Nachricht nicht zustellbar — Beschränkungen des WhatsApp-Kontos des EmpfängersDer Empfänger könnte Ihre Nummer blockiert haben oder sein Konto ist beschränkt. Loggen und überspringen.
500 / 503Meta-Infrastruktur-Ausfall oder temporärer FehlerMit exponentiellem Backoff wiederholen. metastatus.com auf aktive Vorfälle prüfen. Nicht mehr als 3 Mal wiederholen.

Vollständige WhatsApp-Client-Klasse (Node.js)

Die obigen Funktionen in einer wiederverwendbaren Klasse mit eingebauter Retry-Logik, konsistenter Fehlerbehandlung und umgebungsbasierter Konfiguration:

Node.js
WhatsAppClient.js
class WhatsAppClient { constructor({ phoneId = process.env.WA_PHONE_ID, token = process.env.WA_TOKEN, version = 'v21.0', } = {}) { this.baseUrl = `https://graph.facebook.com/${version}/${phoneId}/messages`; this.headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }; } async #send(payload) { const res = await fetch(this.baseUrl, { method: 'POST', headers: this.headers, body: JSON.stringify({ messaging_product: 'whatsapp', ...payload }), }); if (!res.ok) { const err = await res.json(); throw Object.assign(new Error(err?.error?.message ?? 'WhatsApp API error'), { code: err?.error?.code, status: res.status, }); } return (await res.json()).messages[0].id; } text(to, body) { return this.#send({ to, type: 'text', text: { body } }); } image(to, link, caption = '') { return this.#send({ to, type: 'image', image: { link, caption } }); } document(to, link, filename, caption = '') { return this.#send({ to, type: 'document', document: { link, filename, caption } }); } location(to, lat, lng, name = '', address = '') { return this.#send({ to, type: 'location', location: { latitude: lat, longitude: lng, name, address } }); } buttons(to, bodyText, buttons) { return this.#send({ to, type: 'interactive', interactive: { type: 'button', body: { text: bodyText }, action: { buttons: buttons.map(b => ({ type: 'reply', reply: { id: b.id, title: b.label.slice(0, 20) } })) }, }}); } template(to, name, lang, variables = []) { return this.#send({ to, type: 'template', template: { name, language: { code: lang }, components: variables.length ? [{ type: 'body', parameters: variables.map(v => ({ type: 'text', text: v })) }] : [], }}); } } // Verwendung const wa = new WhatsAppClient(); await wa.text('+15550001234', 'Hello! 👋'); await wa.template('+15550001234', 'order_shipped', 'en', ['#1234']); await wa.buttons('+15550001234', 'Rate your experience:', [{ id: 'great', label: 'Great! 🌟' }, { id: 'poor', label: 'Could be better' }]); module.exports = WhatsAppClient;

Die andere Hälfte: Antworten empfangen mit SocialHook

Das Senden von Nachrichten ist nur die halbe Miete. Wenn Ihr Kunde antwortet, feuert die WhatsApp Cloud API einen Webhook an Ihren Server — aber Sie brauchen einen öffentlich erreichbaren HTTPS-Endpunkt, um ihn zu empfangen, plus HMAC-SHA256-Signaturverifizierung, Extraktion verschachtelter Payloads und Retry-Handling.

SocialHook übernimmt die gesamte Eingangsschicht. Verbinden Sie Ihre WhatsApp-Nummer mit SocialHook, fügen Sie die URL Ihres Servers als Ziel ein, und jede Kundenantwort kommt als normalisiertes JSON-Ereignis an — verifiziert, aus Metas verschachteltem Umschlag extrahiert und in unter 50 ms an Ihren Endpunkt weitergeleitet. Dasselbe flache Format funktioniert auch für Facebook Messenger und Instagram DMs.

Das vollständige Setup: Ihr Server nutzt den obigen WhatsAppClient für ausgehende Nachrichten, SocialHook liefert eingehende Ereignisse an Ihren Webhook-Handler. Zwei Richtungen, eine Pauschale von 50 $/Monat. Siehe den vollständigen Eingangs-Webhook-Leitfaden oder beginnen Sie mit dem 5-Minuten-Quickstart.

Häufige Fragen

Wie sende ich eine WhatsApp-Nachricht über die Cloud API?
POST an https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages mit Ihrem Authorization: Bearer {ACCESS_TOKEN}-Header und einem JSON-Body mit messaging_product: "whatsapp", to: "+E.164_number", type: "text" und text: { body: "message" }. Vollständigen Code für Node.js, Python und PHP finden Sie im Textnachrichten-Abschnitt oben.
Was ist die WhatsApp-Phone-Number-ID und wo finde ich sie?
Die Phone Number ID ist ein numerischer Identifier für Ihre spezifische WhatsApp-Business-Nummer auf der Cloud API — sie ist verschieden von der tatsächlichen Telefonnummer. Finden Sie sie im Meta Developer Dashboard → WhatsApp → Phone Numbers. Sie sieht aus wie eine 15–16-stellige Zahl. Verwenden Sie sie in der URL: graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages. Eine Telefonnummer hat eine ID, die sich nie ändert.
Was ist der Unterschied zwischen einer Session-Nachricht und einer Vorlagen-Nachricht?
Eine Session-Nachricht (Text, Bild, Buttons, Standort, Dokument) kann nur innerhalb des 24-Stunden-Service-Fensters gesendet werden, das sich öffnet, wenn ein Kunde Ihnen zuerst schreibt. Diese sind innerhalb des Monatskontingents kostenlos. Eine Vorlagen-Nachricht ist ein von Meta genehmigtes Format, das zum Initiieren neuer Konversationen oder zum Senden außerhalb des Service-Fensters erforderlich ist. Vorlagen müssen genehmigt werden (1–24 Std. für Utility, bis zu 5 Tage für Marketing), bevor sie gesendet werden können.
Was passiert, wenn ich einen 429 von der WhatsApp Cloud API erhalte?
Sie haben das Rate-Limit erreicht (80 msg/s standardmäßig). Exponentielles Backoff implementieren: vor dem Retry 1000 * 2^attempt + random(500) ms warten, gedeckelt auf 60 Sekunden. Jitter (die Zufallskomponente) hinzufügen, um synchronisierte Retries von mehreren Workern zu verhindern. Für Massenkampagnen einen festen 15–20-ms-Delay zwischen Sendungen hinzufügen, um sicher unter dem Limit zu bleiben, statt es zu erreichen und zu retryen.
Wie sende ich eine WhatsApp-Vorlage mit Variablen in Python?
Setzen Sie "type": "template" und fügen Sie ein components-Array mit einer Body-Komponente, die parameters enthält — ein Objekt pro Variable in Ihrer Vorlage. Jeder Parameter ist {"type": "text", "text": "variable_value"}. Sie ordnen sich sequenziell zu {{1}}, {{2}}, {{3}} in Ihrer Vorlage zu. Vollständigen Python-Code finden Sie im Vorlagen-Abschnitt oben.
Wie empfange ich WhatsApp-Antworten von Kunden?
Kundenantworten kommen per Webhook an — Meta feuert einen HTTP POST an Ihren registrierten Endpunkt, wenn ein Nachrichtenereignis auftritt. Sie brauchen eine öffentlich erreichbare HTTPS-URL, HMAC-SHA256-Signaturverifizierung und Parsing verschachtelter Payloads. SocialHook übernimmt all das automatisch und liefert sauberes JSON in unter 50 ms an Ihren Endpunkt. Das vollständige Setup finden Sie in unserem Eingangs-Webhook-Leitfaden.

Sie können senden. Jetzt
auch Antworten empfangen.

Ihr ausgehender Code ist bereit. Verbinden Sie SocialHook für den Eingang — jede Kundenantwort kommt als sauberes JSON an Ihrem Webhook-Endpunkt an. Kein HMAC-Boilerplate, kein verschachteltes Payload-Parsing, keine Retry-Infrastruktur zum Aufbauen.

Keine Kreditkarte erforderlich · 50 $/Monat nach der Testphase · Jederzeit kündbar