2025, Dec 13 05:00
Stop requests from freezing: set separate connect and read timeouts in Python to fail fast
Learn why a Python requests timeout may not stop hangs: set separate connect and read timeouts with a tuple to prevent freezes and fail fast in production APIs
When a production script that has run for years suddenly freezes on a network call, the expectation is that a timeout will protect the rest of the program. A common surprise is discovering that a timeout was set, yet the call still blocked and the function never returned. The case below shows how a request timed out while reading from the socket and why a more explicit timeout configuration prevents such stalls.
The scenario: a request timeout that didn’t time out
The function is called many times a day and is supposed to fail fast. It sets a timeout in requests, wraps the call in a try/except, and returns a fallback value on failure. Yet the execution froze during the API call, and the traceback ended with “TimeoutError: The read operation timed out”.
Problematic example
import requests
import json
def invoke_api():
payload_args = {
"someparameterhere": "example",
"moreparameterhere": "example"
}
headers_map = {
"Content-type": "application/json",
"X-ClientLocalIP": "11.161.0.23",
"X-ClientPublicIP": "11.22.55.66",
"X-MACAddress": "32:01:0a:a0:00:16",
"Accept": "application/json",
"X-PrivateKey": "privatekeyhere",
"X-UserType": "USER",
"X-SourceID": "WEB",
"Authorization": "Bearer somelongstringhere."
}
for _ in range(1):
try:
resp_obj = requests.request(
"POST",
"https://apiurl.com/apidataurlexample",
data=json.dumps(payload_args),
headers=headers_map,
timeout=2
).json()
return resp_obj
except Exception as err:
print(err)
return 0
The traceback clearly points to a read operation timing out at a lower level (ssl/socket), not returning control as expected. The question is why a defined timeout did not prevent the freeze.
What actually happened
This is a case of different “timeout” layers. A timeout value passed to requests is handled by the requests stack, typically raising a requests.exceptions.Timeout. In the observed failure, the call surfaced a TimeoutError originating in libraries below requests during the read phase. The distinction matters because you may not see the behavior you expect if the read operation times out outside of requests’ own exception class. In other words, a single timeout may not be enough to bound both connection establishment and response read.
Requests supports specifying timeouts as a tuple, which separates connect timeout from read timeout. That ensures the client gives up both when establishing the TCP/SSL connection and when waiting for the server to send response data.
:param timeout: (optional) How many seconds to wait for the server to send data before giving up, as a float, or a (connect timeout, read timeout) tuple.
Fix: enforce both connect and read timeouts explicitly
Set the timeout as a two-value tuple to cover both stages. This aligns with the observed “read operation timed out” traceback and prevents the call from stalling beyond your threshold.
import requests
import json
def invoke_api():
payload_args = {
"someparameterhere": "example",
"moreparameterhere": "example"
}
headers_map = {
"Content-type": "application/json",
"X-ClientLocalIP": "11.161.0.23",
"X-ClientPublicIP": "11.22.55.66",
"X-MACAddress": "32:01:0a:a0:00:16",
"Accept": "application/json",
"X-PrivateKey": "privatekeyhere",
"X-UserType": "USER",
"X-SourceID": "WEB",
"Authorization": "Bearer somelongstringhere."
}
for _ in range(1):
try:
resp_obj = requests.request(
"POST",
"https://apiurl.com/apidataurlexample",
data=json.dumps(payload_args),
headers=headers_map,
timeout=(2, 2)
).json()
return resp_obj
except Exception as err:
print(err)
return 0
By providing timeout=(2, 2), you configure a 2-second connect timeout and a 2-second read timeout. If the remote side stops sending data or stalls after the connection is made, the read timeout expires and the call returns control to your code instead of freezing.
Why this matters
In production, the difference between connection and read phases is not academic. Under load or when the upstream API has issues, a read can block even if the connection was quick. Explicitly setting both timeouts makes failure predictable and prevents the function from blocking the flow, whether you call it once or from multiple threads.
Conclusion
When a request appears to “freeze” despite a timeout, verify that you are constraining both connection and read phases. Use a tuple for timeout so your function fails fast and returns control even if the read operation stalls deep in the networking stack. With that in place, exceptions are raised and caught as intended, and the rest of the program keeps running.