Error Medic

Twitter API Rate Limit Errors: Fix 429, 401, 403, and 503 Responses

Fix Twitter API rate limit (429), unauthorized (401), forbidden (403), and 503 errors with step-by-step diagnostics, exponential backoff code, and tier upgrade

Last updated:
Last verified:
2,162 words
Key Takeaways
  • HTTP 429 (rate limited) means you have exhausted your endpoint-specific request window; check x-rate-limit-reset header and wait before retrying.
  • HTTP 401 (unauthorized) indicates an invalid, expired, or missing Bearer token or OAuth signature — regenerate credentials in the Twitter Developer Portal.
  • HTTP 403 (forbidden) means your app lacks the required scopes or your access tier does not include the endpoint you are calling.
  • HTTP 503 (service unavailable) is a Twitter-side outage; implement exponential backoff with jitter and monitor status.twitterstat.us.
  • Quick fix: inspect response headers (x-rate-limit-remaining, x-rate-limit-reset), implement retry-after logic, and confirm your app's access tier and OAuth scopes match the endpoint requirements.
Fix Approaches Compared
MethodWhen to UseTime to ApplyRisk
Inspect rate-limit headers and waitHTTP 429 with valid credentials< 5 minNone — safe, stateless
Regenerate Bearer tokenHTTP 401 after token expiry or revocation5–10 minLow — invalidates existing sessions
Re-authorize OAuth 1.0a keysHTTP 401 with signature mismatch10–15 minLow — apps using old keys stop working
Add missing OAuth scopes in Dev PortalHTTP 403 on specific endpoint5–10 min (re-auth users)Medium — requires user re-consent flow
Upgrade API access tierHTTP 403 on tier-locked endpoints or sustained 429s1–3 days (approval)Low — cost increase
Exponential backoff with jitterHTTP 429 and 503 in high-volume pipelines1–2 hours dev timeNone — improves reliability
Request caching layer (Redis/Memcached)Repeated identical lookups causing 429s2–4 hours dev timeMedium — stale data risk
Twitter Firehose / Enterprise APIVolume exceeds Pro tier limitsDays to weeks (sales)Low — significant cost

Understanding Twitter API Rate Limit and Auth Errors

Twitter's API v2 enforces per-endpoint, per-app, and per-user rate limits using a sliding window model. Every response includes diagnostic headers that tell you exactly where you stand. When those limits are breached — or when authentication is misconfigured — you receive one of four HTTP error codes: 401, 403, 429, or 503. Each has a distinct root cause and a distinct fix path.


Error Taxonomy

HTTP 429 Too Many Requests — twitter api rate limited

This is the most common error in production Twitter integrations. The response body looks like:

{
  "title": "Too Many Requests",
  "detail": "Too Many Requests",
  "type": "about:blank",
  "status": 429
}

The critical information is in the response headers:

  • x-rate-limit-limit — total requests allowed in this window
  • x-rate-limit-remaining — requests remaining before the window resets
  • x-rate-limit-reset — Unix timestamp when the window resets
  • retry-after — seconds to wait (present on some endpoints)

Twitter v2 rate limits are endpoint-specific. For example, GET /2/tweets/search/recent allows 450 requests per 15 minutes per app (using App-only auth) or 180 requests per 15 minutes per user (using user context). Hitting one endpoint's limit does not affect another endpoint.

HTTP 401 Unauthorized — twitter api 401 / twitter api unauthorized

Error response:

{
  "title": "Unauthorized",
  "type": "https://api.twitter.com/2/problems/unauthorized",
  "status": 401,
  "detail": "Unauthorized"
}

Root causes:

  1. Bearer token is missing from the Authorization: Bearer <token> header.
  2. Bearer token has been revoked (manually in Dev Portal or due to policy violation).
  3. OAuth 1.0a signature is malformed — wrong consumer key, timestamp skew > 5 minutes, or nonce reuse.
  4. Using v1.1 tokens against v2 endpoints without proper migration.

HTTP 403 Forbidden — twitter api 403

Error response:

{
  "title": "Forbidden",
  "type": "https://api.twitter.com/2/problems/not-authorized-for-resource",
  "status": 403,
  "detail": "You are not permitted to perform this action."
}

Root causes:

  1. Missing OAuth scopes — e.g., calling POST /2/tweets without the tweet.write scope.
  2. Access tier mismatch — e.g., calling filtered stream endpoints on the Free tier.
  3. App suspended or in read-only mode due to policy violation.
  4. Attempting to access another user's protected data without user-level auth.

HTTP 503 Service Unavailable — twitter api 503

This indicates Twitter infrastructure issues, not a client-side problem. The body is usually an HTML error page or empty. Check https://api.twitterstat.us before debugging your code.


Step 1: Diagnose the Error

For HTTP 429:

# Capture full headers on any curl request
curl -v -H "Authorization: Bearer $TWITTER_BEARER_TOKEN" \
  "https://api.twitter.com/2/tweets/search/recent?query=openai&max_results=10" 2>&1 \
  | grep -E "x-rate-limit|retry-after|< HTTP"

Look at x-rate-limit-remaining in the output. If it is 0, calculate seconds until reset:

# Convert x-rate-limit-reset Unix timestamp to human-readable
date -d @$(curl -sI -H "Authorization: Bearer $TWITTER_BEARER_TOKEN" \
  "https://api.twitter.com/2/tweets/search/recent?query=test" \
  | grep x-rate-limit-reset | awk '{print $2}' | tr -d '\r')

For HTTP 401:

Verify your Bearer token is being sent correctly:

# Minimal auth check
curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $TWITTER_BEARER_TOKEN" \
  "https://api.twitter.com/2/users/me"
# Expected: 200. If 401, token is bad.

For OAuth 1.0a apps, verify your system clock is synchronized (Twitter rejects requests with > 5 minutes skew):

timedatectl status | grep "System clock synchronized"
# If no: sudo systemctl restart systemd-timesyncd

For HTTP 403:

Check your app's scopes in the Developer Portal at https://developer.twitter.com/en/portal/apps. Compare against the endpoint's documented required scopes. Common scope requirements:

  • tweet.read — reading tweets
  • tweet.write — posting tweets
  • users.read — reading user profiles
  • offline.access — refresh tokens
  • dm.read, dm.write — direct messages

Step 2: Fix by Error Type

Fixing HTTP 429 — Implement Exponential Backoff

Never implement a naive time.sleep(15 * 60). Use exponential backoff with jitter so that multiple concurrent processes do not all hammer the API simultaneously when the window resets:

import time, random, requests

def twitter_get_with_backoff(url, headers, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers)
        if resp.status_code == 200:
            return resp.json()
        if resp.status_code == 429:
            reset_ts = int(resp.headers.get('x-rate-limit-reset', time.time() + 60))
            wait = max(reset_ts - time.time(), 1)
            jitter = random.uniform(0, 5)
            print(f"Rate limited. Waiting {wait + jitter:.1f}s (attempt {attempt + 1})")
            time.sleep(wait + jitter)
        elif resp.status_code in (500, 503):
            wait = (2 ** attempt) + random.uniform(0, 1)
            print(f"Server error {resp.status_code}. Backoff {wait:.1f}s")
            time.sleep(wait)
        else:
            resp.raise_for_status()
    raise Exception(f"Max retries exceeded for {url}")

Fixing HTTP 401 — Regenerate Credentials

  1. Navigate to https://developer.twitter.com/en/portal/apps
  2. Select your app → Keys and Tokens
  3. Click Regenerate next to Bearer Token
  4. Update your environment variable or secrets manager:
# Verify new token works immediately
export TWITTER_BEARER_TOKEN="your_new_token_here"
curl -s -H "Authorization: Bearer $TWITTER_BEARER_TOKEN" \
  "https://api.twitter.com/2/users/me" | jq '.data.username'

If you are using OAuth 1.0a and getting 401 with a oauth_signature problem, regenerate Access Token and Secret in the same portal section, then re-test.

Fixing HTTP 403 — Add Scopes or Upgrade Tier

If scopes are missing, you must re-run the OAuth authorization flow — existing tokens cannot be upgraded in place. For server-side apps using OAuth 2.0 PKCE:

# Construct new auth URL with corrected scopes
SCOPES="tweet.read%20tweet.write%20users.read%20offline.access"
echo "https://twitter.com/i/oauth2/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPES}&state=state123&code_challenge=challenge&code_challenge_method=plain"

If the endpoint requires a higher access tier:

Tier Cost Key Limits
Free $0 500k tweets/month read; no filtered stream
Basic $100/mo 10M tweets/month read; 1 filtered stream rule
Pro $5,000/mo 1M tweets/month read; 25 rules; full-archive search
Enterprise Custom Unrestricted; SLA; dedicated support

Apply for a tier upgrade at https://developer.twitter.com/en/portal/products.

Fixing HTTP 503 — Handle Gracefully

503s are transient. Add them to your retry logic (see backoff code above) and alert on sustained 503s:

# Quick status check
curl -s https://api.twitterstat.us/api/v2/status.json | jq '.status.description'

Step 3: Prevent Recurrence

Cache aggressively. User lookups, tweet metadata, and timeline data rarely need real-time freshness. A Redis cache with a 60–300 second TTL dramatically reduces API calls:

import redis, json
r = redis.Redis(host='localhost', port=6379, db=0)

def get_user_cached(user_id, headers):
    cache_key = f"twitter:user:{user_id}"
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)
    data = twitter_get_with_backoff(
        f"https://api.twitter.com/2/users/{user_id}", headers)
    r.setex(cache_key, 300, json.dumps(data))  # 5-minute TTL
    return data

Distribute requests across time. If you need to process 10,000 user lookups and your limit is 900 per 15 minutes, schedule batches with time.sleep guards or use a task queue (Celery, RQ) with rate-limiter middleware.

Monitor proactively. Log x-rate-limit-remaining on every response and alert when it drops below 10% of x-rate-limit-limit. This gives you advance warning before you hit zero.

Frequently Asked Questions

bash
#!/usr/bin/env bash
# twitter-api-diag.sh — Diagnose Twitter API auth and rate limit issues
# Usage: TWITTER_BEARER_TOKEN=xxxx bash twitter-api-diag.sh

set -euo pipefail

BASE="https://api.twitter.com/2"
TOKEN="${TWITTER_BEARER_TOKEN:-}"

if [[ -z "$TOKEN" ]]; then
  echo "ERROR: Set TWITTER_BEARER_TOKEN environment variable"
  exit 1
fi

echo "=== 1. Basic authentication check ==="
HTTP_CODE=$(curl -s -o /tmp/tw_auth_body.json -w "%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/me")
echo "HTTP $HTTP_CODE"
cat /tmp/tw_auth_body.json | python3 -m json.tool 2>/dev/null || cat /tmp/tw_auth_body.json
echo

echo "=== 2. Rate limit header inspection (search endpoint) ==="
curl -sI \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/tweets/search/recent?query=openai&max_results=10" \
  | grep -iE "x-rate-limit|retry-after|http/"
echo

echo "=== 3. Check rate limit remaining for /2/users/me ==="
RESET_TS=$(curl -sI \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/me" \
  | grep -i x-rate-limit-reset | awk '{print $2}' | tr -d '\r')
REMAINING=$(curl -sI \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/me" \
  | grep -i x-rate-limit-remaining | awk '{print $2}' | tr -d '\r')
echo "Remaining: ${REMAINING:-unknown}"
if [[ -n "${RESET_TS:-}" ]]; then
  RESET_HUMAN=$(date -d "@$RESET_TS" 2>/dev/null || date -r "$RESET_TS" 2>/dev/null || echo "N/A")
  echo "Reset at: $RESET_HUMAN (Unix: $RESET_TS)"
  NOW=$(date +%s)
  WAIT=$(( RESET_TS - NOW ))
  echo "Seconds until reset: $WAIT"
fi
echo

echo "=== 4. Twitter API status ==="
STATUS=$(curl -s "https://api.twitterstat.us/api/v2/status.json" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status',{}).get('description','unknown'))" 2>/dev/null || echo "Could not fetch status")
echo "Twitter status: $STATUS"
echo

echo "=== 5. System clock sync (critical for OAuth 1.0a) ==="
timedatectl status 2>/dev/null | grep -E "synchronized|NTP|RTC" || echo "timedatectl not available"
echo "Current UTC epoch: $(date -u +%s)"
echo

echo "=== Diagnosis complete ==="
E

Error Medic Editorial

The Error Medic Editorial team is composed of senior DevOps engineers, SREs, and API integration specialists with collective experience across hyperscaler infrastructure, developer tooling, and production incident response. We write deep-dive troubleshooting guides grounded in real production failures — no filler, no hand-waving, just reproducible diagnostics and proven fixes.

Sources

Related Articles in Twitter Api

Explore More API Errors Guides