Currently Available: Need a skilled Software Developer for your next project?
Categories
APIs Laravel

How to Set Timeouts and Retry Failed Requests with the Laravel HTTP Client

When Laravel code calls an external API, it waits for the server to respond. If the server is slow or unavailable, the request can take too long or fail. Laravel’s HTTP client lets you set a timeout for each request and retry failed requests. A timeout limits how long Laravel waits for a response. A retry sends the request again after a failure.

Choose both limits carefully. A short timeout keeps one attempt from waiting too long, while retries give temporary failures another chance. The examples below show how to combine timeouts and retries, handle connection timeouts, and avoid repeating requests that could duplicate work.

Set a timeout for each request

Use timeout() to set the maximum wait in seconds. If the server does not respond within that time, Laravel throws an Illuminate\Http\Client\ConnectionException. The Laravel HTTP client documentation shows this method. Check the documentation for your installed Laravel version to confirm the syntax.

use Illuminate\Support\Facades\Http;

$response = Http::timeout($timeoutSeconds)
    ->get($url);

Set $timeoutSeconds to a limit that fits the external service. A shorter limit frees your code sooner if the service stalls, but gives a slow, healthy response less time to arrive.

Laravel also lets you limit how long it waits to connect to the server. Use connectTimeout() for that. By default, Laravel allows 10 seconds to connect and 30 seconds for the request. You can set both limits:

$response = Http::connectTimeout($connectionTimeoutSeconds)
    ->timeout($timeoutSeconds)
    ->get($url);

Laravel applies the timeout to each request attempt. With retries, later attempts and the pauses between them add to the total waiting time.

Combine a timeout with retries

Use retry() to set the maximum number of attempts and the delay between attempts, in milliseconds. Chain timeout() and retry() before sending the request:

use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;

$response = Http::connectTimeout($connectionTimeoutSeconds)
    ->timeout($timeoutSeconds)
    ->retry(
        $attempts,
        $delayMilliseconds,
        function ($exception, $request) {
            return $exception instanceof ConnectionException;
        },
        throw: false
    )
    ->get($url);

This callback retries connection failures, including timeouts, and skips other failures. Without a callback, Laravel’s documented retry behavior covers client and server error responses, such as HTTP 4xx and 5xx responses.

The callback controls which failures Laravel retries. In this example, Laravel does not retry a response with an HTTP error status. The throw: false argument lets Laravel return that response instead of throwing a RequestException. You can then check it after the request:

if ($response->failed()) {
    // Handle the HTTP error response.
}

Without throw: false, Laravel can throw a RequestException for an HTTP error when you use retry(). A request without retries normally returns the error response instead. The error-handling documentation explains how to check these responses or throw an exception yourself.

throw: false does not suppress connection failures. If every connection attempt fails, Laravel still throws a ConnectionException. Catch it if your code needs to handle a server that cannot be reached.

Choose retry rules that fit the request

A retry sends the same request again. If an API processes a write but its response times out, Laravel cannot tell from the timeout alone whether the API completed the work. A retry can repeat a payment, order, or other state-changing action. Use the external API’s idempotency mechanism, if it provides one, or confirm that the operation is safe to repeat before enabling retries.

Passing a number as the second argument to retry() gives each retry the same delay. Laravel also accepts a callback there if you want to calculate the delay. You can instead pass an array of delays as the first argument, such as retry([100, 200]), to wait 100 milliseconds before the second attempt and 200 before the third.

If you need to retry a webhook much later, a queued job can handle those attempts. Your request-handling code can finish without waiting through the delays. Exponential backoff means multiplying the delay after each failure, for example waiting one second, then two, then four. Not every increasing delay follows that pattern.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *