<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sundarapandy</title>
    <description>The latest articles on DEV Community by Sundarapandy (@sundarapandy).</description>
    <link>https://dev.to/sundarapandy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3876705%2Fde19f23b-c7fe-4a97-b596-6283a1af595a.png</url>
      <title>DEV Community: Sundarapandy</title>
      <link>https://dev.to/sundarapandy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sundarapandy"/>
    <language>en</language>
    <item>
      <title>What Happens When an API Call Fails? A Practical Guide to Timeouts, Retries, and Circuit Breakers</title>
      <dc:creator>Sundarapandy</dc:creator>
      <pubDate>Mon, 21 Sep 2026 13:23:07 +0000</pubDate>
      <link>https://dev.to/sundarapandy/what-happens-when-an-api-call-fails-a-practical-guide-to-timeouts-retries-and-circuit-breakers-297i</link>
      <guid>https://dev.to/sundarapandy/what-happens-when-an-api-call-fails-a-practical-guide-to-timeouts-retries-and-circuit-breakers-297i</guid>
      <description>&lt;p&gt;Your app submits a request to an external API.&lt;br&gt;
Usually, everything works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Application → External API → Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But what happens when the external API becomes slow?&lt;/p&gt;

&lt;p&gt;Or stops responding completely?&lt;/p&gt;

&lt;p&gt;Your application may keep waiting, requests may start piling up, and eventually other parts of your system can become affected.&lt;br&gt;
This is why production applications need to be designed not only for successful requests, but also for failures.&lt;/p&gt;

&lt;p&gt;In this post, we'll look at three important mechanisms for handling unreliable dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Circuit breakers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also look at exponential backoff and graceful degradation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why API Failures Are Different in Production
&lt;/h2&gt;

&lt;p&gt;When developing locally, an API call often looks simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application&lt;br&gt;
    ↓&lt;br&gt;
API Request&lt;br&gt;
    ↓&lt;br&gt;
External Service&lt;br&gt;
    ↓&lt;br&gt;
Response&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In production, there are many things that can go wrong.&lt;br&gt;
An external service might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take too long to respond&lt;/li&gt;
&lt;li&gt;Return a 500 error&lt;/li&gt;
&lt;li&gt;Become temporarily unavailable&lt;/li&gt;
&lt;li&gt;Rate-limit your requests&lt;/li&gt;
&lt;li&gt;Experience network problems&lt;/li&gt;
&lt;li&gt;Return an unexpected response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, imagine an e-commerce application that calls a recommendation service:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User&lt;br&gt;
  ↓&lt;br&gt;
E-commerce API&lt;br&gt;
  ↓&lt;br&gt;
Recommendation Service&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the recommendation service becomes unavailable, should the entire application stop working?&lt;br&gt;
Usually, no.&lt;br&gt;
The application should have a strategy for handling that failure.&lt;br&gt;
That's where resilience patterns become important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeouts: Don't Wait Forever
&lt;/h2&gt;

&lt;p&gt;The first rule is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never allow an external request to wait indefinitely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose your application sends a request to a payment service:&lt;br&gt;
&lt;strong&gt;Application → Payment API&lt;/strong&gt;&lt;br&gt;
If the payment API doesn't respond, your application might continue waiting.&lt;br&gt;
If enough requests do this simultaneously, you can end up with many requests consuming connections and resources.&lt;/p&gt;

&lt;p&gt;A timeout places a limit on how long the application will wait.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application&lt;br&gt;
     ↓&lt;br&gt;
Payment API&lt;br&gt;
     ↓&lt;br&gt;
No response&lt;br&gt;
     ↓&lt;br&gt;
Timeout&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;response = requests.get(&lt;br&gt;
    "https://api.example.com/payment",&lt;br&gt;
    timeout=5 &lt;br&gt;
)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The exact timeout value depends on the operation and system requirements, but the important principle is:&lt;br&gt;
&lt;strong&gt;Every external dependency should have an intentional timeout.&lt;/strong&gt;&lt;br&gt;
A timeout doesn't fix the underlying failure.&lt;br&gt;
It prevents one slow dependency from holding your application hostage indefinitely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retries: Temporary Failures Can Recover
&lt;/h2&gt;

&lt;p&gt;Some failures are temporary.&lt;br&gt;
A network request might fail once and succeed a moment later.&lt;br&gt;
In those cases, retrying can be useful.&lt;br&gt;
A basic retry flow looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Request&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;br&gt;
   ↓&lt;br&gt;
Success&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
Attempt 1 → Failed&lt;br&gt;
Attempt 2 → Failed&lt;br&gt;
Attempt 3 → Success&lt;/p&gt;

&lt;p&gt;This can be useful for transient problems such as temporary network failures or certain server errors.&lt;/p&gt;

&lt;p&gt;However, retries should not be added blindly.&lt;br&gt;
Imagine 1,000 requests reach a service that is already struggling.&lt;br&gt;
If every request immediately retries three times, the service could suddenly receive thousands of additional requests. Instead of recovering, the situation can become worse. This is sometimes referred to as a retry storm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exponential Backoff
&lt;/h2&gt;

&lt;p&gt;One way to make retries safer is to introduce a delay between attempts.&lt;br&gt;
Instead of:&lt;br&gt;
&lt;strong&gt;Retry → Retry → Retry&lt;/strong&gt;&lt;br&gt;
you can use:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Request&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Wait&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;br&gt;
   ↓&lt;br&gt;
Failure&lt;br&gt;
   ↓&lt;br&gt;
Wait longer&lt;br&gt;
   ↓&lt;br&gt;
Retry&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is called &lt;strong&gt;exponential backoff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A simplified example might look like:&lt;br&gt;
&lt;strong&gt;Attempt 1&lt;/strong&gt; → Failure&lt;br&gt;
Wait 1 second&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 2&lt;/strong&gt; → Failure&lt;br&gt;
Wait 2 seconds&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 3&lt;/strong&gt; → Failure&lt;br&gt;
Wait 4 seconds&lt;/p&gt;

&lt;p&gt;The delay increases after each failed attempt.&lt;br&gt;
In distributed systems, jitter is often added to these delays as well. This prevents many clients from retrying at exactly the same time.&lt;/p&gt;

&lt;p&gt;A practical retry strategy therefore usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A maximum number of retries&lt;/li&gt;
&lt;li&gt;Exponential backoff&lt;/li&gt;
&lt;li&gt;Jitter&lt;/li&gt;
&lt;li&gt;Appropriate timeout values&lt;/li&gt;
&lt;li&gt;Rules for which errors are retryable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Don't Retry Every Request
&lt;/h2&gt;

&lt;p&gt;This is an important distinction. Not every failed request should automatically be retried.&lt;br&gt;
For example, retrying a read operation may be relatively safe in many cases.&lt;br&gt;
But consider a request that creates a payment or places an order.&lt;br&gt;
If the server processed the request successfully but the response was lost, your client may not know whether the operation actually happened.&lt;br&gt;
Automatically sending the same request again could potentially create a duplicate operation.&lt;br&gt;
This is why &lt;strong&gt;idempotency&lt;/strong&gt; matters.&lt;/p&gt;

&lt;p&gt;For operations that may be retried, systems often use idempotency keys or other mechanisms to ensure that repeating the request doesn't accidentally repeat the business operation.&lt;br&gt;
The key question isn't:&lt;br&gt;
“&lt;strong&gt;Can we retry this request&lt;/strong&gt;?”&lt;br&gt;
It's:&lt;br&gt;
“&lt;strong&gt;What happens if we retry this request?&lt;/strong&gt;”&lt;/p&gt;

&lt;h2&gt;
  
  
  Circuit Breakers: Stop Calling a Failing Service
&lt;/h2&gt;

&lt;p&gt;Now consider a different situation.&lt;br&gt;
An external service isn't failing once or twice.&lt;br&gt;
It's been failing continuously for several minutes.&lt;br&gt;
Should your application keep sending requests?&lt;br&gt;
Probably not.&lt;br&gt;
This is where a &lt;strong&gt;circuit breaker&lt;/strong&gt; can help.&lt;br&gt;
A circuit breaker generally has three states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Success&lt;br&gt;
      ┌───────────┐&lt;br&gt;
      │           ↓&lt;br&gt;
   CLOSED → OPEN → HALF-OPEN&lt;br&gt;
      ↑                 │&lt;br&gt;
      └─────────────────┘&lt;br&gt;
          Recovery&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Closed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is operating normally.&lt;br&gt;
Requests are allowed through.&lt;br&gt;
Application → External Service&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dependency is consistently failing.&lt;br&gt;
The circuit opens and stops sending requests to that dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application → Circuit Breaker → Fallback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prevents your application from repeatedly calling a service that is already unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After some time, the circuit breaker allows a limited request through to check whether the dependency has recovered.&lt;/p&gt;

&lt;p&gt;If the request succeeds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open → Closed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it fails again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Half-Open → Open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This gives the failing service time to recover while protecting your application from repeated failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graceful Degradation
&lt;/h2&gt;

&lt;p&gt;Sometimes the best solution isn't retrying at all.&lt;br&gt;
Instead, your application can provide a reduced version of the functionality.&lt;br&gt;
Consider an online shopping application:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Product Page&lt;br&gt;
     ↓&lt;br&gt;
Recommendation Service&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The recommendation service goes down.&lt;br&gt;
You could make the entire product page fail.&lt;br&gt;
Or you could simply hide the recommendations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Product Page&lt;br&gt;
     ↓&lt;br&gt;
Recommendation Service ❌&lt;br&gt;
     ↓&lt;br&gt;
Show Product Without Recommendations&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user can still view the product and continue shopping.&lt;br&gt;
This is called graceful degradation.&lt;br&gt;
The idea is simple:&lt;br&gt;
When one feature fails, don't necessarily let the entire system fail with it.&lt;br&gt;
Putting the Patterns Together&lt;br&gt;
These mechanisms work particularly well when combined.&lt;br&gt;
A simplified architecture could look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            ┌───────────────┐
            │ Your Service  │
            └───────┬───────┘
                    ↓
             ┌─────────────┐
             │  Timeout    │
             └──────┬──────┘
                    ↓
             ┌─────────────┐
             │    Retry    │
             └──────┬──────┘
                    ↓
          ┌──────────────────┐
          │  Circuit Breaker │
          └─────────-────────┘
                    ↓
             External Service
                    ↓
              ┌─────────┐
              │Fallback │
              └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The exact implementation depends on your architecture, but the concepts work together:&lt;br&gt;
&lt;strong&gt;Timeouts&lt;/strong&gt; prevent requests from waiting indefinitely.&lt;br&gt;
&lt;strong&gt;Retries&lt;/strong&gt; handle certain temporary failures.&lt;br&gt;
&lt;strong&gt;Backoff&lt;/strong&gt; prevents retries from overwhelming a failing service.&lt;br&gt;
&lt;strong&gt;Circuit breakers&lt;/strong&gt; interrupt repeated calls to an unhealthy Service.&lt;br&gt;
&lt;strong&gt;Fallbacks&lt;/strong&gt; allow your application to continue operating when possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should You Monitor?
&lt;/h2&gt;

&lt;p&gt;Adding resilience mechanisms isn't enough.&lt;br&gt;
You also need to know when they're being triggered.&lt;br&gt;
Useful metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request failure rate&lt;/li&gt;
&lt;li&gt;Timeout rate&lt;/li&gt;
&lt;li&gt;Retry count&lt;/li&gt;
&lt;li&gt;Retry success rate&lt;/li&gt;
&lt;li&gt;Circuit breaker state&lt;/li&gt;
&lt;li&gt;External API latency&lt;/li&gt;
&lt;li&gt;Fallback frequency&lt;/li&gt;
&lt;li&gt;Dependency availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if your retry success rate suddenly drops from 90% to 10%, that could indicate that the dependency has a larger problem.&lt;br&gt;
Without monitoring, your system may appear to be working while silently relying on retries and fallbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Checklist
&lt;/h2&gt;

&lt;p&gt;When your application depends on external services, consider the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;br&gt;
Set a reasonable timeout for every external request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry&lt;/strong&gt;&lt;br&gt;
Retry only failures that are safe and potentially temporary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backoff&lt;/strong&gt;&lt;br&gt;
Avoid immediate repeated retries. Use exponential backoff and, where appropriate, jitter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency&lt;/strong&gt;&lt;br&gt;
Make sure retrying an operation doesn't accidentally duplicate a business action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circuit Breaker&lt;/strong&gt;&lt;br&gt;
Stop repeatedly calling a dependency that is consistently failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fallback&lt;/strong&gt;&lt;br&gt;
Decide whether your application can continue with reduced functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;br&gt;
Track failures, retries, latency, and dependency health.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;External services will eventually become slow, unavailable, or unpredictable.&lt;br&gt;
The objective is not to create a system that will never fail.&lt;br&gt;
That's unrealistic.&lt;br&gt;
The goal is to build a system that fails predictably, limits the impact of failures, and recovers when dependencies become healthy again.&lt;br&gt;
Timeouts, retries, exponential backoff, circuit breakers, and graceful degradation are some of the building blocks that make that possible.&lt;/p&gt;

&lt;p&gt;The next time you add an external API to your application, don't ask only:&lt;br&gt;
&lt;strong&gt;“What happens when the API works?”&lt;/strong&gt;&lt;br&gt;
Also ask:&lt;br&gt;
&lt;strong&gt;“What happens when it doesn't?”&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
