Implementing exponential backoff for 429 errors during bulk SCIM user updates

Just noticed that our nightly SCIM sync job is hitting the rate limiter harder than expected. We’re provisioning ~5k users from Okta to Genesys Cloud using the PUT /api/v2/scim/v2/Users/{id} endpoint. The job runs on a single-threaded worker to keep ordering predictable, but we’re seeing a spike in 429 Too Many Requests responses around the 200th request.

The docs mention rate limiting but don’t give a clear algorithm for backoff when using the REST API directly Genesys Docs. I’m trying to implement a standard exponential backoff with jitter in Python using requests, but the Retry-After header isn’t always present in the 429 response, or it’s zero.

Here is my current retry logic:

import requests
import time
import random

def update_user_with_backoff(url, headers, data, max_retries=5):
 for attempt in range(max_retries):
 response = requests.put(url, headers=headers, json=data)
 if response.status_code == 200:
 return response.json()
 elif response.status_code == 429:
 retry_after = response.headers.get('Retry-After', 0)
 wait_time = float(retry_after) if retry_after > 0 else (2 ** attempt) + random.uniform(0, 1)
 print(f"Rate limited. Waiting {wait_time}s...")
 time.sleep(wait_time)
 else:
 response.raise_for_status()
 raise Exception("Max retries exceeded")

Is there a specific Retry-After value I should expect, or should I be parsing a custom header? Also, is there a better pattern for bulk updates than sequential PUTs? I want to avoid getting our OAuth client throttled globally.

How I usually solve this is by wrapping the API call in a retry loop that respects the Retry-After header.
Cause: The 429 response includes a Retry-After header specifying the exact wait time in seconds.
Solution: Use this Python snippet to handle the backoff correctly instead of guessing.

import time
import requests

def update_user_with_backoff(url, headers, data, max_retries=3):
 for attempt in range(max_retries):
 response = requests.put(url, headers=headers, json=data)
 if response.status_code == 429:
 wait_time = int(response.headers.get('Retry-After', 2 ** (attempt + 1)))
 print(f"Rate limited. Waiting {wait_time}s...")
 time.sleep(wait_time)
 continue
 return response
 raise Exception("Max retries exceeded")