Back to all articles
Laravel Insights Nov 14, 2025 โˆ™ 1 min read

Mastering Laravel's Http::batch() for Concurrent Requests

How to use Laravel's Http::batch() for parallel requests, progress tracking, and error handling.

Arrows moving in parallel, symbolizing concurrent HTTP requests streamlined by Laravel's Http::batch() feature.

Mastering Laravel's Http::batch(): Simplify Concurrent HTTP Requests with Ease

How to use Laravel's Http::batch() for parallel requests, progress tracking, and error handling.

Modern applications often rely on communicating with multiple external APIs to aggregate data, sync information, or trigger workflows. Handling these HTTP requests sequentially can create significant performance bottlenecks, as each request must wait for the previous one to complete. While Laravel has long offered Http::pool for concurrent requests, managing the entire lifecycle—tracking progress, handling individual failures, and executing callbacks—required considerable boilerplate code.

With the release of Laravel 12.32, the framework introduces a powerful and elegant solution: Http::batch(). This new method, contributed by Wendell Adriel, simplifies the process of sending multiple concurrent HTTP requests by providing a clean, expressive API complete with lifecycle hooks. It empowers development teams to build faster, more resilient integrations with less code.

This guide provides a comprehensive overview of the Http::batch() method. We will explore its core features, detail practical use cases with ready-to-use code examples, and outline best practices for leveraging this feature to optimize your application's performance and reliability.

What is Http::batch()?

Http::batch() is a new addition to Laravel's fluent HTTP client that allows you to dispatch a group of requests to run concurrently while providing full control over the batch's lifecycle. It is built on the foundation of Http::pool but adds a sophisticated layer of state management and event handling, making complex asynchronous workflows easy to manage.

The key features of Http::batch() include:

  • Concurrent Execution: Sends all defined requests in parallel, drastically reducing the total time required to fetch data from multiple sources.
  • Lifecycle Hooks: Offers a rich set of callbacks (before, progress, catch, then, finally) that allow you to execute logic at every stage of the batch process.
  • Progress Tracking: Provides helper methods to monitor the status of the batch in real-time, including the total, pending, and failed requests.
  • Granular Error Handling: The catch hook allows you to handle failures for each request individually without terminating the entire batch.

Use Cases: Where Http::batch() Shines

This feature is particularly valuable in scenarios where your application needs to interact with several external endpoints simultaneously.

1. Building a Data Aggregation Dashboard

Imagine you are building a dashboard that displays metrics from various services like Google Analytics, Stripe, and a CRM. Instead of fetching this data sequentially, you can use Http::batch() to retrieve it all at once, significantly improving the dashboard's load time.

2. Syncing Data Across Multiple Services

When a user updates their profile in your application, you might need to propagate that change to several third-party services (e.g., a marketing automation platform, a support desk, and a billing system). Http::batch() can execute these API calls in parallel, ensuring the data sync is fast and efficient. You can also use the catch hook to log any failed sync attempts for later retry.

3. Batch Processing and Validation

Consider a workflow where you need to validate a list of email addresses against multiple verification APIs. Http::batch() allows you to send all validation requests concurrently and aggregate the results, making the process much faster than a sequential approach.

Practical Examples and Code Snippets

Let's dive into how to use Http::batch() with practical, ready-to-use examples.

Basic Usage: Sending Multiple Requests

The fundamental use case is to define and send a batch of requests. You can add requests to the batch and optionally assign them a key for easy identification in the callbacks.

use Illuminate\Support\Facades\Http;

$batch = Http::batch()
    ->add(
        Http::asJson()->post('https://api.service-one.com/users', ['name' => 'John Doe'])
    )
    ->add(
        'crm_update', // Assigning a key
        Http::patch('https://api.crm.com/contacts/123', ['status' => 'active'])
    )
    ->add(
        Http::get('https://api.analytics.com/stats')
    )
    ->send();

// Check if the entire batch was successful
if ($batch->successful()) {
    // All requests succeeded
}

Using Lifecycle Hooks for Full Control

The real power of Http::batch() comes from its lifecycle hooks. These allow you to manage the entire workflow with precision.

use Illuminate\Http\Client\Batch;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

$batch = Http::batch()
    ->add(Http::get('https://api.github.com/users/taylorotwell'))
    ->add('service_status', Http::get('https://api.statuspage.io/v1/status'))
    ->add('invalid_endpoint', Http::get('https://api.invalid-service.com/data'))
    
    // Runs before any requests are sent
    ->before(function (Batch $batch) {
        Log::info("Starting a batch of {$batch->totalRequests} requests.");
    })

    // Runs after each successful request
    ->progress(function (Response $response, string|null $key, Batch $batch) {
        $requestIdentifier = $key ?? $response->effectiveUri();
        Log::info("Request '{$requestIdentifier}' succeeded.", [
            'progress' => "{$batch->processedRequests()} / {$batch->totalRequests}"
        ]);
    })

    // Runs after each failed request
    ->catch(function (Response $response, string|null $key, Batch $batch) {
        $requestIdentifier = $key ?? $response->effectiveUri();
        Log::error("Request '{$requestIdentifier}' failed.", [
            'status' => $response->status()
        ]);
    })

    // Runs only if ALL requests succeed
    ->then(function (Batch $batch) {
        Log::info("All requests in the batch succeeded.");
    })

    // Runs after all requests have finished, regardless of success or failure
    ->finally(function (Batch $batch) {
        Log::info("Batch finished. Failures: {$batch->failedRequests}");
    })
    ->send();

// You can still access responses after the batch is sent
$taylor = $batch->response('https://api.github.com/users/taylorotwell');
$status = $batch->response('service_status');

if ($taylor && $taylor->ok()) {
    // Process successful response
}

Tracking Progress and Handling Failures

Http::batch() provides several helper properties and methods to inspect the state of the batch, which are especially useful within the callbacks.

  • $batch->totalRequests: The total number of requests in the batch.
  • $batch->pendingRequests: The number of requests yet to be processed.
  • $batch->failedRequests: The count of failed requests.
  • $batch->processedRequests(): The number of completed requests (both success and failure).
  • $batch->hasFailures(): Returns true if any request has failed.
  • $batch->finished(): Returns true once all requests are complete.

Strengths and Limitations

Strong Points

  • Improved Developer Experience: The fluent API and chained hooks make concurrent request logic clean, readable, and easy to maintain.
  • Enhanced Performance: Executing requests in parallel dramatically reduces latency when interacting with multiple APIs.
  • Resilient Workflows: Granular catch handling allows you to build robust systems that can gracefully manage individual API failures without halting the entire process.
  • Full Visibility: Progress tracking and lifecycle events provide complete insight into the execution of the batch, which is excellent for logging and debugging.

Potential Limitations

  • Resource Consumption: Sending a large number of concurrent requests can consume significant server resources (CPU and memory). It is important to be mindful of the batch size, especially in resource-constrained environments.
  • Rate Limiting: Be aware of the rate limits of the APIs you are calling. Sending too many requests in parallel can get your application temporarily or permanently blocked.
  • Not a Replacement for Queues: For long-running or deferrable tasks, using Laravel's queue system is still the recommended approach. Http::batch() is designed for synchronous workflows where you need to wait for the results of all requests.

Conclusion

Laravel's Http::batch() is a significant enhancement to the framework's HTTP client, providing a proven and efficient solution for managing concurrent API requests. It strikes a perfect balance between power and simplicity, offering a rich set of features within an intuitive and expressive API.

By integrating Http::batch() into your projects, your team can build faster and more resilient applications. It eliminates the need for custom boilerplate code, reduces the complexity of handling parallel requests, and provides the tools necessary to monitor and debug your integrations effectively. For any Laravel developer working with multiple external services, mastering Http::batch() is an essential step toward optimizing your development process and enhancing application performance.


Related articles

Continue exploring Laravel insights and practical delivery strategies.

Laravel consulting

Need senior Laravel help for this topic?

Let's adapt these practices to your product and deliver the next milestone.