How many WhatsApp numbers does your AI agent need?

Work out how many WhatsApp numbers an AI agent needs: the limits per number, a three-part formula, worked examples with costs, routing and Business API tiers.

A shelf grid of identical phones, a few glowing green, joined by thin lines to a central hub.

Key takeaways

  • A whasp number allows 100 active chats in 30 days, 40 new chats in 24 hours and 1,500 sent messages in 24 hours, and the numbers you need is the largest of three divisions, each rounded up.
  • Plan to 80% of each limit, because above 80% a number is flagged at_risk: that turns 100 users per number into 80, 40 new chats a day into 32, and 1,500 sends into 1,200.
  • A chatty agent is limited by messages, not users: at the 80% line one number can send 15 messages per active user per day.
  • A launch spike is limited by new chats: 1,000 signups in one day need 32 numbers, while the same signups spread over five days need 13.
  • Meta's Business API scales differently: its tiers (250, 2,000, 10,000, 100,000, unlimited) cap how many people you can message outside the 24-hour window, not your replies.

On whasp, the number of WhatsApp numbers your AI agent needs is the largest of three divisions, each rounded up: active users divided by 80, signups on your busiest day divided by 32, and messages sent on your busiest day divided by 1,200. Those are 80% of the per-number limits. Add a spare number or two for bans.

What limits does one number have?

A whasp number is a real personal WhatsApp account on its own cloud phone and IP, so its limits apply per number. The API reference lists them:

Limit, per number Value What happens at the limit
Active chats (people who wrote in the last 30 days) 100 The number leaves /numbers/pick; incoming messages still arrive
New chats in 24 hours 40 Same
Messages sent in 24 hours 1,500 Sends fail with 429 number_daily_limit
Messages received No limit

A number is flagged at_risk when any limit is above 80%, so the planning ceilings are 80 users, 32 new chats and 1,200 sends. Two more facts shape a plan. Messages from one number go out one at a time, about a second apart, so a backlog of 120 replies takes about two minutes to drain. And the account allows 100 API requests per 10 seconds, which is 10 a second, the same pace as 10 numbers sending flat out. Beyond that, queue on your side.

Count active users the way whasp does: people who wrote in a rolling 30 days, not total signups. People who go quiet roll off after 30 days. GET /v1/numbers/{number}/chats lists who wrote and when, so you can measure it.

How do you calculate the numbers you need?

The calculation takes three inputs from your own data: active users (U), new users on your busiest day (S), and messages you send on your busiest day (M). Each limit gives a count, and the largest wins:

  • by users: U / (100 x 0.8) = U / 80
  • by signups: S / (40 x 0.8) = S / 32
  • by sends: M / (1,500 x 0.8) = M / 1,200

Then add a spare of 10%, at least one. When a number goes critical (banned, logged out, phone offline over 10 minutes, or sends failing), /numbers/pick stops handing it out and its share of new signups lands on the others. The replacement arrives free but empty, and your own logic moves the users. The spare is where they fit while that happens. Each number is $49 per 30 days.

// Per-number limits from the whasp API reference, and the load we plan to.
const LIMITS = { users: 100, signups: 40, sends: 1500 };
const PLAN = 0.8; // above 80% of a limit a number is flagged at_risk

export function numbersNeeded(users: number, busiestDaySignups: number, busiestDaySends: number) {
  const byUsers = Math.ceil(users / (LIMITS.users * PLAN));
  const bySignups = Math.ceil(busiestDaySignups / (LIMITS.signups * PLAN));
  const bySends = Math.ceil(busiestDaySends / (LIMITS.sends * PLAN));
  const base = Math.max(byUsers, bySignups, bySends);
  const spare = Math.max(1, Math.ceil(base * 0.1)); // room for a number that goes critical
  return { byUsers, bySignups, bySends, base, spare, total: base + spare, monthlyUsd: (base + spare) * 49 };
}

What do three real-sized plans look like?

The three plans below use example assumptions, not measurements. Replace them with numbers from your own logs.

A language tutor with 3,000 active users

Assume 25% of active users write on a given day, the agent sends each of them 12 messages, the busiest day is twice the average, and a launch brings 400 new users in one day.

  • Sends: 3,000 x 0.25 x 12 = 9,000 on an average day, 18,000 on the busiest.
  • By users: 3,000 / 80 = 37.5, so 38. By signups: 400 / 32 = 12.5, so 13. By sends: 18,000 / 1,200 = 15.
  • Largest is 38. Spare: 10% of 38 = 3.8, so 4. Total 42 numbers, 42 x $49 = $2,058 a month, or $0.69 per active user.

Users decide this one.

A chatty companion with 1,200 active users

Assume 50% write on a given day, the agent sends each 40 messages, the busiest day is 1.5 times the average, and 100 new users arrive on it.

  • Sends: 1,200 x 0.5 x 40 = 24,000 on an average day, 36,000 on the busiest.
  • By users: 1,200 / 80 = 15. By signups: 100 / 32 = 3.1, so 4. By sends: 36,000 / 1,200 = 30.
  • Largest is 30. Spare 3. Total 33 numbers, 33 x $49 = $1,617, or $1.35 per active user.

Messages decide this one. At 80 users per number, 1,200 sends a day allows 15 messages per active user per day. This companion sends 30 per active user on its busiest day, so it needs twice the numbers its user count suggests.

A launch with 1,000 signups in one day

Assume 1,000 users in total and 6,000 messages sent that day.

  • By users: 1,000 / 80 = 12.5, so 13. By signups: 1,000 / 32 = 31.25, so 32. By sends: 6,000 / 1,200 = 5.
  • Largest is 32. Spare 4. Total 36 numbers, $1,764 a month.

Now spread the same 1,000 signups over five days, 200 a day, with 1,200 sends on the busiest day:

  • By users: 13. By signups: 200 / 32 = 6.25, so 7. By sends: 1,200 / 1,200 = 1.
  • Largest is 13. Spare 2. Total 15 numbers, $735 a month.

A waiting list or invite codes would save 21 numbers and $1,029 a month. In this example new chats per day is the limit that binds, not users.

Plan Active users Signups, busiest day Sends, busiest day Numbers Per month
Tutor 3,000 400 18,000 42 $2,058
Companion 1,200 100 36,000 33 $1,617
Launch in one day 1,000 1,000 6,000 36 $1,764
Launch over five days 1,000 200 1,200 15 $735

How do you route new users and keep them on a number?

GET /v1/numbers/pick returns the healthy, online number with the most room for a new user, and a wa.me link that opens the chat. Pass text and the link opens with that message prefilled. Put your user id in it, so the agent knows who is writing (more on onboarding links).

const API = "https://api.whasp.ai/v1";
const headers = { Authorization: `Bearer ${process.env.WHASP_API_KEY}` };

/** The link to show a new user, or null when every number is full. */
export async function signupLink(userId: string): Promise<string | null> {
  const params = new URLSearchParams({ text: `Hi, my code is ${userId}` });
  const res = await fetch(`${API}/numbers/pick?${params}`, { headers });
  if (res.status === 409) return null; // no_capacity: hold the signup, add a number
  if (!res.ok) throw new Error(`pick failed with ${res.status}`);
  const { wa_link } = (await res.json()) as { number: string; wa_link: string };
  return wa_link;
}

A chat stays on its number. When you send without from, whasp uses the number the person last wrote to, which is what you want. A different from returns 409 chat_not_open unless that person wrote to that number too.

When signupLink returns null, show a waiting screen and add numbers. Add them before you need them: POST /v1/numbers can answer 503 no_capacity when none is ready (nothing is charged), and a number set up from scratch takes about 30 minutes.

How do you watch headroom and add numbers?

Poll GET /v1/numbers every few minutes. Each number reports its stats and its limits, so you can add up the room left to 80% across the healthy numbers and alert on any number that is not healthy. The number.status_changed webhook tells you the moment health or online changes (webhook handling).

type Num = {
  number: string;
  status: "provisioning" | "active" | "retired";
  health: "healthy" | "at_risk" | "critical";
  stats: { active_chats_30d: number; new_chats_24h: number; outbound_24h: number };
  limits: { active_chats_30d: number; new_chats_24h: number; outbound_24h: number };
};

/** Free room across healthy numbers, counted up to 80% of each limit. */
export async function headroom() {
  const res = await fetch(`${API}/numbers`, { headers });
  const { data } = (await res.json()) as { data: Num[] };
  const live = data.filter((n) => n.status === "active");
  const sick = live.filter((n) => n.health !== "healthy").map((n) => n.number);
  const room = { users: 0, signups: 0, sends: 0 };
  for (const n of live.filter((n) => n.health === "healthy")) {
    room.users += Math.max(0, Math.floor(n.limits.active_chats_30d * PLAN) - n.stats.active_chats_30d);
    room.signups += Math.max(0, Math.floor(n.limits.new_chats_24h * PLAN) - n.stats.new_chats_24h);
    room.sends += Math.max(0, Math.floor(n.limits.outbound_24h * PLAN) - n.stats.outbound_24h);
  }
  return { numbers: live.length, sick, room };
}

/** Adds a number. This charges $49 at once, so call it behind a ceiling. */
export async function addNumber(label: string): Promise<Num> {
  const res = await fetch(`${API}/numbers`, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ label, profile: { name: "Ava", about: "Your AI friend" } }),
  });
  if (res.status === 402) throw new Error("balance too low: top up first");
  if (res.status === 503) throw new Error("no_capacity: nothing charged, retry later");
  if (!res.ok) throw new Error(`add failed with ${res.status}`);
  return (await res.json()) as Num;
}

Call addNumber when room.users falls below the signups you expect in the next two weeks, or room.signups below your busiest day, and cap the total so a bug cannot spend your balance. Keep the balance above numbers x $49: a number renews by itself while the balance covers it, and when the balance runs out it gets 3 days of grace and then retires.

Several products or customers

One account has one webhook URL, so run one pool per product, persona or customer and tell them apart yourself. Give each number a label such as tutor-fr-1, set its profile (name, about, photo) to the persona, and route each event by its number field. Run numbersNeeded per pool. Billing is a single balance, so charge each customer for their count of numbers x $49 on your side.

How do the Business API’s limits compare?

Meta limits the unique people a business can message outside the 24-hour customer service window, per moving 24 hours and per business portfolio, shared by all its numbers. New portfolios start at 250. Verification, or sending 2,000 delivered messages at high quality, raises it to 2,000. After that it rises automatically to 10,000, 100,000 and unlimited when your messages are high quality and you used at least half the limit in the last 7 days.

whasp WhatsApp Business Platform
What is capped Active chats, new chats and sends, per number Unique people messaged outside the 24-hour window, per portfolio
Replies to people who wrote Count toward the 1,500 sends Not part of the tier
How you add capacity Add a number: $49 per 30 days, usually ready at once Earn a higher tier through verification and quality
Speed per number One message about every second 80 messages per second by default, up to 1,000
Numbers per account You add them with POST /v1/numbers 2 at first, 20 once verified

The official platform scales by quota, whasp by phones. Replies inside the window are not part of Meta’s tier and we found no cap on active users per official number, so for an agent Meta admits, one official number can serve a very large audience. Whether your AI is allowed there at all is the first question, and the pricing comparison shows what the official route costs.

What to do next

Pull three numbers from your own logs: active users in a rolling 30 days, signups on your busiest day, and messages sent on your busiest day. Run numbersNeeded and add the numbers one launch ahead. Wire up the headroom check, and read why numbers get banned to size your spare.

Questions

How many WhatsApp numbers do I need for 1,000 users?

On whasp, 1,000 active users need 13 numbers at the 80-user planning line, or 10 at the hard limit of 100. Check new chats and messages per day as well, because either can call for more numbers than users do.

What is the message limit per WhatsApp number on whasp?

A number can send up to 1,500 messages in 24 hours, hold 100 active chats over 30 days and open 40 new chats in 24 hours. Incoming messages are unlimited, and at 1,500 sends the API answers 429 number_daily_limit.

How do I send new users to the right number?

Call GET /v1/numbers/pick. It returns the healthy, online number with the most room and a wa.me link, and a chat then stays on that number. It answers 409 no_capacity when every number is full, offline or unhealthy.

What are the WhatsApp Business API messaging limits?

Meta caps the unique people a business can message outside the 24-hour window per moving 24 hours, per business portfolio: 250 at the start, then 2,000, 10,000, 100,000 and unlimited as the account verifies and sends high-quality messages.

What happens when a whasp number is full?

It stops being offered by /numbers/pick, but messages from its existing users still arrive. Past 1,500 sends in 24 hours, sends from it fail with 429 number_daily_limit.

All articles