01 · Definition
What is load testing?
Load testing is running your own system under realistic, controlled traffic to measure how it behaves (latency, error rate, queue growth, resource use) at and beyond the load you expect. For a WhatsApp integration that means two directions: the send pipeline (your API, queue, workers and the calls to the Messages Endpoint) and the webhook receivers that must absorb the wave of statuses and replies a big Broadcast produces.
Tests come in several shapes. A smoke test checks the script works at tiny volume. A load test holds the expected peak. A stress test keeps raising traffic until something breaks, to find the limit and see how failure looks. A spike test jumps from idle to peak in seconds, like a campaign scheduled for 10:00. A soak test holds moderate load for hours to expose memory leaks, exhausted connection pools and slowly growing queues.
One rule is specific to messaging: never load test against Meta's live Cloud API. Real requests deliver real messages to real people, are charged per delivered message, hit the number's Throughput ceiling (80 messages per second by default, with Error 130429 (Rate Limit Hit) beyond it) and can damage quality signals. Point the pipeline at a mock that behaves like the Cloud API, with realistic wamid responses and latency and injected 130429 and 5xx errors, and have the mock fire signed status webhooks back at your receivers. Load testing measures capacity; chaos engineering, by contrast, injects faults into a running system.
Key takeaways
- Load testing runs your own system under controlled, realistic traffic to measure latency, errors and capacity before real users do.
- Never load test against Meta's live Cloud API: it sends real, billed messages and runs into your number's throughput limit.
- Test both directions: the send pipeline and the webhook receivers that absorb several statuses per message.
- Run smoke, load, spike, stress and soak tests, using arrival-rate scenarios so slow responses cannot hide the tail.
- Judge each run on p99 latency, error rate and queue age together, then fix the first bottleneck and run it again.
02 · Operation
How does load testing work?
Define the target in numbers
Write down the expected peak, for example 80 sends per second per number and 600 status webhooks per second, and the pass criteria, such as p99 acknowledgement under 500 ms and errors under 1%.
Build a Cloud API mock
Stub
POST /{PHONE_NUMBER_ID}/messagesto return a realistic response with a fakewamid, add realistic latency, and inject a share of 130429 throughput errors and 5xx responses so retries and backoff get exercised.Generate webhook traffic too
Replay statuses for every mocked send (sent, delivered, read and some failed) signed with a test app secret in
X-Hub-Signature-256, so receivers verify signatures under load exactly as they do in production. Webhook Testing proves a receiver handles each payload correctly; this step proves it keeps up.Run the shapes in order
Smoke, then a ramp to the target load, then a spike, then stress until failure, then a multi-hour soak. Use an open, arrival-rate workload so slow responses do not quietly reduce the load you apply.
Find the bottleneck, fix it, repeat
Watch queue age, p99 latency, error rate, CPU, connection pools and database locks together. Fix the first limit you hit and rerun: the next bottleneck is usually close behind.
03 · Code samples
Load testing in code
// load-test.js — run with: k6 run load-test.js// Targets STAGING only: SEND_API is your service, wired to a Cloud API mock.import http from "k6/http";import crypto from "k6/crypto";import { check } from "k6";export const options = {scenarios: {sends: {executor: "ramping-arrival-rate", // open model: arrivals do not wait for responsesstartRate: 10,timeUnit: "1s",preAllocatedVUs: 200,maxVUs: 1000,stages: [{ duration: "2m", target: 80 }, // ramp to one number's default 80 per second{ duration: "5m", target: 80 }, // hold the expected peak{ duration: "30s", target: 320 }, // spike: four numbers at once{ duration: "3m", target: 320 },],exec: "send",},statuses: {executor: "constant-arrival-rate",rate: 600,timeUnit: "1s",duration: "10m",preAllocatedVUs: 300,maxVUs: 1500,exec: "statusWebhook",},},thresholds: {"http_req_duration{scenario:statuses}": ["p(99)<500"],http_req_failed: ["rate<0.01"],},};export function send() {const res = http.post(__ENV.SEND_API + "/v1/whatsapp/sends",JSON.stringify({ to: "919800000001", template: "order_update", params: ["48213"] }),{ headers: { "Content-Type": "application/json", "Idempotency-Key": "lt-" + __VU + "-" + __ITER } });check(res, { accepted: (r) => r.status >= 200 && r.status < 300 });}const SAMPLE = open("./samples/status-delivered.json"); // a recorded, anonymised payloadexport function statusWebhook() {const sig = "sha256=" + crypto.hmac("sha256", __ENV.TEST_APP_SECRET, SAMPLE, "hex");const res = http.post(__ENV.WEBHOOK_URL, SAMPLE, {headers: { "Content-Type": "application/json", "X-Hub-Signature-256": sig },});check(res, { acked: (r) => r.status === 200 });}
Two open-model scenarios: sends ramp and spike, signed status webhooks arrive at a constant rate, and p99 and error thresholds decide pass or fail.
04Playground
A working model of Load Testing. Use its controls to replay it step by step.
Push the needle: where does the pipeline break?
- Healthy: p99 under 150 ms0–200 sends/s
- Saturating: queues grow200–260 sends/s
- Breaking: p99 over 4 s, retries260–500 sends/s
Stress test: breaking point
260 sends/s
Raising load step by step finds the knee near 260 per second: a synchronous CRM call in the webhook handler.
Stress test: breaking point: 260 sends/s — Breaking: p99 over 4 s, retries zone. Raising load step by step finds the knee near 260 per second: a synchronous CRM call in the webhook handler.
05 · Specifications
Load testing: key facts and limits
| Test shapes | Smoke, load, stress, spike, soak |
|---|---|
| Default Cloud API throughput | 80 messages per second per number |
| Over the limit | Error 130429: inject it in your mock |
| Upgraded throughput | Up to 1,000 messages per second for eligible numbers |
| Test target | Your staging stack and a mock, never Meta's live API |
| Pass criteria | p99 latency and error-rate thresholds per scenario |
06 · Rationale
Why does load testing matter?
Campaign day is the worst time to discover that your webhook receivers fall over at 400 requests a second, or that the database connection pool limits your workers to half the throughput Meta allows you. A load test turns those surprises into numbers you can plan with: sustainable sends per second, the P99 Latency of webhook acknowledgements at peak, how long a 200,000-message broadcast takes to drain. It also shows whether Horizontal Scaling and Graceful Degradation really work before customers depend on them.
07 · Cautions
Common mistakes with load testing
Testing against the live Cloud API
Real sends reach real phones, are billed per delivery, hit the 80 messages-per-second ceiling and can harm quality. Use a mock, and keep live end-to-end checks to a handful of messages to numbers you control.
Closed-loop load generators
A tool that waits for each response before sending the next request applies less load exactly when the system slows down, which hides the tail (coordinated omission). Use arrival-rate scenarios.
Forgetting the webhook half
A broadcast produces several status webhooks per message (sent, delivered, read). Testing only the sends misses the receivers, which often fail first.
Testing a different system
Staging with a tenth of production's data, no load balancer and cold caches gives numbers that do not transfer. Match the topology, data volume and configuration limits that matter.
Reading averages
A healthy average can hide a p99 of several seconds. Judge every run on percentiles, error rate and queue age together.
08 · Worked example: Rehearsing an Independence Day sale
Example: Rehearsing an Independence Day sale
Before its Independence Day sale, a Kochi electronics chain plans a 300,000-message broadcast across four numbers. A staging run against a Cloud API mock ramps to 320 sends per second, with status webhooks replayed at twice that rate. At 260 sends per second the receivers' p99 acknowledgement jumps from 90 ms to 4 seconds, because each status triggered a synchronous CRM update. Moving that update behind a queue brings p99 back under 150 ms at 400 per second, and a four-hour soak shows no memory growth. On the day, the sale runs without a single retry storm.
09 · Recommended practice
Best practices for load testing
- Keep load-test scripts in the repo and run a short version in CI before major releases.
- Record the breaking point and the bottleneck for every run, so capacity trends are visible over time.
- Model retries: a 2% error rate with three retries adds load you also need to test.
- Tag test traffic clearly so it can never be confused or mixed with real customer data.
10 · Questions and answers
Load Testing FAQs
- Can I load test the WhatsApp Cloud API directly?
- No. Test your own pipeline against a mock instead. Live requests deliver real, billed messages, are capped by your number's throughput and can affect quality; Meta's limits are documented, so model them in the mock.
- What is the difference between load testing and stress testing?
- Stress testing is one kind of load test. A load test checks behaviour at the expected peak; a stress test keeps raising traffic beyond it to find the breaking point and see how the system fails.
- How much load should I test with?
- Test at your expected peak plus headroom, often 1.5 to 2 times. Base it on real numbers: messages per second across all your numbers, and the statuses each message produces.
- Which metrics matter most in a load test?
- Tail latency, error rate and saturation. Watch p95 and p99 latency, the share of failed requests, queue depth and age, and CPU, memory and connection-pool use together.