Slack API Error Guide: 'ratelimited' HTTP 429 Too Many Requests
Fix the Slack API ratelimited / HTTP 429 error: honor Retry-After, respect per-method tiers, batch chat.postMessage, and back off correctly with curl examples.
- #slack
- #troubleshooting
- #errors
- #rate-limits
Overview
The ratelimited error (returned alongside HTTP status 429 Too Many Requests) means your app has exceeded the request budget Slack allows for a given method. Slack enforces per-method, per-workspace rate limits organized into tiers, and when you cross the threshold it rejects further calls and tells you how long to wait via the Retry-After response header. This is a throttling signal, not an error in your payload — the correct response is to back off and retry, not to change the request.
You will see the JSON body plus a 429 status and a Retry-After header:
HTTP/2 429
retry-after: 30
{
"ok": false,
"error": "ratelimited"
}
It occurs when bursty or high-frequency calls (loops over chat.postMessage, polling conversations.history, bulk users.info) outpace the method’s tier.
Symptoms
- Calls return HTTP
429witherror: ratelimited. - A
Retry-Afterheader gives the seconds to wait. - Failures cluster during bursts (startup syncs, fan-out notifications, backfills).
- Lower-tier methods (Tier 1/2) fail far sooner than Tier 4 methods.
curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"msg"}'
HTTP/2 429
retry-after: 30
Common Root Causes
1. Posting messages faster than ~1/second per channel
chat.postMessage is roughly Tier-limited to about one message per second per channel for sustained traffic. A tight loop blows past it.
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d "{\"channel\":\"C0123456789\",\"text\":\"$i\"}"
done
200
200
429
429
The 429s start once the burst exceeds the per-channel rate.
2. Polling a Tier-3 method too aggressively
conversations.history and similar Tier-3 methods allow roughly 50+ requests/minute. Polling every second across many channels exhausts the tier.
curl -s -D - -o /dev/null \
"https://slack.com/api/conversations.history?channel=C0123456789&limit=200" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN"
HTTP/2 429
retry-after: 12
3. Ignoring the Retry-After header
Retrying immediately after a 429 (instead of waiting Retry-After seconds) keeps you throttled and can extend the penalty.
RESP=$(curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"x"}')
echo "$RESP" | grep -i retry-after
retry-after: 30
You must sleep at least 30 seconds before the next call.
4. Fan-out without batching
Sending the same message to many users via individual chat.postMessage calls multiplies requests. Slack offers no bulk-post; you must throttle yourself.
curl -s -o /dev/null -w "%{http_code} %header{retry-after}\n" \
-X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"U0AAAAAAA","text":"broadcast"}'
429 18
5. Many app instances sharing one token
Rate limits are per-method per-workspace per-token. Several pods using the same bot token share one budget, so horizontal scaling multiplies the request rate without multiplying the limit.
curl -s "https://slack.com/api/auth.test" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
"ok": true,
"team_id": "T0AAAAAAA",
"user_id": "U0BOTBOT01"
}
All instances authenticating as the same user_id draw from the same bucket.
6. Tier-1 methods called repeatedly
Some methods are Tier 1 (about 1 request/minute). Calling them in a loop is almost guaranteed to throttle.
curl -s -D - -o /dev/null "https://slack.com/api/some.tier1.method" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN"
HTTP/2 429
retry-after: 47
Diagnostic Workflow
Step 1: Confirm it is a 429 with Retry-After
curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"probe"}'
A 429 status plus a retry-after header confirms rate limiting (versus a payload error returning 200 with ok:false).
Step 2: Identify which method is throttling
# Print status + retry-after for each method you call
curl -s -o /dev/null -w "history=%{http_code} retry=%header{retry-after}\n" \
"https://slack.com/api/conversations.history?channel=C0123456789&limit=200" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN"
Find the specific endpoint hitting 429; tiers differ, so the fix is method-specific.
Step 3: Honor Retry-After with a real wait
RA=$(curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"y"}' | awk -F': ' 'tolower($1)=="retry-after"{print $2+0}')
echo "waiting ${RA}s"; sleep "${RA:-1}"
Always sleep the advertised seconds before retrying.
Step 4: Throttle the client side
Introduce a token-bucket / fixed delay so sustained traffic stays under the method’s tier (e.g. one post per second per channel). Spread fan-out over time rather than bursting.
Step 5: Retry and confirm recovery
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"recovered"}'
200
Example Root Cause Analysis
An alerting service fans out an incident notice to 300 on-call users by looping chat.postMessage, and about a third of the deliveries fail. Capturing one failure:
curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"U0AAAAAAA","text":"PagerDuty incident #4821"}'
HTTP/2 429
retry-after: 22
The loop fires all 300 requests with no delay, far above the sustained per-method rate, so Slack throttles after the first burst. The code also retries failures immediately, ignoring Retry-After, which prolongs the throttle.
Fix: add a fixed delay between sends and honor Retry-After on any 429:
for u in $(cat oncall_user_ids.txt); do
code=$(curl -s -o /dev/null -w "%{http_code}" -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d "{\"channel\":\"$u\",\"text\":\"incident\"}")
[ "$code" = "429" ] && sleep 30
sleep 1
done
With one-second spacing and a 30-second backoff on throttle, all 300 messages deliver.
Prevention Best Practices
- Always read and obey the
Retry-Afterheader; never retry a429immediately. - Stay under each method’s tier — roughly one
chat.postMessageper second per channel — by rate-limiting on the client side. - Use exponential backoff with jitter for retries so many workers do not resynchronize and re-burst together.
- Spread fan-out and backfills over time instead of looping with no delay; Slack offers no bulk-post endpoint.
- Remember the budget is per-token per-workspace: scaling out pods on one token does not raise the limit — shard work, not the token’s quota.
- For ad-hoc triage, the free incident assistant can flag which method’s tier you are exceeding during a notification storm. See more in Slack guides.
Quick Command Reference
# Capture status + Retry-After
curl -s -D - -o /dev/null -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"probe"}'
# Status + retry-after in one line
curl -s -o /dev/null -w "%{http_code} %header{retry-after}\n" \
"https://slack.com/api/conversations.history?channel=C0123456789&limit=200" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN"
# Honor Retry-After then retry
RA=$(curl -s -D - -o /dev/null ... | awk -F': ' 'tolower($1)=="retry-after"{print $2+0}')
sleep "${RA:-1}"
# Confirm recovery
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
-d '{"channel":"C0123456789","text":"ok"}'
Conclusion
ratelimited / HTTP 429 means you exceeded a method’s tier, and Slack is asking you to slow down. The usual root causes:
- Posting faster than about one message per second per channel.
- Polling a Tier-3 method (like
conversations.history) too aggressively. - Ignoring
Retry-Afterand retrying immediately. - Fan-out without batching or spacing the requests.
- Multiple instances sharing one token’s per-workspace budget.
- Looping a Tier-1 method limited to roughly one request per minute.
Read the Retry-After header, wait the advertised time, and throttle the client to the method’s tier — rate limits are a pacing problem, solved by backoff rather than retrying harder.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.