ScreenshotNeo

BlogHow-to

How to Capture a Website Screenshot with Screenshotlayer in WordPress

Use Screenshotlayer’s API from WordPress to capture a website screenshot, configure the output, and handle credentials and errors safely.

By the ScreenshotNeo team4 October 20269 min read

To capture a website screenshot with Screenshotlayer in WordPress, make a server-side HTTP request to its screenshot endpoint with your access key and the full target URL. The official material reviewed describes an API integration; it does not establish a dedicated WordPress plugin. The examples below therefore use WordPress’s HTTP API and keep the access key out of public page markup.

What you need before you start

  • A Screenshotlayer account and an access key from its account dashboard.
  • The complete URL to capture, including https:// or http://.
  • A WordPress site whose server can make outbound HTTPS requests.

An access key authenticates API requests, so treat it as a secret. Do not put it in a shortcode attribute, JavaScript, a public page, or a URL that visitors can inspect. The service’s API specification documents a capture endpoint and request parameters; check the current provider documentation and account plan because the specification repository is archived and service terms can change.

Capture a screenshot from WordPress

This minimal example uses WordPress’s built-in HTTP API. Add it to a small site-specific plugin or a child theme’s functions.php. A site-specific plugin is easier to preserve when changing themes. Replace the example target URL and store the key in wp-config.php or another server-side secret store.

// In wp-config.php, before the “stop editing” line:
define( 'SCREENSHOTLAYER_ACCESS_KEY', 'YOUR_ACCESS_KEY' );
// In a site-specific plugin or child theme functions.php:
function mysite_screenshotlayer_capture( $target_url ) {
    if ( ! defined( 'SCREENSHOTLAYER_ACCESS_KEY' ) || ! SCREENSHOTLAYER_ACCESS_KEY ) {
        return new WP_Error( 'missing_screenshotlayer_key', 'Screenshotlayer access key is not configured.' );
    }

    $target_url = esc_url_raw( $target_url );
    if ( ! $target_url || ! wp_http_validate_url( $target_url ) ) {
        return new WP_Error( 'invalid_capture_url', 'Provide a valid HTTP or HTTPS URL.' );
    }

    $endpoint = 'https://api.screenshotlayer.com/api/capture';
    $response = wp_remote_get( add_query_arg( array(
        'access_key' => SCREENSHOTLAYER_ACCESS_KEY,
        'url'        => $target_url,
        'format'     => 'PNG',
    ), $endpoint ), array(
        'timeout'     => 45,
        'redirection' => 3,
    ) );

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $status = wp_remote_retrieve_response_code( $response );
    $body   = wp_remote_retrieve_body( $response );
    $type   = wp_remote_retrieve_header( $response, 'content-type' );

    if ( 200 !== $status ) {
        return new WP_Error( 'screenshotlayer_http_error', 'Screenshotlayer returned HTTP ' . (int) $status );
    }
    if ( false === strpos( strtolower( (string) $type ), 'image/' ) || '' === $body ) {
        return new WP_Error( 'screenshotlayer_unexpected_response', 'The response was not a non-empty image.' );
    }

    return $body;
}

The endpoint and parameter names above follow the provider’s API specification. Confirm the endpoint and currently supported formats in the live documentation for your account before deploying. For a quick local check, call the function from a controlled admin-only action and save or inspect the returned bytes; do not expose the key in a front-end request. A production implementation should also cap image size, restrict who can request captures, and decide where generated files belong.

Save the returned image in the WordPress media library

If you want a persistent media attachment rather than bytes in memory, use WordPress’s upload helpers. This example assumes the capture function above succeeded:

$image_bytes = mysite_screenshotlayer_capture( 'https://example.com/' );
if ( is_wp_error( $image_bytes ) ) {
    // Log or surface a safe error to an administrator.
    return;
}

require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';

$temp_file = wp_tempnam( 'screenshotlayer.png' );
if ( ! $temp_file || false === file_put_contents( $temp_file, $image_bytes ) ) {
    return new WP_Error( 'screenshot_save_failed', 'Could not write the temporary screenshot file.' );
}

$file_array = array(
    'name'     => 'website-screenshot-' . gmdate( 'Ymd-His' ) . '.png',
    'tmp_name' => $temp_file,
);
$attachment_id = media_handle_sideload( $file_array, 0, 'Website screenshot' );
if ( is_wp_error( $attachment_id ) ) {
    @unlink( $temp_file );
    return $attachment_id;
}

$image_url = wp_get_attachment_url( $attachment_id );

Do not assume the API response is an image solely because the request completed: authentication or parameter errors may return an error payload. The earlier function checks the HTTP status, content type, and non-empty body; for stricter handling, verify the image signature and maximum dimensions before storing it.

Configure the capture

Screenshotlayer documents these useful request settings. Exact accepted values and plan availability should be checked against the current documentation and account.

Parameter Purpose When to use it
fullpage=1 Capture the full page height rather than only the viewport. Long articles or landing pages where below-the-fold content matters.
viewport Set capture viewport dimensions. Reproduce a particular browser window size.
width Set output/thumbnail width. Generate consistent previews for cards or listings.
format Choose an output image format. Pick a format supported by your plan and downstream use.
delay Wait before taking the screenshot. Give client-side rendering or animations time to settle.
ttl Set the cache lifetime. Balance freshness against repeat capture requests.
Export options Control how output is exported. Use when the returned result should be delivered or retained through a documented export path.

For example, add options to the argument array passed to add_query_arg():

$options = array(
    'access_key' => SCREENSHOTLAYER_ACCESS_KEY,
    'url'        => 'https://example.com/article',
    'fullpage'   => 1,
    'viewport'   => '1280x900',
    'width'      => 900,
    'format'     => 'PNG',
    'delay'      => 2,
    'ttl'        => 3600,
);
$request_url = add_query_arg( $options, 'https://api.screenshotlayer.com/api/capture' );

Use a full target URL with a protocol, encode query parameters through WordPress helpers rather than concatenating raw user input, and avoid unnecessarily long delays. The provider FAQ says its default cache duration is 2,592,000 seconds (30 days); ttl can request a shorter custom duration. Treat this as a provider-documented default and verify current behavior.

Call the API directly for diagnosis

Before debugging WordPress code, isolate the API request. These examples are for diagnosis and should not be placed in public source code: the access key appears in the request URL/query string.

cURL

curl -G 'https://api.screenshotlayer.com/api/capture' \
  --data-urlencode 'access_key=YOUR_ACCESS_KEY' \
  --data-urlencode 'url=https://example.com/' \
  --data-urlencode 'format=PNG' \
  -o screenshot.png

Python

import requests

response = requests.get(
    'https://api.screenshotlayer.com/api/capture',
    params={
        'access_key': 'YOUR_ACCESS_KEY',
        'url': 'https://example.com/',
        'format': 'PNG',
    },
    timeout=45,
)
response.raise_for_status()
if not response.headers.get('content-type', '').lower().startswith('image/'):
    raise RuntimeError(f"Expected image, got {response.headers.get('content-type')}: {response.text[:500]}")
with open('screenshot.png', 'wb') as output:
    output.write(response.content)

Node.js

const params = new URLSearchParams({
  access_key: process.env.SCREENSHOTLAYER_ACCESS_KEY,
  url: 'https://example.com/',
  format: 'PNG',
});
const response = await fetch(`https://api.screenshotlayer.com/api/capture?${params}`, {
  signal: AbortSignal.timeout(45000),
});
if (!response.ok) throw new Error(`Screenshotlayer HTTP ${response.status}`);
const contentType = response.headers.get('content-type') || '';
if (!contentType.toLowerCase().startsWith('image/')) {
  throw new Error(`Expected image, got ${contentType}: ${(await response.text()).slice(0, 500)}`);
}
const bytes = new Uint8Array(await response.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('screenshot.png', bytes));

WordPress integration choices

Run captures on demand

For an admin tool or a user-triggered capture, require an authenticated WordPress capability and a nonce, validate the requested target URL, and impose rate limits. Never accept an arbitrary URL from unauthenticated visitors: server-side fetching can be abused to make requests from your server to internal services. If the target domain is known, allowlist it.

Run captures in the background

Screenshot requests can take time. Avoid holding a normal page render open while a capture runs. Queue work through a background job or scheduled task, store a job state, and show the result when ready. Retry transient network failures with a small bounded retry policy and backoff; do not retry invalid credentials or malformed URLs.

Display or retain the result

You can save returned image bytes into the media library as shown above, or use a documented provider export option if it fits your workflow. Saving locally gives WordPress control over retention and display, but consumes hosting storage and requires cleanup. A remote result can reduce local storage needs, but ensure its URL lifecycle and access behavior meet your needs.

Security, performance, reliability, and cost

  • Protect credentials: use server-side configuration, restrict access to the capture action, and redact request URLs from logs because credentials may be sent as query parameters.
  • Guard against server-side request abuse: validate and, where possible, allowlist target domains. Reject local hostnames, private IP ranges, and unexpected protocols.
  • Control latency: choose a reasonable timeout, avoid large full-page images unless needed, and use cache lifetime intentionally. A long delay adds directly to capture time.
  • Bound resource use: cap response size and queued jobs; store only the formats and dimensions your page needs; periodically remove obsolete media attachments.
  • Handle transient failures: distinguish transport errors, HTTP errors, and successful image responses. Retry only temporary failures and avoid duplicate concurrent captures for the same URL.
  • Budget by current plan: request allowances, price, and feature availability are service terms that can change. Check Screenshotlayer’s current pricing and dashboard before building a workflow around a quota.

The provider FAQ describes uptime as “around 99.9%” but says it does not offer public statistics. Treat that as the provider’s claim rather than independent reliability evidence; production workflows should still handle timeouts and failed requests.

Troubleshooting

Symptom Likely cause What to do
Authentication error Missing, misspelled, inactive, or improperly configured access key. Confirm the key in the account dashboard and keep it server-side. Check that it is not empty or truncated.
Invalid URL response The target lacks http:// or https://, or contains malformed characters. Pass a complete URL and let add_query_arg() encode parameters.
WordPress returns WP_Error DNS, TLS, firewall, hosting outbound-request policy, or timeout issue. Inspect the error message in server logs, confirm outbound HTTPS is allowed, and test the endpoint from the host environment.
HTTP response is not an image The API returned an error payload or an account/option issue. Check status and content type before saving; inspect a redacted response body and consult current API error documentation.
Capture is cut off Viewport-only capture was used, or the target content had not rendered. Request fullpage=1 when appropriate and use a modest delay for dynamic rendering.
Screenshot looks stale A cached capture is being returned. Use a shorter documented ttl or the provider’s current cache controls, and verify the new request is accepted.
HTTPS request is unavailable HTTPS API eligibility may depend on account plan. Check the current plan and provider documentation; the reviewed specification says paid customers can use HTTPS.
Slow or failed WordPress page load The capture is running synchronously during page rendering. Move work to a background queue and display a pending state rather than blocking the visitor request.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It can remove cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. AI agents can take screenshots through its MCP server. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Sign up free for 1,000 screenshots a month with no card.

FAQ

Does Screenshotlayer have a WordPress plugin?

The official pages reviewed for this guide do not establish a dedicated WordPress plugin. Use a custom server-side integration unless you verify a current official plugin independently.

Yes. Save the image to the WordPress media library, then assign its attachment ID as the post thumbnail through WordPress’s post APIs. Ensure the capture is permitted for the target site and that your retention policy is clear.

How long does Screenshotlayer cache a screenshot by default?

The provider FAQ states 2,592,000 seconds, or 30 days, and says ttl can request a shorter period. Verify the current behavior in the provider documentation.

Is the access key safe in a browser request?

No. A browser request exposes the key to visitors. Make the request from WordPress server-side code and protect the key as a credential.