Send WhatsApp messages from PHP, Python or Node.js — code examples
If you're building a backend feature — order confirmations, appointment reminders, OTP codes — you don't need a no-code tool in the middle. TextMeFlow's API is a single authenticated POST request, so calling it directly from your own PHP, Python or Node.js code is often simpler than wiring up an automation platform. This guide has working snippets for all three.
Before you start
- Create a free TextMeFlow account — 50 messages/month, no credit card, no Meta Business verification.
- Scan the QR code from the portal with the phone that holds your WhatsApp number.
- Copy your API key from the dashboard.
Full endpoint reference: /docs/api.
PHP
<?php
$response = file_get_contents('https://api.textmeflow.eu/v1/messages', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n" .
"Authorization: Bearer YOUR_API_KEY\r\n",
'content' => json_encode([
'to' => '+32472932208',
'text' => 'Your order #1042 has shipped.',
]),
],
]));
$data = json_decode($response, true);
echo $data['status']; // "queued"
If you're migrating from TextMeBot, the drop-in /send.php compatibility endpoint (documented in the migration guide) needs no code changes at all — just swap the base URL and key. New integrations should use /v1/messages directly since it returns a proper JSON body and a message_id you can track.
Python
import requests
response = requests.post(
"https://api.textmeflow.eu/v1/messages",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"to": "+32472932208",
"text": "Your order #1042 has shipped.",
},
)
response.raise_for_status()
message_id = response.json()["message_id"]
For appointment reminders sent from a cron job or Celery task, keep the to number in E.164 format (+ and country code) — the API rejects anything else with a 422.
Node.js
const response = await fetch("https://api.textmeflow.eu/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_API_KEY",
},
body: JSON.stringify({
to: "+32472932208",
text: "Your order #1042 has shipped.",
}),
});
const data = await response.json();
console.log(data.status); // "queued"
Works the same in a Next.js API route, an Express handler or a Lambda — there's no SDK dependency, just fetch and your API key.
Sending media
All three examples above accept an optional media_url field alongside text — point it at any publicly reachable file (image, PDF, voice note) up to 100 MB and it's attached to the WhatsApp message.
Handling errors and quota
A 202 response means the message is queued, not delivered — poll GET /v1/messages/{id} if you need delivery status in your own dashboard. A 429 means you've hit your plan's rate limit or monthly quota; the free plan caps at 50 messages/month for the lifetime of the account, so anything beyond that needs a paid plan (Starter starts at €5/month).
Receiving replies
Sending is only half of most integrations. Configure a webhook in the portal and TextMeFlow POSTs every inbound WhatsApp message to your endpoint as HMAC-signed JSON — verify the signature server-side before trusting the payload. See /docs/webhooks for the exact header format and a verification example.
Wrapping up
For most backend use cases this is the entire integration: one POST to send, one webhook endpoint to receive. No Meta Business Manager, no template approval queue, no SDK to install. Start free and send your first message in a few minutes.
Zelf WhatsApp-berichten versturen via API?
Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.
Gratis voor altijd