Reader Stacks

Making HTTP Requests From Laravel With the Http Facade

Laravel's Http facade is a fluent wrapper around Guzzle — the current standard way to call an external API, replacing hand-rolled cURL or file_get_contents calls for anything server-to-server.

Calling an external API from a Laravel app used to mean reaching for raw cURL functions or file_get_contents() with a manually built stream context — the Http facade (introduced in Laravel 7, built on top of Guzzle) is the current standard replacement: a fluent, testable API for the same job.

1. A basic GET request

use Illuminate\Support\Facades\Http;

$response = Http::get('https://api.example.com/users', [
    'page' => 2,
]);

$data = $response->json();       // decoded array
$status = $response->status();   // 200
$ok = $response->successful();   // true for 2xx

2. POST with a JSON body

$response = Http::post('https://api.example.com/orders', [
    'product_id' => 42,
    'quantity' => 3,
]);

Http::post() sends the second argument as a JSON body by default, with the Content-Type: application/json header set automatically — no manual json_encode() or header configuration needed for the common case.

3. Headers and authentication

$response = Http::withToken($apiToken)
    ->withHeaders(['Accept' => 'application/json'])
    ->get('https://api.example.com/account');

// Basic auth
$response = Http::withBasicAuth('username', 'password')->get(...);

4. Timeouts and retries

$response = Http::timeout(10)
    ->retry(3, 200) // retry up to 3 times, 200ms between attempts
    ->get('https://api.example.com/data');

Without an explicit timeout, a slow or hanging external API can tie up a PHP worker process for the platform's default timeout (often 30–60 seconds) — for any external call, setting an explicit, appropriately short timeout is worth doing deliberately rather than relying on the default.

5. Handling errors properly

$response = Http::get('https://api.example.com/data');

if ($response->failed()) {
    Log::error('API call failed', ['status' => $response->status(), 'body' => $response->body()]);
    // handle the failure — don't assume $response->json() is safe to use here
}

// Or, to throw an exception on a 4xx/5xx response:
$response = Http::get('https://api.example.com/data')->throw();

By default, a 4xx or 5xx response doesn't throw an exception — $response->successful()/->failed() need to be checked explicitly, or ->throw() called deliberately to convert an error response into a catchable RequestException. Code that assumes every response is successful and calls ->json() unconditionally will silently work with an empty or unexpected array on failure rather than erroring visibly.

6. Faking HTTP calls in tests

Http::fake([
    'api.example.com/*' => Http::response(['status' => 'ok'], 200),
]);

This is one of the concrete advantages over raw cURL: Http::fake() intercepts any matching outgoing request during a test and returns a canned response instead, so tests covering code that calls external APIs don't make real network requests, don't depend on a third-party service being up, and run fast and deterministically.

Topics: APIs & Integrations