Okay, so you hit your first 429 Too Many Requests and now you're wondering what happened. Yeah, this one tripped me up too when I first looked at it — the error message is technically correct but not super helpful about why you're being throttled.
Here's the deal: FieldPulse uses a token bucket algorithm for rate limiting. You get a burst capacity and then a steady refill rate. The headers in every response tell you where you stand.
Current Limits (as of November 2025)
These are subject to change, so always check the response headers, but here's what we're generally working with:
| Tier | Requests/minute | Burst capacity |
|---|---|---|
| Standard API keys | 60 | 100 |
| Enterprise/Partner | 300 | 500 |
| Webhook ingestion | 100 | 200 |
Yeah, the webhook ingestion limit is separate — I learned that the hard way when a customer started firing 200+ events per minute at our endpoint during their migration. The events queue, but you'll want to design for that.
Reading the Headers
Every API response includes these:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1731428400
That Reset is a Unix timestamp. Don't try to calculate it yourself — just respect when it says you're out. The server clock is the source of truth.
Backoff Strategies That Actually Work
Look, exponential backoff with jitter is the standard advice, but here's what I actually recommend based on watching a lot of integrations:
- First 429: Wait 1 second, retry
- Second 429: Wait 5 seconds, retry
- Third 429: Wait until
X-RateLimit-Reset, then retry - Still failing: Something's wrong with your design — don't just keep backing off indefinitely
The jitter part matters more than you'd think. If you've got 50 workers all hitting the same rate limit window, they'll synchronize their retries and hammer the server again. Add randomness: sleep = base * (2 ** attempt) + rand(0, 1000) / 1000.0
Design Patterns to Avoid Throttling Entirely
Better than handling 429s is not hitting them. A few patterns:
Batch where possible. The bulk endpoints (/work-orders/bulk, /customers/import) count as one request no matter how many records. I saw one integration go from 800 requests to 12 just by switching to bulk.
Use webhooks instead of polling. If you're hitting /work-orders every 30 seconds to check for status changes, just... don't. Set up webhooks instead. Way less load on both sides.
Cache aggressively. User metadata, service types, parts catalogs — these don't change often. Store them locally and refresh on a schedule, not per-request.
A Gotcha with Pagination
The page parameter is convenient but can burn through your rate limit fast on large datasets. If you're pulling thousands of records, use cursor-based pagination instead. The next_cursor in the response is stable across writes, which page definitely isn't.
Here's a pattern I keep reusing:
cursor = None
while True:
params = {'limit': 100}
if cursor:
params['cursor'] = cursor
resp = requests.get(url, params=params, headers=headers)
data = resp.json()
process(data['results'])
cursor = data.get('next_cursor')
if not cursor:
break
Not fancy, but it respects the limits and won't miss records if someone's creating work orders while you're paginating.
When to Ask for an Increase
If you're consistently hitting limits with legitimate use cases — high-volume syncing, real-time dashboards, whatever — reach out. We can bump enterprise accounts, but we need to know your pattern first. The limit exists to protect shared infrastructure, not to frustrate you specifically.
Worth noting: asking for 10,000 req/min because you don't want to implement caching will get a different response than "we're migrating 50,000 historical records and need a temporary window."
Related
- Using the FieldPulse API — authentication and general setup
- Setting Up Webhooks — the polling alternative
- Rate limit handling discussion — community patterns