Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Slack By James Joyner IV · · 10 min read

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 429 with error: ratelimited.
  • A Retry-After header 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-After header; never retry a 429 immediately.
  • Stay under each method’s tier — roughly one chat.postMessage per 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:

  1. Posting faster than about one message per second per channel.
  2. Polling a Tier-3 method (like conversations.history) too aggressively.
  3. Ignoring Retry-After and retrying immediately.
  4. Fan-out without batching or spacing the requests.
  5. Multiple instances sharing one token’s per-workspace budget.
  6. 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.

Free download · 368-page PDF

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.