Python Requests: Reachable Token Refresh and Safe Failure Boundaries
TECH INSIGHT / EXPLAINED
Handle transport errors, HTTP status, authentication and JSON decoding as separate boundaries. A useful wrapper must make the authentication branch reachable and must not automatically repeat order creation.
Local execution of the actual project
These captures show the existing project UI or functions executed locally with non-private sample inputs. Each caption states the scope of the execution.
Timeout is not a total stopwatch
Requests has no default timeout. timeout=(3,10) configures connection and read inactivity limits; ten seconds is not a guarantee that the whole body finishes within ten seconds. A response that keeps delivering bytes can take longer. Use an overall operation budget in addition to per-request limits.
Inspect 401 before raising it
The example allows one refresh only for a trusted read API whose authentication contract supports that action. It handles 401 before raise_for_status, closes the first response, updates the header and tries once more. A second 401 raises normally. A 403 can mean insufficient permission and must not automatically trigger refresh.
def authenticated_get_json(session, url, token, refresh_token):
# For a trusted read-only API whose contract permits one 401 refresh.
for attempt in range(2):
response = session.get(url,
headers={'Authorization': 'Bearer ' + token},
timeout=(3, 10), allow_redirects=False)
try:
if response.status_code == 401 and attempt == 0:
needs_refresh = True
else:
if not 200 <= response.status_code < 300:
response.raise_for_status()
raise RuntimeError('unexpected non-success status')
return response.json()
finally:
response.close()
if needs_refresh:
token = refresh_token() # returns a new token, exactly once
raise RuntimeError('unreachable')Retry only where the operation permits it
For safe reads, selected connection failures, 429 and transient 5xx can justify bounded backoff with jitter. Honor both seconds and HTTP-date Retry-After values, and stop when the overall deadline or attempt budget is exhausted. Do not silently shorten a server-requested delay.
For a POST order, a timeout may mean accepted-but-unconfirmed. Query its documented identifier and reconcile before deciding on another submission. This example intentionally does not implement a generic retry(method, url) that would hide that distinction.
JSON success is another check
A 2xx response can contain invalid JSON or the wrong schema. Catch the decoding error where the application can respond meaningfully, then validate required keys, types and domain ranges. Do not log the first 200 characters of every body: that fragment may contain personal data or secrets. Prefer status, endpoint label, content type, body length and a sanitized error code.
Test four paths without a real server: 401→200 refreshes once; 401→401 stops; 403 never refreshes; 200 with invalid JSON propagates a decoding failure. These tests verify control flow that a successful live request never exercises.
Sources and further reading
Requests timeout behavior · HTTP semantics · Rate-limit companion
Content and examples reviewed: 2026-09-29
댓글
댓글 쓰기