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/guzzleGetting started
The smallest useful thing you can do with it, and what each part means.
php
<?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();
}Advanced usage
Where the library earns its place over a simpler alternative.
php
<?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]);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.
