PHPackages                             ctroms/retryable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. ctroms/retryable

ActivePackage[HTTP &amp; Networking](/categories/http)

ctroms/retryable
================

Easily determine and retry fails requests with configuratble strategies

v1.2.0(3y ago)61971MITPHPPHP ^7.2.5|^8.0

Since Apr 13Pushed 3y ago3 watchersCompare

[ Source](https://github.com/ctroms/retryable)[ Packagist](https://packagist.org/packages/ctroms/retryable)[ RSS](/packages/ctroms-retryable/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (5)Dependencies (2)Versions (6)Used By (0)

Retryable
=========

[](#retryable)

Retryable is a package built for the Laravel framework that makes retrying failed requests a breeze.

Installation
------------

[](#installation)

```
composer install ctroms/retryable
```

Usage
-----

[](#usage)

#### Default Strategy

[](#default-strategy)

The quickest way to get started is by simply passing a callable with your request logic to `retry()`. By default, this uses an exponential backoff with constant jitter between 0 and 1000ms and a maximum delay of 64 seconds under the hood. Simply pass a callable with your retryable logic as the argument.

```
$response = Retry::retry(function () {
    return $this->client->request('GET', $url, $params);
});
```

### Fluent Strategy Builder

[](#fluent-strategy-builder)

The `Retry` object exposes a fluent interface that easily allows you to build a strategy for your retrayble request.

```
$response = Retry::errors(422)
                ->times(10)
                ->maxDelay(64000)
                ->base(1000)
                ->jitter(JitterStrategy::CONSTANT)
                ->backoff(BackoffStrategy::EXPONENTIAL)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

MethodDescription`errors()`The status codes or exception messages to retry on`times()`The maxumum number of times your request should be retried.`sleep()`The maximum duration for a single delay`base()`The base constant to be used for backoff and jitter strategies. Defaults to 1000ms if not specified`backoff()`The strategy object that defines which backoff method you want to use`jitter()`The strategy object that defines how to add jitter to the backoff strategy`retry()`This method should be passed a callable that contains the logic of your request### Retry on Specific Errors

[](#retry-on-specific-errors)

To specify errors that are retryable, use the `errors()` method. If this method is not specified the default errors are '422' and '5\*\*' status codes.

#### Using a Status Code

[](#using-a-status-code)

```
$response = Retry::errors(422)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

#### Using an Exception Message

[](#using-an-exception-message)

```
$response = Retry::errors('Something exception message we should retry on.')
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

#### 5\*\* Status Codes

[](#5-status-codes)

Since 500 errors usually need to be retried, you can use the string '5\*\*' to match all 500 level error codes.

```
$response = Retry::errors('5**')
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

#### Multiple Errors

[](#multiple-errors)

To retry on multiple errors, simply pass an array of status codes and messages to the `errors()` method.

```
$response = Retry::errors([422,'5**', 'Something exception message we should retry on.'])
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

### Backoff

[](#backoff)

The `backoff()` method accepts one of three strings to define your backoff strategy.

#### Constant Strategy

[](#constant-strategy)

```
$response = Retry::backoff(BackoffStrategy::CONSTANT)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

#### Linear Backoff Strategy

[](#linear-backoff-strategy)

```
$response = Retry::backoff(BackoffStrategy::LINEAR)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

#### Exponential Backoff Strategy

[](#exponential-backoff-strategy)

```
$response = Retry::backoff(BackoffStrategy::EXPONENTIAL)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

### Limit the Number of Retry Attempts

[](#limit-the-number-of-retry-attempts)

To avoid retrying a down service indefinitely, you can set a max number of attempts with the `times()` method.

```
$response = Retry::times(10)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

### Limit the Delay

[](#limit-the-delay)

To prevent your strategy from increaseing delays infinitely, you can limit the maximum delay with the `maxDelay()` method. Once this limit is reached each subsequent delay will not exceed the max delay defined.

**Note that the units are in milliseconds. (E.g. 6000ms == 60s).**

```
$response = Retry::maxDelay(6000)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

> #### Sharp Knives Warning.
>
> [](#sharp-knives-warning)
>
> If you do not specify a max sleep time, the backoff delay will continue to increment infinitely. Similarly, if you do not specify a maximum number of tries, the request will continue to retry indefninitey.

### Base

[](#base)

You can set a new base constant for the strategy with the `base()` method. The following example sets a base of 2 on the linearBackoff strategy. In this case the first attempt delays 2 seconds, the second 4 seconds, the third 6 seconds and so on.

```
$response = Retry::base(2000)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

### Jitter

[](#jitter)

Generally, you'll want to apply jitter to your backoff strategy to avoid competing with other clients using the same retry strategy. The `jitter()` method accepts one of three strings to define your jitter strategy.

#### Constant Jitter Strategy

[](#constant-jitter-strategy)

```
$response = Retry::jitter(JitterStrategy::CONSTANT)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

#### Equal Jitter Strategy

[](#equal-jitter-strategy)

```
$response = Retry::jitter(JitterStrategy::EQUAL)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

#### Full Jitter Strategy

[](#full-jitter-strategy)

```
$response = Retry::jitter(JitterStrategy::FULL)
                ->retry(function () {
                    $this->client->request('GET', $url, $params);
                });
```

The following example creates a retryable request using the an exponential backoff with constant jitter, a base of 1s, a max delay of 64s and retries a maximum of 10 times.

```
$response = Retry::backoff(JitterStrategy::EXPONENTIAL)
                ->jitter(JitterStrategy::CONSTANT)
                ->base(1000)
                ->times(10)
                ->maxRetries(64000)
                ->retry(function () {
                    return $this->client->request('GET', $url, $params);
                });
```

### Callable Strategies

[](#callable-strategies)

In addition to the fluent strategy builder, you can pass a callable to the `usingStrategy()` method. This can be a callback or an object with an `__invoke()` method. The exception object is passed as the first argument and a retryable object is passed as the second argument.

> The retryable object is a simple object that keeps track of the number of times your retry strategy has been called. You can get the retry attempts using the `getRetryAttempts()` method. Your callabel should always return a boolean.

```
$response = Retry::errors([422, '5**'])
        ->usingStrategy(function ($exception, $retryable) {
            if ($exception->getCode() == 422) {
                sleep(2);
            }
            if ($exception->getCode() >= 500) {
                sleep(4);
            }
            if ($retryable->getRetryAttempts > 5) {
                sleep(6);
            }
        })->retry(function () {
            return $this->client->request('GET', $url, $params);
        });
```

### Callable Deciders

[](#callable-deciders)

If the logic needed to determine if a failed request should be retried is more complicated than matching a status code or error message, you can pass a callable to the `usingDecider()` method on the `Retry` object. The exception object is passed as the first argument and a retryable object is passed as the second argument.

```
$response = Retry::usingDecider(function ($exception, $retryable) {
    if ($exception->getCode() == 422) {
        return true;
    }
    if ($retryable->getRetryAttempts() > 5) {
        return false;
    }
    return false;
})->backoff(BackoffStrategy::CONSTANT)
->retry(function () {
    return $this->client->request('GET', $url, $params);
})
```

Credits
-------

[](#credits)

Thanks to [Caleb Porzio](http://twitter.com/calebporzio) for advice, contributions and for convincing me to build this in the first place.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity17

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~448 days

Total

5

Last Release

1160d ago

Major Versions

v0.1.0 → v1.0.02021-02-04

### Community

Maintainers

![](https://www.gravatar.com/avatar/4dec43df495de42fd9731d6868e68e34bdaedd3bf0442b134bac1f51abb66b5e?d=identicon)[christrombley](/maintainers/christrombley)

---

Top Contributors

[![ctroms](https://avatars.githubusercontent.com/u/3716415?v=4)](https://github.com/ctroms "ctroms (15 commits)")

### Embed Badge

![Health badge](/badges/ctroms-retryable/health.svg)

```
[![Health](https://phpackages.com/badges/ctroms-retryable/health.svg)](https://phpackages.com/packages/ctroms-retryable)
```

###  Alternatives

[binaryk/laravel-restify

Laravel REST API helpers

651399.1k](/packages/binaryk-laravel-restify)[namu/wirechat

A Laravel Livewire messaging app for teams with private chats and group conversations.

54324.5k](/packages/namu-wirechat)[lomkit/laravel-rest-api

A package to build quick and robust rest api for the Laravel framework.

59152.2k](/packages/lomkit-laravel-rest-api)[wirechat/wirechat

A Laravel Livewire messaging app for teams with private chats and group conversations.

5434.7k](/packages/wirechat-wirechat)[api-platform/laravel

API Platform support for Laravel

59126.4k6](/packages/api-platform-laravel)[georgeboot/laravel-echo-api-gateway

Use Laravel Echo with API Gateway Websockets

10435.5k](/packages/georgeboot-laravel-echo-api-gateway)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
