You're Frustrated, I Get It
Nothing kills momentum like a 429 error right when you need to upload 50 files to Google Drive or sync a folder with Dropbox. Your code stops, you see "rate limit exceeded", and you're stuck.
The Real Fix: Either Batch or Backoff
What's actually happening here is you're sending too many API requests per second. Cloud providers like Google Drive, Dropbox, and OneDrive enforce a quota — usually 10-100 requests per second per user. Exceed it, and they return HTTP 429.
The fix has two paths. Pick one based on your use case:
- Batch requests — group multiple operations into one request. This works great for uploads, deletes, or metadata fetches. Google Drive API supports this natively.
- Exponential backoff — when you get a 429, wait, then retry, but increase the wait time each time. This works for any API.
Batch Example (Google Drive API)
from googleapiclient.http import BatchHttpRequest
def callback(request_id, response, exception):
if exception is not None:
print(f"{request_id} failed: {exception}")
batch = service.new_batch_http_request(callback=callback)
for file_id in file_ids:
request = service.files().delete(fileId=file_id)
batch.add(request)
batch.execute()
The reason batching works: instead of sending 50 separate HTTP requests (which counts against your rate limit 50 times), you send 1 batch that counts as 1 request. Most cloud storage APIs support up to 100 operations per batch.
Exponential Backoff Example (Works with Any API)
import time
import requests
def call_with_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
continue
return response
raise Exception("Max retries exceeded")
What's happening here: each retry waits 2^attempt seconds. So attempt 0 waits ~1s, attempt 1 waits ~2s, attempt 2 waits ~4s, up to 32 seconds. The random jitter prevents all clients from retrying at the same time (that's called thundering herd).
Why Step 1 or Step 2 Works
The core issue is you're using up your quota too fast. Quotas reset every second or minute, depending on the provider. By batching, you reduce the number of requests. By backing off, you give the quota time to reset. Either attack the bottleneck.
One detail people miss: many SDKs (like Google's Python client) have built-in retry logic, but they don't use exponential backoff by default. You need to enable it. For example:
from googleapiclient.http import build_http
from googleapiclient.errors import HttpError
# Enable exponential backoff in the client
service = build('drive', 'v3', credentials=creds,
http=build_http().request)
Less Common Variations
Not all 429 errors are the same. Here are two edge cases:
- Per-user vs per-project limits — some APIs (like Dropbox) limit per user, not per app. If you're using a shared account, other users might be eating your quota. The fix: use separate accounts or check your app's dashboard.
- IP-based throttling — if you're making requests from a shared IP (like a corporate proxy or cloud server), the limit might apply to that IP. Then batching won't help. You need to either get a dedicated IP or stagger requests across different times.
- Hidden throttling — some APIs return 200 OK but slow down responses when you're near the limit. Response times go from 100ms to 5s. The fix: monitor response time and slow down proactively.
How to Prevent This from Happening Again
Stop guessing. Here's what you actually need:
- Check your API quota in the provider's dashboard. Google Cloud Console, Dropbox App Console, Azure Portal — they all show your current usage and limit.
- Implement rate limiting in your code using a token bucket or leaky bucket algorithm. Libraries like
ratelimitin Python do this cleanly. - Monitor the Retry-After header in the 429 response. Some APIs tell you exactly how long to wait. Use that, not your own guess.
- Log every 429 with a timestamp and the number of requests made in the last minute. This helps you see patterns.
- Use the provider's SDK — they usually handle backoff and batching for you. Google Drive, Dropbox, and OneDrive all have official SDKs that do this correctly.
"But my app only makes 10 requests per second, and the limit is 100!" — I've seen this. What's happening is you're making requests per minute, not per second. Or you're hitting a different endpoint with a lower limit. Check the exact endpoint quota, not the overall account quota.
One last thing: don't use time.sleep(1) between requests. That's amateur hour. If your limit is 100 requests per second, waiting 1 second means you're using 1% of your quota. That's slow. Use a proper rate limiter that lets you send bursts up to the limit, then pauses just enough to stay under.