I’ve been working on a small Python API client recently, and I realized the request itself is usually the easy part.
At first, my code was basically just:
requests.get() → raise_for_status() → parse JSON.
That worked fine until I started running into timeouts, 429 responses, and the occasional 502/503.
The part I’m still figuring out is how aggressive retry logic should be.
Right now, I’m thinking about handling failures roughly like this:
timeout / connection error → retry
429 → respect Retry-After if it exists
500 / 502 / 503 / 504 → retry a limited number of times
401 / 403 / 404 → fail immediately
POST requests → be much more careful because retrying can create duplicate side effects
I’m also using exponential backoff with a little jitter, mostly to avoid multiple requests retrying at exactly the same time.
What I’m less sure about is where this logic should live.
Would you normally keep retry behavior inside each API client so the rules stay explicit, or put it into a reusable retry layer that all clients share?
I can see advantages to both.
Keeping it local makes the behavior easier to understand, but centralizing it avoids repeating the same timeout/backoff code everywhere.
For people who deal with APIs in production, how do you usually decide which errors are actually retryable?
And do you treat GET and POST retries differently in practice?