ScreenshotNeo

BlogHow-to

Screenshotlayer API Integration in Laravel for Indian Web Developers

Call Screenshotlayer securely from Laravel, handle image and API errors, test with HTTP fakes, and review plan and billing details before choosing a tier.

By the ScreenshotNeo team4 October 202610 min read

Use Laravel’s HTTP client to send a server-side GET request to Screenshotlayer’s capture endpoint. Pass the access key and a complete target URL, including https://, as query parameters; keep the key in server-side configuration. Add a timeout, handle both transport and provider errors, and test the request with Laravel HTTP fakes.

This guide uses Laravel’s Http facade. Screenshotlayer’s documented endpoint is http://api.screenshotlayer.com/api/capture; its paid plans list HTTPS availability, so confirm your plan’s current entitlement before using the HTTPS endpoint shown in the code. The API requires an access_key and target url. See the [Screenshotlayer API specification](https://github.com/apilayer/screenshotlayer-API/blob/master/docs/specifications.md) and [Laravel HTTP client documentation](https://laravel.com/framework/docs/13.x/http-client).

1. Store the API key safely

Do not put the Screenshotlayer key in a Blade template, browser JavaScript, a public repository, or a URL that you log. Screenshotlayer’s terms make users responsible for keeping credentials secret. Use an environment variable and read it through Laravel configuration.

Add a service entry to config/services.php:

'screenshotlayer' => [
    'key' => env('SCREENSHOTLAYER_ACCESS_KEY'),
    // Use HTTPS only if your current plan supports it.
    'endpoint' => env(
        'SCREENSHOTLAYER_ENDPOINT',
        'https://api.screenshotlayer.com/api/capture'
    ),
],

Set the secret in your deployment environment or local .env file:

SCREENSHOTLAYER_ACCESS_KEY=your_real_access_key
SCREENSHOTLAYER_ENDPOINT=https://api.screenshotlayer.com/api/capture

If your plan does not include HTTPS API access, verify the current terms and endpoint options with Screenshotlayer before configuring the request. Avoid silently downgrading a production integration to an unencrypted connection.

2. Make a Laravel request

This service sends the required parameters and optional capture settings as a query array. Laravel handles query-string encoding, so do not concatenate the URL yourself. The example asks for a PNG and full-page capture; confirm the exact option spelling and availability in Screenshotlayer’s current specification and your plan before relying on optional parameters.

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use RuntimeException;

class ScreenshotlayerClient
{
    public function capture(string $targetUrl): string
    {
        if (! filter_var($targetUrl, FILTER_VALIDATE_URL)) {
            throw new RuntimeException('The target URL is invalid.');
        }

        if (! in_array(parse_url($targetUrl, PHP_URL_SCHEME), ['http', 'https'], true)) {
            throw new RuntimeException('The target URL must use HTTP or HTTPS.');
        }

        $key = config('services.screenshotlayer.key');
        $endpoint = config('services.screenshotlayer.endpoint');

        if (! is_string($key) || $key === '' || ! is_string($endpoint) || $endpoint === '') {
            throw new RuntimeException('Screenshotlayer is not configured.');
        }

        $response = Http::timeout(30)
            ->accept('image/png')
            ->get($endpoint, [
                'access_key' => $key,
                'url' => $targetUrl,
                'fullpage' => 1,
                'format' => 'png',
            ]);

        if ($response->failed()) {
            // Do not include the request URL: it contains the access_key.
            throw new RuntimeException(
                'Screenshotlayer returned HTTP status '.$response->status().'.'
            );
        }

        $contentType = strtolower((string) $response->header('Content-Type'));
        if (! str_contains($contentType, 'image/')) {
            // Providers can return an API error payload even when the HTTP
            // response itself is successful. Avoid returning it as an image.
            throw new RuntimeException('Screenshotlayer did not return an image.');
        }

        return $response->body();
    }
}

Call the service from a controller and return the bytes with an image content type:

<?php

namespace App\Http\Controllers;

use App\Services\ScreenshotlayerClient;
use Illuminate\Http\Request;
use RuntimeException;

class ScreenshotController
{
    public function store(Request $request, ScreenshotlayerClient $client)
    {
        $validated = $request->validate([
            'url' => ['required', 'url'],
        ]);

        try {
            $png = $client->capture($validated['url']);
        } catch (RuntimeException $exception) {
            report($exception);

            return response()->json([
                'message' => 'The screenshot could not be created.',
            ], 502);
        }

        return response($png, 200)
            ->header('Content-Type', 'image/png')
            ->header('Content-Disposition', 'inline; filename="screenshot.png"');
    }
}

In a real application, decide whether screenshots should be returned directly, stored in private object storage, or referenced by a short-lived application URL. Validate who may submit capture requests and which destinations are permitted. An unrestricted endpoint that fetches arbitrary URLs can create server-side request forgery risk; block internal and local destinations according to your application’s network policy.

3. Understand the request parameters

Parameter Purpose Guidance
access_key Authenticates your account. Read from server-side configuration. Never send it to a browser or include it in logs.
url Identifies the page to capture. Send a complete URL with an http:// or https:// scheme.
fullpage Requests a full-page capture in this example. Check the current API specification and plan for accepted values and availability.
format Requests an output format in this example. PNG is the default according to the specification; JPEG and GIF are listed API formats. WebP is listed on the provider homepage for paid tiers. Verify exact options and plan entitlement.

Laravel’s GET method accepts query parameters as an associative array. Use this for all provider options rather than building a query string manually. Only add options you have verified against the current specification; plan features and provider behavior can change.

4. Handle provider errors and retries

Screenshotlayer documents errors for missing or invalid keys, invalid URLs, and reached usage limits. Laravel’s failed() check catches unsuccessful HTTP status codes, but an API may also encode an error in a successful HTTP response. Check that the response is an image before treating its body as image bytes. If your integration needs more specific messages, parse and validate the documented error payload format rather than displaying raw provider output.

  • Invalid or missing key: Check the server secret, configuration cache, and account key. Do not retry unchanged credentials.
  • Invalid URL: Require a complete HTTP or HTTPS URL, validate input, and make sure the submitted target is reachable by the provider.
  • Usage limit: Review account usage and quota. Repeating the same request will not restore quota.
  • Timeout or connection error: Catch Laravel’s connection exception at the application boundary and return a controlled error. A longer timeout may help slow captures, but also occupies a worker for longer.
  • Unexpected non-image body: Treat it as an error and inspect a safe, redacted diagnostic. Never log the request URL because it contains the key.

Laravel supports retries, but use them selectively. Retrying a transient connection failure may be reasonable if the capture operation is safe to repeat. Do not blindly retry authentication errors, malformed URLs, or quota errors. Repeated capture attempts can also consume provider usage when the request is accepted, so verify how the provider accounts for retries.

5. Test without calling the live API

Laravel’s HTTP fake lets tests verify the endpoint and query parameters without needing a Screenshotlayer account or making a live request. This example assumes the service class above.

<?php

namespace Tests\Feature;

use App\Services\ScreenshotlayerClient;
use Illuminate\Support\Facades\Http;
use RuntimeException;
use Tests\TestCase;

class ScreenshotlayerClientTest extends TestCase
{
    public function test_it_requests_a_png_for_the_target_url(): void
    {
        config([
            'services.screenshotlayer.key' => 'test-key',
            'services.screenshotlayer.endpoint' => 'https://api.screenshotlayer.com/api/capture',
        ]);

        Http::fake([
            'api.screenshotlayer.com/*' => Http::response(
                'fake-png-bytes',
                200,
                ['Content-Type' => 'image/png']
            ),
        ]);

        $png = app(ScreenshotlayerClient::class)->capture('https://example.com/page?a=1&b=two');

        $this->assertSame('fake-png-bytes', $png);

        Http::assertSent(function ($request) {
            return $request->method() === 'GET'
                && $request['access_key'] === 'test-key'
                && $request['url'] === 'https://example.com/page?a=1&b=two'
                && (string) $request['format'] === 'png';
        });
    }

    public function test_it_rejects_a_provider_error_response(): void
    {
        config([
            'services.screenshotlayer.key' => 'test-key',
            'services.screenshotlayer.endpoint' => 'https://api.screenshotlayer.com/api/capture',
        ]);

        Http::fake([
            'api.screenshotlayer.com/*' => Http::response(
                '{"error":"invalid key"}',
                200,
                ['Content-Type' => 'application/json']
            ),
        ]);

        $this->expectException(RuntimeException::class);
        app(ScreenshotlayerClient::class)->capture('https://example.com');
    }
}

Add tests for an HTTP error status, invalid target URL, missing configuration, and a transport timeout. Laravel documents connection exceptions and HTTP fakes in its [HTTP client guide](https://laravel.com/framework/docs/13.x/http-client). Keep tests deterministic by faking the provider call instead of relying on external network access.

6. cURL, Python, and Node.js equivalents

These examples show the same server-side request shape for integration comparisons. Keep credentials in environment-backed server configuration in each runtime; do not paste real keys into committed source.

cURL

curl -G 'https://api.screenshotlayer.com/api/capture' \
  --data-urlencode 'access_key=YOUR_API_KEY' \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'fullpage=1' \
  --data-urlencode 'format=png' \
  --output screenshot.png

Python

import os
import requests

response = requests.get(
    'https://api.screenshotlayer.com/api/capture',
    params={
        'access_key': os.environ['SCREENSHOTLAYER_ACCESS_KEY'],
        'url': 'https://example.com',
        'fullpage': 1,
        'format': 'png',
    },
    timeout=30,
)
response.raise_for_status()
if not response.headers.get('Content-Type', '').lower().startswith('image/'):
    raise RuntimeError('Screenshotlayer did not return an image')
with open('screenshot.png', 'wb') as image_file:
    image_file.write(response.content)

Node.js

const query = new URLSearchParams({
  access_key: process.env.SCREENSHOTLAYER_ACCESS_KEY,
  url: 'https://example.com',
  fullpage: '1',
  format: 'png',
});

const response = await fetch(
  `https://api.screenshotlayer.com/api/capture?${query}`,
  { signal: AbortSignal.timeout(30_000) },
);

if (!response.ok) {
  throw new Error(`Screenshotlayer returned HTTP ${response.status}`);
}
if (!(response.headers.get('content-type') || '').toLowerCase().startsWith('image/')) {
  throw new Error('Screenshotlayer did not return an image');
}
const image = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('screenshot.png', image),
);

7. Troubleshooting

Symptom Likely cause Fix
Authentication error Missing, mistyped, or inactive key; stale cached Laravel configuration. Check the environment secret and provider account. Refresh Laravel’s configuration cache as part of deployment. Never expose the key while debugging.
Invalid URL response The target lacks a scheme, is malformed, or cannot be fetched. Submit a full URL such as https://example.com and validate it before making the request.
HTTP works locally but fails in production Environment configuration, outbound network policy, TLS, or plan endpoint access differs. Check deployment secrets and outbound connectivity; confirm HTTPS availability for the account plan.
Image viewer says the file is invalid The response body may be an API error document rather than an image. Check status and content type before returning or saving bytes. Inspect only redacted diagnostics.
Request takes too long The target is slow or the chosen timeout is too low for the capture. Choose a deliberate timeout, surface a controlled application error, and avoid tying up web workers for long periods if captures are frequent.
Usage unexpectedly reaches quota Repeated requests, retries, or application traffic may be consuming the allowance. Review provider usage and your own request volume. Avoid automatic retries for non-transient errors.
Tests contact the provider The test did not fake the matching host or endpoint. Use Http::fake() and assert the sent request with Laravel’s HTTP testing methods.

8. Performance, reliability, and cost

A screenshot call is an external network request, so total latency depends on both your application and the provider’s capture work. Set a finite timeout that fits your user-facing response budget. For work that can finish later, consider queueing it in your application and returning a job status to the caller; Laravel’s queueing is an application design choice, not a Screenshotlayer feature claim.

Record safe operational data such as duration, HTTP status, and whether a response was an image. Redact keys and avoid logging full query strings. Consider your own cache when the same target and capture options are requested repeatedly, while respecting page freshness and your product’s requirements.

Screenshotlayer’s pricing page lists USD plans captured for this research: Free at 100 monthly snapshots and marked non-commercial; Basic at $19.99/month for 10,000; Professional at $59.99/month for 30,000; and Enterprise at $149.99/month for 75,000. The page also lists possible overage fees and plan-dependent features such as HTTPS, dedicated workers, and export options. Treat these as provider terms that can change, and check the [current pricing page](https://screenshotlayer.com/pricing) before purchase. The researched material does not establish INR pricing, GST treatment, Indian tax implications, or whether a particular Indian card will be accepted. Confirm billing details with the provider.

9. Or skip the browser setup

If you want a screenshot API without maintaining browser capture infrastructure, try [ScreenshotNeo](https://screenshotneo.com). Its API accepts a URL and returns an image or PDF, and the parameter names used by other screenshot APIs also work, which can make switching easier. See the [ScreenshotNeo API documentation](https://screenshotneo.com/docs/) for the available parameters.

curl -G 'https://api.screenshotneo.com/v1/shot' \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. [Sign up for free](https://screenshotneo.com/account/sign-up/).

FAQ

Can I call Screenshotlayer directly from a browser?

Keep the key server-side. A browser request exposes credentials to visitors and browser tooling. Call the API from Laravel and return the resulting image or an application-controlled reference.

Does the free plan support commercial use or HTTPS?

The researched pricing page marks the free plan non-commercial. HTTPS is listed among paid-plan features; verify the current plan details before selecting an endpoint.

Does the API return PNG only?

No. The researched specification lists PNG as the default, plus JPEG and GIF; the homepage lists WebP in paid tiers. Check current documentation for exact parameter values and your plan’s access.

Are prices shown in Indian rupees?

The researched plan prices are in USD. The available evidence does not confirm INR pricing, tax treatment, or India-specific payment acceptance.