[{"data":1,"prerenderedAt":1749},["ShallowReactive",2],{"guide:rate-limits":3},{"id":4,"title":5,"body":6,"date":1736,"description":1737,"extension":1738,"meta":1739,"navigation":1024,"path":1740,"seo":1741,"stem":1742,"tags":1743,"updated":1747,"__hash__":1748},"guides\u002Fdocs\u002Frate-limits.md","Rate Limits and Quotas",{"type":7,"value":8,"toc":1727},"minimark",[9,27,31,142,151,192,199,203,206,254,265,549,555,559,565,679,696,789,798,1514,1518,1528,1543,1547,1550,1609,1623,1627,1634,1671,1687,1695,1703,1707,1723],[10,11,12,13,17,18,21,22,26],"p",{},"Two different things can slow a Wapito request down, and they answer differently. ",[14,15,16],"strong",{},"Plan limits","\nare the quotas you bought: how many requests a minute and a month, how many messages a day, how\nmany number checks. ",[14,19,20],{},"Anti-ban controls"," are the limits the linked number needs whatever you\nbought: a send queue that paces messages, a warm-up ladder for new numbers, a guard on messages to\nstrangers, and WhatsApp's own reach-out timelock. Both answer ",[23,24,25],"code",{},"429",", but with different codes, and\nthe right reaction is different for each. This page lists them all.",[28,29,16],"h2",{"id":30},"plan-limits",[32,33,34,50],"table",{},[35,36,37],"thead",{},[38,39,40,44,47],"tr",{},[41,42,43],"th",{},"Limit",[41,45,46],{},"Sandbox",[41,48,49],{},"Premium",[51,52,53,65,76,87,98,109,120,131],"tbody",{},[38,54,55,59,62],{},[56,57,58],"td",{},"Channels",[56,60,61],{},"1",[56,63,64],{},"Unlimited, billed per channel",[38,66,67,70,73],{},[56,68,69],{},"API requests per minute, per channel",[56,71,72],{},"60",[56,74,75],{},"300",[38,77,78,81,84],{},[56,79,80],{},"API requests per month",[56,82,83],{},"1,000",[56,85,86],{},"Unlimited",[38,88,89,92,95],{},[56,90,91],{},"Messages sent per day",[56,93,94],{},"150",[56,96,97],{},"Unlimited (the warm-up ladder still applies)",[38,99,100,103,106],{},[56,101,102],{},"Number checks per day",[56,104,105],{},"30",[56,107,108],{},"2,000",[38,110,111,114,117],{},[56,112,113],{},"Webhook endpoints",[56,115,116],{},"2",[56,118,119],{},"5",[38,121,122,125,128],{},[56,123,124],{},"Maximum media size",[56,126,127],{},"16 MB",[56,129,130],{},"64 MB",[38,132,133,136,139],{},[56,134,135],{},"Dedicated proxy",[56,137,138],{},"—",[56,140,141],{},"Optional",[10,143,144,145,150],{},"The prices and the rest of the plan are on the ",[146,147,149],"a",{"href":148},"\u002Fpricing\u002F","pricing page",". Three counters do the\ncounting:",[152,153,154,165,179],"ul",{},[155,156,157,160,161,164],"li",{},[14,158,159],{},"Requests"," — every call to ",[23,162,163],{},"\u002Fv1\u002F*"," counts once toward the month, whatever it does. The\nper-minute limit is a fixed one-minute window per channel, so two channels in one account have\ntwo windows.",[155,166,167,170,171,174,175,178],{},[14,168,169],{},"Sent"," — one per message accepted by a send endpoint (",[23,172,173],{},"\u002Fmessages\u002F*",", ",[23,176,177],{},"\u002Fstories\u002F*",", a message\nto a Channel). A send that is rejected — a bad recipient, a quota refusal, a timelock — is not\ncounted, and one the engine refuses after acceptance is refunded.",[155,180,181,184,185,191],{},[14,182,183],{},"Checks"," — one per phone number looked up. A bulk\n",[186,187],"endpoint-ref",{"label":188,"op":189,"tag":190},"POST \u002Fcontacts\u002Fcheck","check-contacts","contacts"," of fifty numbers\ncosts fifty, whether or not they exist on WhatsApp.",[10,193,194,195,198],{},"Days and months are UTC: the daily counters reset at 00:00 UTC and the monthly one on the first\nof the month. The dashboard's ",[14,196,197],{},"Usage"," tab draws the last thirty days of each.",[28,200,202],{"id":201},"reading-what-is-left","Reading what is left",[10,204,205],{},"Every response carries the per-minute state of the window it was counted in:",[207,208,213],"pre",{"className":209,"code":210,"language":211,"meta":212,"style":212},"language-http shiki shiki-themes github-light-default github-dark-default","X-RateLimit-Limit: 60\nX-RateLimit-Remaining: 41\nX-RateLimit-Reset: 1789459260\n","http","",[23,214,215,232,243],{"__ignoreMap":212},[216,217,220,224,228],"span",{"class":218,"line":219},"line",1,[216,221,223],{"class":222},"sjgCt","X-RateLimit-Limit",[216,225,227],{"class":226},"sjeE4",":",[216,229,231],{"class":230},"sSVrQ"," 60\n",[216,233,235,238,240],{"class":218,"line":234},2,[216,236,237],{"class":222},"X-RateLimit-Remaining",[216,239,227],{"class":226},[216,241,242],{"class":230}," 41\n",[216,244,246,249,251],{"class":218,"line":245},3,[216,247,248],{"class":222},"X-RateLimit-Reset",[216,250,227],{"class":226},[216,252,253],{"class":230}," 1789459260\n",[10,255,256,258,259,264],{},[23,257,248],{}," is the epoch second at which the window rolls over. For everything else — the\ndaily and monthly quotas, the warm-up rung, the cold-send budget and the timelock — ask\n",[186,260],{"label":261,"op":262,"tag":263},"GET \u002Fchannel\u002Flimits","get-channel-limits","channel"," before a bulk\nrun, and size the batch to the answer instead of discovering the cap through refusals:",[207,266,270],{"className":267,"code":268,"language":269,"meta":212,"style":212},"language-json shiki shiki-themes github-light-default github-dark-default","{\n  \"plan\": \"premium\",\n  \"rate_limit_per_minute\": 300,\n  \"api_requests\": { \"cap\": null, \"used\": 19422, \"resets_at\": \"2026-10-01T00:00:00.000Z\" },\n  \"sent\": { \"cap\": null, \"used\": 412, \"resets_at\": \"2026-09-16T00:00:00.000Z\" },\n  \"number_checks\": { \"cap\": 2000, \"used\": 38, \"resets_at\": \"2026-09-16T00:00:00.000Z\" },\n  \"warmup\": { \"day\": 11, \"cap\": null, \"ladder_complete\": true },\n  \"cold_send\": { \"window_cap\": 60, \"used\": 4, \"resets_at\": \"2026-09-15T09:00:00.000Z\" },\n  \"timelock\": { \"active\": false, \"reason\": null, \"until\": null },\n  \"media_max_bytes\": 67108864,\n  \"webhooks_max\": 5\n}\n","json",[23,271,272,278,292,304,344,378,412,448,483,519,532,543],{"__ignoreMap":212},[216,273,274],{"class":218,"line":219},[216,275,277],{"class":276},"s4rv2","{\n",[216,279,280,283,286,289],{"class":218,"line":234},[216,281,282],{"class":222},"  \"plan\"",[216,284,285],{"class":276},": ",[216,287,288],{"class":230},"\"premium\"",[216,290,291],{"class":276},",\n",[216,293,294,297,299,302],{"class":218,"line":245},[216,295,296],{"class":222},"  \"rate_limit_per_minute\"",[216,298,285],{"class":276},[216,300,75],{"class":301},"sHrmB",[216,303,291],{"class":276},[216,305,307,310,313,316,318,321,323,326,328,331,333,336,338,341],{"class":218,"line":306},4,[216,308,309],{"class":222},"  \"api_requests\"",[216,311,312],{"class":276},": { ",[216,314,315],{"class":222},"\"cap\"",[216,317,285],{"class":276},[216,319,320],{"class":301},"null",[216,322,174],{"class":276},[216,324,325],{"class":222},"\"used\"",[216,327,285],{"class":276},[216,329,330],{"class":301},"19422",[216,332,174],{"class":276},[216,334,335],{"class":222},"\"resets_at\"",[216,337,285],{"class":276},[216,339,340],{"class":230},"\"2026-10-01T00:00:00.000Z\"",[216,342,343],{"class":276}," },\n",[216,345,347,350,352,354,356,358,360,362,364,367,369,371,373,376],{"class":218,"line":346},5,[216,348,349],{"class":222},"  \"sent\"",[216,351,312],{"class":276},[216,353,315],{"class":222},[216,355,285],{"class":276},[216,357,320],{"class":301},[216,359,174],{"class":276},[216,361,325],{"class":222},[216,363,285],{"class":276},[216,365,366],{"class":301},"412",[216,368,174],{"class":276},[216,370,335],{"class":222},[216,372,285],{"class":276},[216,374,375],{"class":230},"\"2026-09-16T00:00:00.000Z\"",[216,377,343],{"class":276},[216,379,381,384,386,388,390,393,395,397,399,402,404,406,408,410],{"class":218,"line":380},6,[216,382,383],{"class":222},"  \"number_checks\"",[216,385,312],{"class":276},[216,387,315],{"class":222},[216,389,285],{"class":276},[216,391,392],{"class":301},"2000",[216,394,174],{"class":276},[216,396,325],{"class":222},[216,398,285],{"class":276},[216,400,401],{"class":301},"38",[216,403,174],{"class":276},[216,405,335],{"class":222},[216,407,285],{"class":276},[216,409,375],{"class":230},[216,411,343],{"class":276},[216,413,415,418,420,423,425,428,430,432,434,436,438,441,443,446],{"class":218,"line":414},7,[216,416,417],{"class":222},"  \"warmup\"",[216,419,312],{"class":276},[216,421,422],{"class":222},"\"day\"",[216,424,285],{"class":276},[216,426,427],{"class":301},"11",[216,429,174],{"class":276},[216,431,315],{"class":222},[216,433,285],{"class":276},[216,435,320],{"class":301},[216,437,174],{"class":276},[216,439,440],{"class":222},"\"ladder_complete\"",[216,442,285],{"class":276},[216,444,445],{"class":301},"true",[216,447,343],{"class":276},[216,449,451,454,456,459,461,463,465,467,469,472,474,476,478,481],{"class":218,"line":450},8,[216,452,453],{"class":222},"  \"cold_send\"",[216,455,312],{"class":276},[216,457,458],{"class":222},"\"window_cap\"",[216,460,285],{"class":276},[216,462,72],{"class":301},[216,464,174],{"class":276},[216,466,325],{"class":222},[216,468,285],{"class":276},[216,470,471],{"class":301},"4",[216,473,174],{"class":276},[216,475,335],{"class":222},[216,477,285],{"class":276},[216,479,480],{"class":230},"\"2026-09-15T09:00:00.000Z\"",[216,482,343],{"class":276},[216,484,486,489,491,494,496,499,501,504,506,508,510,513,515,517],{"class":218,"line":485},9,[216,487,488],{"class":222},"  \"timelock\"",[216,490,312],{"class":276},[216,492,493],{"class":222},"\"active\"",[216,495,285],{"class":276},[216,497,498],{"class":301},"false",[216,500,174],{"class":276},[216,502,503],{"class":222},"\"reason\"",[216,505,285],{"class":276},[216,507,320],{"class":301},[216,509,174],{"class":276},[216,511,512],{"class":222},"\"until\"",[216,514,285],{"class":276},[216,516,320],{"class":301},[216,518,343],{"class":276},[216,520,522,525,527,530],{"class":218,"line":521},10,[216,523,524],{"class":222},"  \"media_max_bytes\"",[216,526,285],{"class":276},[216,528,529],{"class":301},"67108864",[216,531,291],{"class":276},[216,533,535,538,540],{"class":218,"line":534},11,[216,536,537],{"class":222},"  \"webhooks_max\"",[216,539,285],{"class":276},[216,541,542],{"class":301},"5\n",[216,544,546],{"class":218,"line":545},12,[216,547,548],{"class":276},"}\n",[10,550,551,554],{},[23,552,553],{},"cap: null"," means the plan does not cap that dimension.",[28,556,558],{"id":557},"the-429-responses","The 429 responses",[10,560,561,562,564],{},"All of them use the standard error envelope. Branch on ",[23,563,23],{},"; the message is for humans.",[32,566,567,581],{},[35,568,569],{},[38,570,571,575,578],{},[41,572,573],{},[23,574,23],{},[41,576,577],{},"Which limit",[41,579,580],{},"What to do",[51,582,583,600,628,641,666],{},[38,584,585,590,593],{},[56,586,587],{},[23,588,589],{},"rate_limited",[56,591,592],{},"Requests per minute.",[56,594,595,596,599],{},"Wait for the ",[23,597,598],{},"Retry-After"," header (seconds), then retry the same request.",[38,601,602,607,622],{},[56,603,604],{},[23,605,606],{},"quota_exceeded",[56,608,609,610,613,614,617,618,621],{},"A daily or monthly quota; ",[23,611,612],{},"details.quota"," names it, with ",[23,615,616],{},"used"," and ",[23,619,620],{},"resets_at",".",[56,623,624,625,627],{},"Wait for ",[23,626,620],{},", or upgrade. Nothing to retry sooner.",[38,629,630,635,638],{},[56,631,632],{},[23,633,634],{},"send_rate_limited",[56,636,637],{},"The channel's send queue is saturated.",[56,639,640],{},"You are pushing sends faster than the queue drains; back off for a few seconds and slow the producer.",[38,642,643,648,663],{},[56,644,645],{},[23,646,647],{},"warmup_limit",[56,649,650,651,654,655,174,658,174,661,621],{},"Today's warm-up rung; ",[23,652,653],{},"details"," carries ",[23,656,657],{},"day",[23,659,660],{},"cap",[23,662,616],{},[56,664,665],{},"Stop sending for the day. The rung rises tomorrow.",[38,667,668,673,676],{},[56,669,670],{},[23,671,672],{},"cold_send_limit",[56,674,675],{},"This hour's budget of first messages to strangers.",[56,677,678],{},"Send only to people who have written to you until the hour resets; see below.",[10,680,681,682,684,685,688,689,692,693,621],{},"And one that is not a ",[23,683,25],{}," but belongs in the same handler: ",[23,686,687],{},"423 reachout_timelocked",", with\n",[23,690,691],{},"details.until",", means WhatsApp itself has paused new reach-outs from this number. Replies still\ngo through; cold sends do not until ",[23,694,695],{},"until",[207,697,699],{"className":267,"code":698,"language":269,"meta":212,"style":212},"{\n  \"error\": {\n    \"code\": \"quota_exceeded\",\n    \"message\": \"The plan quota for this resource is exhausted.\",\n    \"details\": { \"quota\": \"sent\", \"used\": 150, \"resets_at\": \"2026-09-16T00:00:00.000Z\" },\n    \"request_id\": \"req_01JRQ8F4X9N2K7YB3C5V6W8H0T\"\n  }\n}\n",[23,700,701,705,713,725,737,770,780,785],{"__ignoreMap":212},[216,702,703],{"class":218,"line":219},[216,704,277],{"class":276},[216,706,707,710],{"class":218,"line":234},[216,708,709],{"class":222},"  \"error\"",[216,711,712],{"class":276},": {\n",[216,714,715,718,720,723],{"class":218,"line":245},[216,716,717],{"class":222},"    \"code\"",[216,719,285],{"class":276},[216,721,722],{"class":230},"\"quota_exceeded\"",[216,724,291],{"class":276},[216,726,727,730,732,735],{"class":218,"line":306},[216,728,729],{"class":222},"    \"message\"",[216,731,285],{"class":276},[216,733,734],{"class":230},"\"The plan quota for this resource is exhausted.\"",[216,736,291],{"class":276},[216,738,739,742,744,747,749,752,754,756,758,760,762,764,766,768],{"class":218,"line":346},[216,740,741],{"class":222},"    \"details\"",[216,743,312],{"class":276},[216,745,746],{"class":222},"\"quota\"",[216,748,285],{"class":276},[216,750,751],{"class":230},"\"sent\"",[216,753,174],{"class":276},[216,755,325],{"class":222},[216,757,285],{"class":276},[216,759,94],{"class":301},[216,761,174],{"class":276},[216,763,335],{"class":222},[216,765,285],{"class":276},[216,767,375],{"class":230},[216,769,343],{"class":276},[216,771,772,775,777],{"class":218,"line":380},[216,773,774],{"class":222},"    \"request_id\"",[216,776,285],{"class":276},[216,778,779],{"class":230},"\"req_01JRQ8F4X9N2K7YB3C5V6W8H0T\"\n",[216,781,782],{"class":218,"line":414},[216,783,784],{"class":276},"  }\n",[216,786,787],{"class":218,"line":450},[216,788,548],{"class":276},[10,790,791,792,794,795,797],{},"A client that treats every ",[23,793,25],{}," as \"retry with exponential back-off\" will hammer a daily quota\nuselessly for hours. The retryable one is ",[23,796,589],{},", and it tells you exactly how long to\nwait:",[799,800,801,868,1217],"code-group",{},[207,802,807],{"className":803,"code":804,"filename":805,"language":806,"meta":212,"style":212},"language-bash shiki shiki-themes github-light-default github-dark-default","# --fail-with-body keeps the JSON error; -D - prints the headers so Retry-After is visible.\ncurl --fail-with-body -D - https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext \\\n  -H \"Authorization: Bearer $WAPITO_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"to\": \"+15551234567\", \"body\": \"Your table is ready.\"}'\n","curl","bash",[23,808,809,815,835,851,860],{"__ignoreMap":212},[216,810,811],{"class":218,"line":219},[216,812,814],{"class":813},"sU953","# --fail-with-body keeps the JSON error; -D - prints the headers so Retry-After is visible.\n",[216,816,817,820,823,826,829,832],{"class":218,"line":234},[216,818,805],{"class":819},"sTDnQ",[216,821,822],{"class":301}," --fail-with-body",[216,824,825],{"class":301}," -D",[216,827,828],{"class":230}," -",[216,830,831],{"class":230}," https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext",[216,833,834],{"class":226}," \\\n",[216,836,837,840,843,846,849],{"class":218,"line":245},[216,838,839],{"class":301},"  -H",[216,841,842],{"class":230}," \"Authorization: Bearer ",[216,844,845],{"class":276},"$WAPITO_TOKEN",[216,847,848],{"class":230},"\"",[216,850,834],{"class":226},[216,852,853,855,858],{"class":218,"line":306},[216,854,839],{"class":301},[216,856,857],{"class":230}," \"Content-Type: application\u002Fjson\"",[216,859,834],{"class":226},[216,861,862,865],{"class":218,"line":346},[216,863,864],{"class":301},"  -d",[216,866,867],{"class":230}," '{\"to\": \"+15551234567\", \"body\": \"Your table is ready.\"}'\n",[207,869,874],{"className":870,"code":871,"filename":872,"language":873,"meta":212,"style":212},"language-javascript shiki shiki-themes github-light-default github-dark-default","async function send(body) {\n  const res = await fetch('https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext', {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${process.env.WAPITO_TOKEN}`,\n      'Content-Type': 'application\u002Fjson',\n    },\n    body: JSON.stringify(body),\n  });\n  if (res.ok) return res.json();\n\n  const { error } = await res.json();\n  if (error.code === 'rate_limited') {\n    const seconds = Number(res.headers.get('retry-after') ?? '1');\n    await new Promise((r) => setTimeout(r, seconds * 1000));\n    return send(body);\n  }\n  \u002F\u002F quota_exceeded, warmup_limit, cold_send_limit: nothing to retry now.\n  throw new Error(`${error.code}: ${JSON.stringify(error.details ?? {})}`);\n}\n","Node","javascript",[23,875,876,897,922,932,937,963,975,980,996,1001,1020,1026,1050,1066,1103,1141,1152,1157,1163,1212],{"__ignoreMap":212},[216,877,878,881,884,888,891,894],{"class":218,"line":219},[216,879,880],{"class":226},"async",[216,882,883],{"class":226}," function",[216,885,887],{"class":886},"sbjLL"," send",[216,889,890],{"class":276},"(",[216,892,893],{"class":819},"body",[216,895,896],{"class":276},") {\n",[216,898,899,902,905,908,911,914,916,919],{"class":218,"line":234},[216,900,901],{"class":226},"  const",[216,903,904],{"class":301}," res",[216,906,907],{"class":226}," =",[216,909,910],{"class":226}," await",[216,912,913],{"class":886}," fetch",[216,915,890],{"class":276},[216,917,918],{"class":230},"'https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext'",[216,920,921],{"class":276},", {\n",[216,923,924,927,930],{"class":218,"line":245},[216,925,926],{"class":276},"    method: ",[216,928,929],{"class":230},"'POST'",[216,931,291],{"class":276},[216,933,934],{"class":218,"line":306},[216,935,936],{"class":276},"    headers: {\n",[216,938,939,942,945,948,950,953,955,958,961],{"class":218,"line":346},[216,940,941],{"class":276},"      Authorization: ",[216,943,944],{"class":230},"`Bearer ${",[216,946,947],{"class":276},"process",[216,949,621],{"class":230},[216,951,952],{"class":276},"env",[216,954,621],{"class":230},[216,956,957],{"class":301},"WAPITO_TOKEN",[216,959,960],{"class":230},"}`",[216,962,291],{"class":276},[216,964,965,968,970,973],{"class":218,"line":380},[216,966,967],{"class":230},"      'Content-Type'",[216,969,285],{"class":276},[216,971,972],{"class":230},"'application\u002Fjson'",[216,974,291],{"class":276},[216,976,977],{"class":218,"line":414},[216,978,979],{"class":276},"    },\n",[216,981,982,985,988,990,993],{"class":218,"line":450},[216,983,984],{"class":276},"    body: ",[216,986,987],{"class":301},"JSON",[216,989,621],{"class":276},[216,991,992],{"class":886},"stringify",[216,994,995],{"class":276},"(body),\n",[216,997,998],{"class":218,"line":485},[216,999,1000],{"class":276},"  });\n",[216,1002,1003,1006,1009,1012,1015,1017],{"class":218,"line":521},[216,1004,1005],{"class":226},"  if",[216,1007,1008],{"class":276}," (res.ok) ",[216,1010,1011],{"class":226},"return",[216,1013,1014],{"class":276}," res.",[216,1016,269],{"class":886},[216,1018,1019],{"class":276},"();\n",[216,1021,1022],{"class":218,"line":534},[216,1023,1025],{"emptyLinePlaceholder":1024},true,"\n",[216,1027,1028,1030,1033,1036,1039,1042,1044,1046,1048],{"class":218,"line":545},[216,1029,901],{"class":226},[216,1031,1032],{"class":276}," { ",[216,1034,1035],{"class":301},"error",[216,1037,1038],{"class":276}," } ",[216,1040,1041],{"class":226},"=",[216,1043,910],{"class":226},[216,1045,1014],{"class":276},[216,1047,269],{"class":886},[216,1049,1019],{"class":276},[216,1051,1053,1055,1058,1061,1064],{"class":218,"line":1052},13,[216,1054,1005],{"class":226},[216,1056,1057],{"class":276}," (error.code ",[216,1059,1060],{"class":226},"===",[216,1062,1063],{"class":230}," 'rate_limited'",[216,1065,896],{"class":276},[216,1067,1069,1072,1075,1077,1080,1083,1086,1088,1091,1094,1097,1100],{"class":218,"line":1068},14,[216,1070,1071],{"class":226},"    const",[216,1073,1074],{"class":301}," seconds",[216,1076,907],{"class":226},[216,1078,1079],{"class":886}," Number",[216,1081,1082],{"class":276},"(res.headers.",[216,1084,1085],{"class":886},"get",[216,1087,890],{"class":276},[216,1089,1090],{"class":230},"'retry-after'",[216,1092,1093],{"class":276},") ",[216,1095,1096],{"class":226},"??",[216,1098,1099],{"class":230}," '1'",[216,1101,1102],{"class":276},");\n",[216,1104,1106,1109,1112,1115,1118,1121,1123,1126,1129,1132,1135,1138],{"class":218,"line":1105},15,[216,1107,1108],{"class":226},"    await",[216,1110,1111],{"class":226}," new",[216,1113,1114],{"class":301}," Promise",[216,1116,1117],{"class":276},"((",[216,1119,1120],{"class":819},"r",[216,1122,1093],{"class":276},[216,1124,1125],{"class":226},"=>",[216,1127,1128],{"class":886}," setTimeout",[216,1130,1131],{"class":276},"(r, seconds ",[216,1133,1134],{"class":226},"*",[216,1136,1137],{"class":301}," 1000",[216,1139,1140],{"class":276},"));\n",[216,1142,1144,1147,1149],{"class":218,"line":1143},16,[216,1145,1146],{"class":226},"    return",[216,1148,887],{"class":886},[216,1150,1151],{"class":276},"(body);\n",[216,1153,1155],{"class":218,"line":1154},17,[216,1156,784],{"class":276},[216,1158,1160],{"class":218,"line":1159},18,[216,1161,1162],{"class":813},"  \u002F\u002F quota_exceeded, warmup_limit, cold_send_limit: nothing to retry now.\n",[216,1164,1166,1169,1171,1174,1176,1179,1181,1183,1185,1188,1190,1192,1194,1196,1198,1200,1202,1205,1208,1210],{"class":218,"line":1165},19,[216,1167,1168],{"class":226},"  throw",[216,1170,1111],{"class":226},[216,1172,1173],{"class":886}," Error",[216,1175,890],{"class":276},[216,1177,1178],{"class":230},"`${",[216,1180,1035],{"class":276},[216,1182,621],{"class":230},[216,1184,23],{"class":276},[216,1186,1187],{"class":230},"}: ${",[216,1189,987],{"class":301},[216,1191,621],{"class":230},[216,1193,992],{"class":886},[216,1195,890],{"class":230},[216,1197,1035],{"class":276},[216,1199,621],{"class":230},[216,1201,653],{"class":276},[216,1203,1204],{"class":226}," ??",[216,1206,1207],{"class":230}," {})",[216,1209,960],{"class":230},[216,1211,1102],{"class":276},[216,1213,1215],{"class":218,"line":1214},20,[216,1216,548],{"class":276},[207,1218,1223],{"className":1219,"code":1220,"filename":1221,"language":1222,"meta":212,"style":212},"language-python shiki shiki-themes github-light-default github-dark-default","import os\nimport time\nimport requests\n\nHEADERS = {\"Authorization\": f\"Bearer {os.environ['WAPITO_TOKEN']}\"}\n\n\ndef send(body: dict) -> dict:\n    while True:\n        res = requests.post(\n            \"https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext\", headers=HEADERS, json=body, timeout=(5, 30)\n        )\n        if res.ok:\n            return res.json()\n        error = res.json()[\"error\"]\n        if error[\"code\"] == \"rate_limited\":\n            time.sleep(int(res.headers.get(\"Retry-After\", \"1\")))\n            continue\n        # quota_exceeded, warmup_limit, cold_send_limit: nothing to retry now.\n        raise RuntimeError(f\"{error['code']}: {error.get('details')}\")\n","Python","python",[23,1224,1225,1233,1240,1247,1251,1291,1295,1299,1320,1330,1340,1379,1384,1392,1400,1416,1437,1459,1464,1469],{"__ignoreMap":212},[216,1226,1227,1230],{"class":218,"line":219},[216,1228,1229],{"class":226},"import",[216,1231,1232],{"class":276}," os\n",[216,1234,1235,1237],{"class":218,"line":234},[216,1236,1229],{"class":226},[216,1238,1239],{"class":276}," time\n",[216,1241,1242,1244],{"class":218,"line":245},[216,1243,1229],{"class":226},[216,1245,1246],{"class":276}," requests\n",[216,1248,1249],{"class":218,"line":306},[216,1250,1025],{"emptyLinePlaceholder":1024},[216,1252,1253,1256,1258,1261,1264,1266,1269,1272,1275,1278,1281,1284,1287,1289],{"class":218,"line":346},[216,1254,1255],{"class":301},"HEADERS",[216,1257,907],{"class":226},[216,1259,1260],{"class":276}," {",[216,1262,1263],{"class":230},"\"Authorization\"",[216,1265,285],{"class":276},[216,1267,1268],{"class":226},"f",[216,1270,1271],{"class":230},"\"Bearer ",[216,1273,1274],{"class":226},"{",[216,1276,1277],{"class":276},"os.environ[",[216,1279,1280],{"class":230},"'WAPITO_TOKEN'",[216,1282,1283],{"class":276},"]",[216,1285,1286],{"class":226},"}",[216,1288,848],{"class":230},[216,1290,548],{"class":276},[216,1292,1293],{"class":218,"line":380},[216,1294,1025],{"emptyLinePlaceholder":1024},[216,1296,1297],{"class":218,"line":414},[216,1298,1025],{"emptyLinePlaceholder":1024},[216,1300,1301,1304,1306,1309,1312,1315,1317],{"class":218,"line":450},[216,1302,1303],{"class":226},"def",[216,1305,887],{"class":886},[216,1307,1308],{"class":276},"(body: ",[216,1310,1311],{"class":301},"dict",[216,1313,1314],{"class":276},") -> ",[216,1316,1311],{"class":301},[216,1318,1319],{"class":276},":\n",[216,1321,1322,1325,1328],{"class":218,"line":485},[216,1323,1324],{"class":226},"    while",[216,1326,1327],{"class":301}," True",[216,1329,1319],{"class":276},[216,1331,1332,1335,1337],{"class":218,"line":521},[216,1333,1334],{"class":276},"        res ",[216,1336,1041],{"class":226},[216,1338,1339],{"class":276}," requests.post(\n",[216,1341,1342,1345,1347,1350,1352,1354,1356,1358,1360,1363,1366,1368,1370,1372,1374,1376],{"class":218,"line":534},[216,1343,1344],{"class":230},"            \"https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext\"",[216,1346,174],{"class":276},[216,1348,1349],{"class":819},"headers",[216,1351,1041],{"class":226},[216,1353,1255],{"class":301},[216,1355,174],{"class":276},[216,1357,269],{"class":819},[216,1359,1041],{"class":226},[216,1361,1362],{"class":276},"body, ",[216,1364,1365],{"class":819},"timeout",[216,1367,1041],{"class":226},[216,1369,890],{"class":276},[216,1371,119],{"class":301},[216,1373,174],{"class":276},[216,1375,105],{"class":301},[216,1377,1378],{"class":276},")\n",[216,1380,1381],{"class":218,"line":545},[216,1382,1383],{"class":276},"        )\n",[216,1385,1386,1389],{"class":218,"line":1052},[216,1387,1388],{"class":226},"        if",[216,1390,1391],{"class":276}," res.ok:\n",[216,1393,1394,1397],{"class":218,"line":1068},[216,1395,1396],{"class":226},"            return",[216,1398,1399],{"class":276}," res.json()\n",[216,1401,1402,1405,1407,1410,1413],{"class":218,"line":1105},[216,1403,1404],{"class":276},"        error ",[216,1406,1041],{"class":226},[216,1408,1409],{"class":276}," res.json()[",[216,1411,1412],{"class":230},"\"error\"",[216,1414,1415],{"class":276},"]\n",[216,1417,1418,1420,1423,1426,1429,1432,1435],{"class":218,"line":1143},[216,1419,1388],{"class":226},[216,1421,1422],{"class":276}," error[",[216,1424,1425],{"class":230},"\"code\"",[216,1427,1428],{"class":276},"] ",[216,1430,1431],{"class":226},"==",[216,1433,1434],{"class":230}," \"rate_limited\"",[216,1436,1319],{"class":276},[216,1438,1439,1442,1445,1448,1451,1453,1456],{"class":218,"line":1154},[216,1440,1441],{"class":276},"            time.sleep(",[216,1443,1444],{"class":301},"int",[216,1446,1447],{"class":276},"(res.headers.get(",[216,1449,1450],{"class":230},"\"Retry-After\"",[216,1452,174],{"class":276},[216,1454,1455],{"class":230},"\"1\"",[216,1457,1458],{"class":276},")))\n",[216,1460,1461],{"class":218,"line":1159},[216,1462,1463],{"class":226},"            continue\n",[216,1465,1466],{"class":218,"line":1165},[216,1467,1468],{"class":813},"        # quota_exceeded, warmup_limit, cold_send_limit: nothing to retry now.\n",[216,1470,1471,1474,1477,1479,1481,1483,1485,1488,1491,1493,1495,1497,1499,1502,1505,1508,1510,1512],{"class":218,"line":1214},[216,1472,1473],{"class":226},"        raise",[216,1475,1476],{"class":301}," RuntimeError",[216,1478,890],{"class":276},[216,1480,1268],{"class":226},[216,1482,848],{"class":230},[216,1484,1274],{"class":226},[216,1486,1487],{"class":276},"error[",[216,1489,1490],{"class":230},"'code'",[216,1492,1283],{"class":276},[216,1494,1286],{"class":226},[216,1496,285],{"class":230},[216,1498,1274],{"class":226},[216,1500,1501],{"class":276},"error.get(",[216,1503,1504],{"class":230},"'details'",[216,1506,1507],{"class":276},")",[216,1509,1286],{"class":226},[216,1511,848],{"class":230},[216,1513,1378],{"class":276},[28,1515,1517],{"id":1516},"the-send-queue","The send queue",[10,1519,1520,1521,1524,1525,1527],{},"Every channel has one first-in, first-out send queue. Messages leave it one at a time, spaced by\nthe channel's ",[23,1522,1523],{},"send_delay_ms"," — 1,500 ms by default, and no lower than 500 ms on Sandbox — plus a\nrandom jitter of up to 1,500 ms, so a burst of a hundred API calls becomes a hundred sends over a\nfew minutes that look like a person typing. If a call would have to wait more than twenty seconds\nin the queue it is refused with ",[23,1526,634],{}," instead of being held open, which is the\nsignal that your producer is running ahead of the number.",[10,1529,1530,1531,1534,1535,1538,1539,621],{},"Typing simulation rides on the same queue: a ",[23,1532,1533],{},"typing_time"," of up to fifteen seconds on a text\nsend, or ",[23,1536,1537],{},"typing_simulation: \"auto\""," in the channel settings, shows the recipient \"typing…\" for a\nduration proportional to the message before it is sent. These are anti-ban controls, not plan\nfeatures, and they are the same on every plan — see the ",[146,1540,1542],{"href":1541},"\u002Fdocs\u002Fanti-ban-guide\u002F","anti-ban guide",[28,1544,1546],{"id":1545},"the-warm-up-ladder","The warm-up ladder",[10,1548,1549],{},"A number that has just been linked is on day 0 of a ladder that caps how many messages it may\nsend each day, before the plan cap is even considered:",[32,1551,1552,1579],{},[35,1553,1554],{},[38,1555,1556,1559,1562,1564,1566,1569,1571,1573,1576],{},[41,1557,1558],{},"Day since first connection",[41,1560,1561],{},"0",[41,1563,61],{},[41,1565,116],{},[41,1567,1568],{},"3",[41,1570,471],{},[41,1572,119],{},[41,1574,1575],{},"6",[41,1577,1578],{},"7 +",[51,1580,1581],{},[38,1582,1583,1586,1589,1592,1595,1598,1601,1604,1606],{},[56,1584,1585],{},"Sends allowed that day",[56,1587,1588],{},"50",[56,1590,1591],{},"100",[56,1593,1594],{},"200",[56,1596,1597],{},"350",[56,1599,1600],{},"500",[56,1602,1603],{},"750",[56,1605,83],{},[56,1607,1608],{},"plan cap",[10,1610,1611,1612,1615,1616,1619,1620,1622],{},"The clock starts the first time the phone number connects and is not reset by a disconnect, a\nreconnect or a change of engine. It ",[14,1613,1614],{},"is"," reset when a different phone number is linked to the\nsame channel — the ladder belongs to the number, because it is the number WhatsApp is judging. A\nsend past the rung answers ",[23,1617,1618],{},"429 warmup_limit","; on Sandbox the plan's 150 a day is the lower of the\ntwo from day 2 on, and that case answers ",[23,1621,606],{}," instead, so the code always names the\nlimit that actually stopped you.",[28,1624,1626],{"id":1625},"the-cold-send-guard","The cold-send guard",[10,1628,1629,1630,1633],{},"A recipient is ",[14,1631,1632],{},"cold"," when they have never messaged the number, or when your first message to\nthem went out less than 24 hours ago and they have not replied since. Groups and Channels are\nnever cold. Each hour the channel may start only so many conversations with cold recipients:",[32,1635,1636,1646],{},[35,1637,1638],{},[38,1639,1640,1643],{},[41,1641,1642],{},"Warm-up day",[41,1644,1645],{},"Cold reach-outs per hour",[51,1647,1648,1656,1664],{},[38,1649,1650,1653],{},[56,1651,1652],{},"0 – 1",[56,1654,1655],{},"10",[38,1657,1658,1661],{},[56,1659,1660],{},"2 – 6",[56,1662,1663],{},"20",[38,1665,1666,1668],{},[56,1667,1578],{},[56,1669,1670],{},"60 on Premium, 20 on Sandbox",[10,1672,1673,1674,1678,1679,1682,1683,1686],{},"What is counted is the number of ",[1675,1676,1677],"em",{},"new"," cold recipients contacted in the last hour, not the\nnumber of messages: a follow-up to a stranger you wrote to twenty minutes ago spends no second\nslot. What is ",[1675,1680,1681],{},"blocked"," once the budget is spent is every cold send, follow-ups included — replies\nto people who have written to you keep flowing. The refusal is ",[23,1684,1685],{},"429 cold_send_limit",", and a\nrefused send is not counted anywhere.",[10,1688,1689,1690,1694],{},"This is the control most likely to surprise a team migrating from a library that had no such\nthing. It exists because messages to people who never asked for them are what gets a number\nreported, and reports are what get it banned. The ",[146,1691,1693],{"href":1692},"\u002Fblog\u002Fhow-to-avoid-whatsapp-bans\u002F","honest guide to avoiding bans","\nexplains the numbers behind the numbers.",[1696,1697,1700],"callout",{"ban-risk":1698,"type":1699},"medium","warn",[10,1701,1702],{},"None of the controls on this page can be raised by paying more. Premium lifts the plan quotas —\nunlimited sends and requests — but the send spacing, the warm-up ladder, the cold-send guard and\nthe timelock apply identically to every channel, because they are what keep the number alive.",[28,1704,1706],{"id":1705},"the-timelock","The timelock",[10,1708,1709,1710,1712,1713,1716,1717,1719,1720,1722],{},"WhatsApp sometimes puts a number into a state where it may reply but may not start new\nconversations for a while. Wapito detects that from the engine and reports it in\n",[23,1711,261],{}," under ",[23,1714,1715],{},"timelock","; while it is active, a cold send answers\n",[23,1718,687],{}," with ",[23,1721,691],{},", and replies go through unchanged. Treat it as a\nwarning shot: the number has been doing something WhatsApp did not like, and the right response\nis to lower the volume, not to route around the lock.",[1724,1725,1726],"style",{},"html pre.shiki code .sjgCt, html code.shiki .sjgCt{--shiki-default:#116329;--shiki-dark:#7EE787}html pre.shiki code .sjeE4, html code.shiki .sjeE4{--shiki-default:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .sSVrQ, html code.shiki .sSVrQ{--shiki-default:#0A3069;--shiki-dark:#A5D6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4rv2, html code.shiki .s4rv2{--shiki-default:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .sHrmB, html code.shiki .sHrmB{--shiki-default:#0550AE;--shiki-dark:#79C0FF}html pre.shiki code .sU953, html code.shiki .sU953{--shiki-default:#6E7781;--shiki-dark:#8B949E}html pre.shiki code .sTDnQ, html code.shiki .sTDnQ{--shiki-default:#953800;--shiki-dark:#FFA657}html pre.shiki code .sbjLL, html code.shiki .sbjLL{--shiki-default:#8250DF;--shiki-dark:#D2A8FF}",{"title":212,"searchDepth":234,"depth":234,"links":1728},[1729,1730,1731,1732,1733,1734,1735],{"id":30,"depth":234,"text":16},{"id":201,"depth":234,"text":202},{"id":557,"depth":234,"text":558},{"id":1516,"depth":234,"text":1517},{"id":1545,"depth":234,"text":1546},{"id":1625,"depth":234,"text":1626},{"id":1705,"depth":234,"text":1706},"2026-09-15","The request, send and check quotas on each plan, the headers that report what is left, and how throttled requests and 429 responses behave.","md",{},"\u002Fdocs\u002Frate-limits",{"title":5,"description":1737},"docs\u002Frate-limits",[1744,1745,1746],"rate-limits","quotas","plans",null,"6Eprkiq6vcYw3E4NEYVFY6fok5IwVdS6O00FbblR8qo",1790464894220]