Most developers set a single HTTP timeout on their API client and forget about it. Thirty seconds, sixty seconds, whatever felt reasonable when they wrote the code. Then a downstream service degrades, requests start piling up, and the whole thing falls over in a way that is hard to diagnose because every failure looks the same in the logs.
A better approach has three layers. First, a connect timeout — how long you are willing to wait for a TCP handshake. This should be short, maybe three to five seconds. If you cannot establish a connection in five seconds, the server is probably not coming back anytime soon. Second, a read timeout — how long you will wait for the first byte of the response after the request is sent. This can be longer, maybe ten to twenty seconds, depending on how heavy the endpoint is. Third, and most importantly, a total timeout that covers the entire round trip. This is your safety net.
The common failure mode is setting the total timeout too high and relying on it alone. When a service slows down, every caller opens a connection, waits the full sixty seconds, times out, and retries. Meanwhile, the struggling service gets hammered with retry traffic on top of the original load. Setting a tighter connect timeout and a reasonable read timeout means fast failures, which means less accumulated load on both sides.
One more thing that helps: circuit breakers. After N consecutive failures, stop calling the service for a cooling-off period. This is standard practice in distributed systems but still surprisingly rare in application-level API clients. If your HTTP client library supports it, turn it on. If it does not, a simple counter and a sleep timer is better than nothing.