API rate limits for bulk work order creation?

POST /work_orders — hitting 429 after ~50 sequential creates. Docs mention 100/min but no burst behavior specified. Need to push 5K records for migration.

Current approach:

for record in dataset:
    POST /work_orders
    sleep(0.6)  # naive rate limit

Still throttled. What's the actual window? Token bucket vs fixed window? Retry-After header sometimes missing.

Also: any batch endpoint planned? Single-request payload limits?

Parents
  • hey carlos — yeah this one tripped me up too when I first looked at it. the 100/min is actually per API key and it's a sliding window, not fixed, which is why your 0.6s sleep can still hit edge cases depending on when your first request started.

    the real gotcha: the burst allowance is not documented publicly but in practice you get about 10 requests in a 1-second burst before the throttle kicks in hard. after that it's ~1.67/sec sustained.

    here's what I ended up with for a similar migration:

    import time
    import requests
    from collections import deque
    
    class ThrottledClient:
        def __init__(self, key, rate=100, per=60):
            self.key = key
            self.min_interval = per / rate
            self.requests = deque()
        
        def _wait_if_needed(self):
            now = time.time()
            while self.requests and self.requests[0] < now - 60:
                self.requests.popleft()
            if len(self.requests) >= 100:
                sleep_time = 60 - (now - self.requests[0]) + 0.1
                time.sleep(max(0, sleep_time))
            
        def post(self, payload):
            self._wait_if_needed()
            # ... actual request ...
            self.requests.append(time.time())

    re: batch endpoint — nothing public yet, but I've heard rumblings about a bulk import API for Q4. no promises though, and it probably won't cover the full work order schema on first release.

    heads up: if you're creating with technician assignments in the same payload, that counts as an extra internal call against your limit. might explain why you're seeing fewer effective requests than expected.

Reply
  • hey carlos — yeah this one tripped me up too when I first looked at it. the 100/min is actually per API key and it's a sliding window, not fixed, which is why your 0.6s sleep can still hit edge cases depending on when your first request started.

    the real gotcha: the burst allowance is not documented publicly but in practice you get about 10 requests in a 1-second burst before the throttle kicks in hard. after that it's ~1.67/sec sustained.

    here's what I ended up with for a similar migration:

    import time
    import requests
    from collections import deque
    
    class ThrottledClient:
        def __init__(self, key, rate=100, per=60):
            self.key = key
            self.min_interval = per / rate
            self.requests = deque()
        
        def _wait_if_needed(self):
            now = time.time()
            while self.requests and self.requests[0] < now - 60:
                self.requests.popleft()
            if len(self.requests) >= 100:
                sleep_time = 60 - (now - self.requests[0]) + 0.1
                time.sleep(max(0, sleep_time))
            
        def post(self, payload):
            self._wait_if_needed()
            # ... actual request ...
            self.requests.append(time.time())

    re: batch endpoint — nothing public yet, but I've heard rumblings about a bulk import API for Q4. no promises though, and it probably won't cover the full work order schema on first release.

    heads up: if you're creating with technician assignments in the same payload, that counts as an extra internal call against your limit. might explain why you're seeing fewer effective requests than expected.

Children
No Data