Making an outgoing HTTP request with custom headers — calling a third-party API from a Laravel backend — is best done through Laravel's built-in Http facade, a genuinely more readable fluent wrapper around Guzzle than raw cURL or manually instantiating a Guzzle client.
A basic GET request with headers
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'Authorization' => 'Bearer '.$apiToken,
'Accept' => 'application/json',
])->get('https://api.example.com/orders');
$orders = $response->json();
A POST request with a JSON body and headers
$response = Http::withHeaders([
'Authorization' => 'Bearer '.$apiToken,
])->post('https://api.example.com/orders', [
'customer_id' => 123,
'total' => 150.00,
]);
Passing an array as the second argument to post() automatically sends it as a JSON body with the correct Content-Type: application/json header set — no manual json_encode() call or explicit content-type header needed for this common case.
Checking the response
if ($response->successful()) {
$data = $response->json();
} elseif ($response->clientError()) {
// 4xx response
} elseif ($response->serverError()) {
// 5xx response
}
These convenience methods (successful(), clientError(), serverError()) read the response's status code range — generally clearer at the call site than manually checking $response->status() >= 200 && $response->status() < 300 for the same result.
Setting a request timeout
$response = Http::timeout(10)->get('https://api.example.com/orders');
Without an explicit timeout, a request to an unresponsive external API can hang far longer than acceptable for a typical web request — setting a reasonable timeout gives a bounded, predictable failure case instead of an indefinitely hanging request.
Retrying automatically on failure
$response = Http::retry(3, 100)->get('https://api.example.com/orders');
retry(3, 100) retries up to 3 times with a 100-millisecond delay between attempts — genuinely useful for a flaky third-party API where a transient failure is common and a simple retry often succeeds on the next attempt.
Sending form-encoded data instead of JSON
$response = Http::asForm()->post('https://api.example.com/login', [
'username' => $username,
'password' => $password,
]);
asForm() switches the request body to application/x-www-form-urlencoded encoding — necessary for an API endpoint (like an OAuth token endpoint) that specifically expects traditional form-encoded data rather than a JSON body.
Faking HTTP responses in a test, without making a real request
Http::fake([
'api.example.com/*' => Http::response(['status' => 'ok'], 200),
]);
Http::fake() intercepts any matching outgoing request during a test and returns the specified fake response instead — essential for testing code that calls an external API without genuinely hitting that real API (and its rate limits, cost, or availability) on every test run.
Why Http:: is generally preferred over raw cURL or a manual Guzzle client
Laravel's Http facade wraps Guzzle under the hood but exposes a considerably more readable, fluent syntax — and critically, it integrates with Http::fake() for testing, something raw curl_exec() calls or a manually instantiated Guzzle client don't get without significantly more manual test setup work.