Skip to content

Guzzle

Web & HTTPNetworking/HTTPPHP

What it is

Guzzle is PHP's standard HTTP client, with PSR-7 message support, middleware, concurrent requests and streaming.

A Client issues requests with options for query, JSON body, headers, timeouts and retries. A handler stack provides middleware.

Installation

composer require guzzlehttp/guzzle

Getting started

The smallest useful thing you can do with it, and what each part means.

Requests with sensible defaults
<?php
$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.example.com',
    'timeout'  => 10,          // never leave this unset
    'connect_timeout' => 5,
    'headers'  => ['Accept' => 'application/json'],
]);

$response = $client->get('/books/42');
$book = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);

$response = $client->post('/books', [
    'json' => ['title' => 'Dune', 'year' => 1965],
]);

// Non-2xx throws by default; disable per request if that is inconvenient.
try {
    $client->get('/missing');
} catch (GuzzleHttp\Exception\ClientException $e) {
    $status = $e->getResponse()->getStatusCode();
}
Guzzle throws on 4xx and 5xx by default, which is the opposite of most clients. Either catch the exception or pass ['http_errors' => false] and check the status yourself.

Advanced usage

Where the library earns its place over a simpler alternative.

Concurrency and retry middleware
<?php
use GuzzleHttp\Promise\Utils;

// Run requests concurrently rather than in sequence.
$promises = [];
foreach ($ids as $id) {
    $promises[$id] = $client->getAsync("/books/$id");
}
$responses = Utils::settle($promises)->wait();   // does not throw on failure

foreach ($responses as $id => $result) {
    if ($result['state'] === 'fulfilled') { /* … */ }
}

// Retry with exponential backoff.
$stack = GuzzleHttp\HandlerStack::create();
$stack->push(GuzzleHttp\Middleware::retry(
    fn ($retries, $req, $res, $err) =>
        $retries < 3 && ($err !== null || $res?->getStatusCode() >= 500),
    fn ($retries) => 1000 * (2 ** $retries),
));
$client = new GuzzleHttp\Client(['handler' => $stack]);
settle() waits for every promise and reports each outcome, unlike unwrap() which throws on the first rejection and discards the rest.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

ConnectException: cURL error 28
A timeout. Raise it if the endpoint is genuinely slow, and add retry middleware for transient failures.
Unexpected exceptions on 404 responses
http_errors is on by default. Catch ClientException, or set 'http_errors' => false and inspect the status.

Best practices

  • Always set timeout and connect_timeout; the defaults allow a request to hang indefinitely.
  • Reuse one Client — it keeps connections alive between requests.
  • Use getAsync with settle() for concurrent calls rather than a sequential loop.
  • Use JSON_THROW_ON_ERROR when decoding, so malformed responses fail loudly.

Background

Why it exists, and what it was reacting to.

Guzzle standardised HTTP in PHP around the PSR-7 interfaces, so requests and responses are interoperable across libraries rather than being client-specific objects.