Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Symfony’s HttpClient to send a server-side request to a screenshot API, check the HTTP status, and save the successful response body as binary image or PDF data. Keep the API key in server-side configuration, not in browser code or a URL. The example below shows a Symfony integration with ScreenshotEngine’s documented POST endpoint; ScreenshotNeo is another option, using a GET request and returning a screenshot or PDF.

How the Symfony integration works

Symfony’s HttpClient component is enough to call a screenshot service: inject HttpClientInterface, send the target URL and capture options, then handle the response according to the provider’s response format. Symfony describes HttpClient as a low-level HTTP client with support for PHP stream wrappers and cURL: Symfony HttpClient documentation.

The example uses ScreenshotEngine’s documented endpoint, https://api.screenshotengine.com/v1/screenshot. That API expects a POST with Bearer authentication and a JSON body; a successful capture returns the file bytes directly, while an error returns JSON. These details are specific to ScreenshotEngine, not universal conventions for screenshot APIs. See its quickstart and authentication documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install and configure Symfony HttpClient

Install the package from your project directory:

composer require symfony/http-client

Symfony exposes the client as the http_client service and can autowire SymfonyContractsHttpClientHttpClientInterface. Store the provider key as a server-side environment variable or deployment secret. For local development, a .env.local entry can hold the value without being committed:

SCREENSHOTENGINE_API_KEY=your-secret-key

Do not put a real key in a public HTML page, client-side JavaScript, a committed repository, logs, or a query string. ScreenshotEngine explicitly cautions against those exposure paths in its authentication guidance.

Create a reusable capture service

This service requests a full-page PNG and returns the response body as bytes. It checks the status before accepting the body as an image, so an error response cannot silently be saved with a .png extension.

<?php

namespace AppService;

use SymfonyContractsHttpClientHttpClientInterface;

final class ScreenshotClient
{
    public function __construct(private HttpClientInterface $http) {}

    public function capture(string $url, string $apiKey): string
    {
        $response = $this->http->request('POST', 'https://api.screenshotengine.com/v1/screenshot', [
            'headers' => [
                'Authorization' => 'Bearer '.$apiKey,
                'Content-Type' => 'application/json',
            ],
            'json' => [
                'url' => $url,
                'format' => 'png',
                'height' => 'full',
            ],
            'timeout' => 120,
        ]);

        $status = $response->getStatusCode();
        if ($status < 200 || $status >= 300) {
            throw new RuntimeException(
                'Screenshot API failed: '.$status.' '.$response->getContent(false)
            );
        }

        return $response->getContent();
    }
}

Symfony’s json option encodes the request body and sets the JSON content type. getStatusCode() reads the HTTP status, and getContent() returns the response body; see the request options and response methods. The explicit timeout gives the remote browser time to render a page, but a long timeout may not suit a synchronous web request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Save the returned image or PDF bytes

Write a file in a worker or command

Once a successful response has been checked, PHP can write the bytes directly:

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
$bytes = $screenshotClient->capture($url, $apiKey);

if (file_put_contents($outputPath, $bytes) === false) {
    throw new RuntimeException('Could not write screenshot file.');
}

Make sure $outputPath points to a directory your application can write to, and choose an extension that matches the requested format. The same byte-handling pattern applies to a provider’s direct PDF response; request PDF output using that provider’s documented parameter names rather than assuming all APIs use format.

Return bytes from a Symfony controller

For a synchronous endpoint that should deliver the image to the caller, use a Symfony binary response and set the matching content type. This controller example receives a PNG byte string from the service:

use SymfonyComponentHttpFoundationResponse;

$bytes = $screenshotClient->capture($url, $apiKey);

return new Response($bytes, 200, [
    'Content-Type' => 'image/png',
    'Content-Disposition' => 'inline; filename="capture.png"',
]);

For a PDF response, use the provider’s PDF option and serve the returned bytes as application/pdf. Do not label JSON error content as a file. The service above throws on non-2xx status, leaving the controller or exception handler to produce an appropriate application response.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle JSON errors and URL inputs safely

ScreenshotEngine’s documented success response is binary file data, but its errors are JSON. Symfony’s getContent(false) lets the service inspect an error body without treating a non-2xx status as a successful file. If a different provider returns JSON metadata on success—for example, a file URL rather than the bytes—decode the JSON using Symfony’s toArray(), then make a separate request for the returned asset. Symfony documents both response approaches in its HttpClient response API.

Validate or allow-list target URLs before submitting user-provided values. This prevents your application from becoming an unrestricted proxy that captures arbitrary destinations. ScreenshotEngine documents its endpoint as accepting a public URL; it does not document custom target-site cookies, target-site Authorization headers, or login scripts. A service limited to public URLs cannot capture a page that depends on the user’s authenticated session. Consult ScreenshotEngine’s authentication documentation and verify any provider’s access features before building a workflow around private pages.

Choose capture options around the job

Capture controls vary by provider. Before selecting an API, compare the settings that affect what the saved file contains and how it can be retrieved:

  • Response mode: Does success return image or PDF bytes directly, or JSON metadata and a downloadable URL?
  • Authentication placement: Does the API expect a bearer token, a key parameter, or another mechanism? Keep credentials server-side regardless.
  • Page dimensions: Check whether it can capture a viewport or a full page, and which output formats it supports.
  • Rendering hooks: For dynamic pages, establish whether the service supports custom CSS or JavaScript, waits, or selector-based readiness.
  • Target access: Confirm whether it can reach only public pages or supports authenticated target sites.
  • Operations: Compare caching, batch capture, quotas, pricing, and timeout limits against your expected workload.

As concrete examples, ScreenshotEngine’s documented quickstart covers PNG and PDF output and full-page capture. Screenshot API lists PNG, JPEG, WebP and PDF, along with viewport, CSS/JavaScript, geolocation, caching and batch options: Screenshot API documentation. Do not infer that one provider supports another’s settings; confirm the exact request fields in the provider’s documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Reliability, latency, and cost in a Symfony app

A screenshot request includes remote page loading and browser rendering, so its completion time depends on both the target page and provider. Set an explicit timeout that fits your use case and handle failures as part of the normal flow. For user-facing requests, a long render can hold a PHP worker and the caller’s connection open; for slower or bulk work, enqueue a job, persist its status, and let a worker perform the capture.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Symfony supports retry configuration for transient HTTP status codes, concurrent requests, and streaming responses; see its HttpClient documentation. Apply retries selectively: retrying a persistent invalid URL, authentication failure, or unsupported option wastes time, while retrying a transient provider or network failure may help. Avoid multiplying long timeouts with automatic retries without considering the total wait a user or worker can incur.

Record enough operational detail to diagnose failures—such as the provider, status code, and provider request ID or error body when available—but redact authorization headers and secrets. Review each service’s billing behavior for failed captures, cached results, and quotas; these terms differ and can materially change the cost of repeated or automated jobs.

Or skip the browser setup

If you want a single request rather than maintaining a browser-capture integration, ScreenshotNeo is a website screenshot API and MCP server for developers. It uses a GET request and can return PNG, JPEG, WebP, or PDF. This Symfony example saves the returned WebP bytes; check the response status before writing the file. Keep the key in server-side configuration, as with any provider.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php

use SymfonyContractsHttpClientHttpClientInterface;

final class ScreenshotNeoClient
{
    public function __construct(private HttpClientInterface $http) {}

    public function capture(string $url, string $apiKey): string
    {
        $response = $this->http->request('GET', 'https://api.screenshotneo.com/v1/shot', [
            'query' => [
                'access_key' => $apiKey,
                'url' => $url,
                'format' => 'webp',
            ],
            'timeout' => 90,
        ]);

        $status = $response->getStatusCode();
        if ($status < 200 || $status >= 300) {
            throw new RuntimeException(
                'ScreenshotNeo request failed: '.$status.' '.$response->getContent(false)
            );
        }

        return $response->getContent();
    }
}

$bytes = $screenshotNeoClient->capture('https://stripe.com', $apiKey);
if (file_put_contents($outputPath, $bytes) === false) {
    throw new RuntimeException('Could not write screenshot file.');
}

See the ScreenshotNeo API documentation for parameters and response details. It removes cookie/consent banners, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents using Claude, Cursor, or another MCP client. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo’s free plan.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common integration failures

The file contains JSON instead of an image

The provider returned an error body, or the integration treated JSON metadata as binary output. Check the HTTP status before writing bytes. For a non-2xx response, inspect the error body safely; for a provider that returns success metadata, parse JSON and retrieve the asset URL it supplies.

The request is unauthorized

Verify the key is present in the Symfony runtime and that the provider’s required authentication format matches the request. The ScreenshotEngine example uses Authorization: Bearer …; ScreenshotNeo uses an access_key query parameter in its documented GET request. Never copy a key into browser-visible code.

The capture times out

Check whether the target page is slow or stalled and whether the configured timeout matches the expected render time. For long-running captures, move work to a queue rather than extending a normal web request indefinitely. Symfony’s configurable retry behavior can help with transient failures, but it cannot fix a page that consistently exceeds the allotted time.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A page behind login is blank or inaccessible

Confirm that the provider supports the authentication method the page requires. ScreenshotEngine’s documented endpoint accepts public URLs and does not expose custom target cookies, target-site authorization headers, or login scripts. Use a provider capability designed for authenticated captures, or capture only a public page.

The response saves but the image is unusable

Confirm the requested output format and the filename extension agree, and inspect the provider’s status and error response rather than assuming every body is an image. Also verify that the target URL is valid and publicly reachable by the capture service.

Frequently Asked Questions

Can I use the screenshot API call from a Symfony command or worker?

Yes. Inject the same HttpClient-backed service into a console command or message handler, then write the successful response bytes to storage.

Does ScreenshotEngine’s documented endpoint capture a logged-in user’s page?

Its documented endpoint accepts public URLs and does not expose custom target cookies, target-site Authorization headers, or login scripts; it is not documented as capturing a user’s authenticated session.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.