ScreenshotNeo

BlogHow-to

How to Use Abstract Screenshot API in WordPress for Webpage Previews

Add webpage screenshots to WordPress with Abstract’s API, a protected server-side PHP request, caching, safe URL handling, and a shortcode.

By the ScreenshotNeo team4 October 202612 min read

Use WordPress’s server-side HTTP API to send a target URL to Abstract’s Website Screenshot API, cache the returned image, and display it with a shortcode. Keep the Abstract API key on the server, validate the target URL, and avoid capturing on every page view. Abstract documents URL and raw HTML input, image output formats, viewport and dimension options, injected CSS, and capture timing; verify current parameter names and response behavior in the official Abstract product documentation before deploying.

This guide uses the product page’s currently shown request shape: https://screenshot.abstractapi.com/v1/ with api_key and url query parameters. It assumes a successful request returns image bytes. The product page is the source for that example endpoint and those parameters; confirm the current API reference, authentication, supported options, response format, and limits before relying on them in production. The shortcode and cache below are WordPress implementation advice, not an Abstract plugin or vendor-provided WordPress integration.

1. Choose a preview workflow

For a small site or an editor-only preview, a server-rendered shortcode can make the request and cache the image response. For previews shown to all visitors, generate and store the image when content is published or updated, then render the stored media URL. The latter avoids making visitors wait for a remote capture and avoids a fresh API request for each cache miss.

Pattern Good fit Trade-off
Capture while rendering the shortcode Private editorial tools and low-volume use A cache miss adds remote API latency to that page request.
Generate on publish/update and store the image Public previews and stable published content Requires a refresh hook or editorial workflow and image storage.
Browser-side API call Not recommended for a secret API key Credentials sent to the browser can be read and reused by others.

WordPress’s HTTP API provides wp_remote_get(), response-code helpers, and related functions for server-side requests. Its guidance also recommends caching repeated remote requests because network calls can slow page rendering. See Making HTTP requests and HTTP request performance and caching.

2. Create an Abstract API key and configure WordPress

  1. Create or sign in to an Abstract account and obtain the Website Screenshot API key.
  2. Put the key in server-side configuration. For example, set an environment variable for PHP-FPM or the hosting environment named ABSTRACT_SCREENSHOT_API_KEY.
  3. Expose it to WordPress in wp-config.php before the “stop editing” line:
define( 'ABSTRACT_SCREENSHOT_API_KEY', getenv( 'ABSTRACT_SCREENSHOT_API_KEY' ) );

Make sure the environment variable is actually available to the PHP process. Do not put the key in a shortcode attribute, JavaScript, a public REST response, or HTML. The WordPress handbook cautions that HTTP Basic Authentication exposes credentials and is intended only for testing and development; use the authentication method specified by Abstract’s current API documentation. See WordPress HTTP authentication guidance.

3. Add a safe, cached shortcode

The example below is a small plugin. It accepts only public HTTP or HTTPS URLs on an explicit host allowlist. Edit $allowed_hosts to the destinations your site needs. This conservative policy helps prevent an attacker from using WordPress as a proxy to request internal services. If editors need arbitrary public destinations, replace the allowlist with a reviewed server-side URL validation policy that also blocks loopback, private, link-local, and reserved IP ranges after DNS resolution, and handles redirects safely. URL validation is a security safeguard for this implementation; it is not an Abstract-specific requirement.

Create wp-content/plugins/abstract-preview/abstract-preview.php with the following contents and activate “Abstract Preview Shortcode” in WordPress:

<?php
/**
 * Plugin Name: Abstract Preview Shortcode
 * Description: Cached Abstract website screenshots for approved preview hosts.
 * Version: 1.0.0
 */

add_shortcode( 'abstract_preview', 'ap_render_preview_shortcode' );

function ap_render_preview_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'url'   => '',
            'width' => '1200',
            'alt'   => 'Webpage preview',
        ),
        $atts,
        'abstract_preview'
    );

    $url = trim( (string) $atts['url'] );
    $parts = wp_parse_url( $url );
    $allowed_hosts = array( 'example.com', 'www.example.com' ); // Replace with your approved hosts.

    if ( ! is_array( $parts ) || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) {
        return '<p>Preview unavailable: provide a valid public URL.</p>';
    }

    $scheme = strtolower( $parts['scheme'] );
    $host = strtolower( rtrim( $parts['host'], '.' ) );
    if ( ! in_array( $scheme, array( 'http', 'https' ), true ) || ! in_array( $host, $allowed_hosts, true ) ) {
        return '<p>Preview unavailable: this destination is not allowed.</p>';
    }

    if ( ! defined( 'ABSTRACT_SCREENSHOT_API_KEY' ) || ! ABSTRACT_SCREENSHOT_API_KEY ) {
        return '<p>Preview unavailable: screenshot service is not configured.</p>';
    }

    $width = absint( $atts['width'] );
    if ( $width < 320 || $width > 2400 ) {
        $width = 1200;
    }

    // Include all capture settings in the cache key so variants do not collide.
    $cache_key = 'ap_preview_' . hash( 'sha256', $url . '|width=' . $width );
    $cached = get_transient( $cache_key );
    if ( is_array( $cached ) && ! empty( $cached['mime'] ) && ! empty( $cached['data'] ) ) {
        return ap_preview_img_html( $cached['mime'], $cached['data'], $atts['alt'], $width );
    }

    $endpoint = add_query_arg(
        array(
            'api_key' => ABSTRACT_SCREENSHOT_API_KEY,
            'url'     => $url,
        ),
        'https://screenshot.abstractapi.com/v1/'
    );

    $response = wp_remote_get( $endpoint, array(
        'timeout'             => 25,
        'redirection'         => 0,
        'limit_response_size' => 12000000,
        'headers'             => array( 'Accept' => 'image/png,image/jpeg,image/gif' ),
    ) );

    if ( is_wp_error( $response ) ) {
        return '<p>Preview temporarily unavailable. Please try again later.</p>';
    }

    $status = wp_remote_retrieve_response_code( $response );
    $mime = strtolower( (string) wp_remote_retrieve_header( $response, 'content-type' ) );
    $body = wp_remote_retrieve_body( $response );
    $allowed_mimes = array( 'image/png', 'image/jpeg', 'image/gif' );

    if ( 200 !== $status || ! in_array( $mime, $allowed_mimes, true ) || '' === $body ) {
        // Do not cache failures as successful previews. Log status without logging the key or full request URL.
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( 'Abstract preview request failed; HTTP status: ' . (int) $status );
        }
        return '<p>Preview temporarily unavailable. Please try again later.</p>';
    }

    $data = base64_encode( $body );
    $cached = array( 'mime' => $mime, 'data' => $data );
    set_transient( $cache_key, $cached, 6 * HOUR_IN_SECONDS );

    return ap_preview_img_html( $mime, $data, $atts['alt'], $width );
}

function ap_preview_img_html( $mime, $data, $alt, $width ) {
    $src = 'data:' . $mime . ';base64,' . $data;
    return sprintf(
        '<img class="abstract-page-preview" src="%1$s" alt="%2$s" width="%3$d" loading="lazy" decoding="async">',
        esc_attr( $src ),
        esc_attr( (string) $alt ),
        (int) $width
    );
}

Use it in a post or page:

[abstract_preview url="https://example.com/" width="1200" alt="Preview of the example.com homepage"]

This implementation stores the image bytes in a transient as base64. That is straightforward for a short example, but it increases database storage and HTML size. For public, frequently viewed previews, save the validated image bytes as a WordPress media attachment (or object storage file), store its attachment ID or URL, and return a normal image URL. Keep the API key and any private API response out of public metadata.

4. Use Abstract’s API options deliberately

Abstract’s product page describes configurable image formats, viewport and dimensions, injected CSS, and delayed capture timing. It also describes URL or raw HTML input. The current parameter names and exact accepted values can change, so consult its current API documentation before adding options to the request. Add each requested setting to the cache key.

Need Implementation choice Effect to consider
Mobile or tablet preview Set a viewport suited to the target layout. The returned image should represent the intended display and responsive breakpoint.
Specific image dimensions Request dimensions supported by the current API. Large captures consume more transfer and storage; avoid dimensions far beyond the rendered display size.
PNG, JPEG, or GIF Choose a format accepted by the API and your output use. Confirm the actual response Content-Type; the sample code accepts only PNG, JPEG, and GIF.
Hide page elements or adjust appearance Use documented injected CSS. Keep CSS controlled by trusted administrators; do not pass arbitrary visitor CSS into remote requests.
Wait for content to render Use documented delay or capture-timing controls. Longer waits increase capture latency. Use the shortest wait that reliably includes required content.
HTML instead of a public URL Use the API’s documented raw HTML input path. HTML may contain private data or scripts; treat it as untrusted input and verify request encoding and size limits.

The reviewed Abstract product page says captures from different IP-geographic locations are not supported at the time of review. Do not design a preview feature that depends on choosing a capture region without verifying that capability has changed.

5. cURL, Python, and Node.js request examples

These examples mirror the currently displayed endpoint and query parameters. They assume image bytes are returned on success. Check status, content type, and vendor errors in your application, and consult the current Abstract documentation before production use. Keep the API key in an environment variable rather than committing it.

cURL

export ABSTRACT_API_KEY='YOUR_API_KEY'
curl --fail --silent --show-error --get 'https://screenshot.abstractapi.com/v1/' \
  --data-urlencode "api_key=$ABSTRACT_API_KEY" \
  --data-urlencode 'url=https://example.com/' \
  --output preview.png

Python

import os
import requests

api_key = os.environ['ABSTRACT_API_KEY']
response = requests.get(
    'https://screenshot.abstractapi.com/v1/',
    params={'api_key': api_key, 'url': 'https://example.com/'},
    timeout=(5, 25),
)
response.raise_for_status()
content_type = response.headers.get('Content-Type', '').split(';', 1)[0].lower()
if content_type not in {'image/png', 'image/jpeg', 'image/gif'}:
    raise ValueError(f'Expected image bytes; received {content_type!r}')
with open('preview.png', 'wb') as image_file:
    image_file.write(response.content)

Node.js

const apiKey = process.env.ABSTRACT_API_KEY;
if (!apiKey) throw new Error('Set ABSTRACT_API_KEY');

const endpoint = new URL('https://screenshot.abstractapi.com/v1/');
endpoint.search = new URLSearchParams({
  api_key: apiKey,
  url: 'https://example.com/',
});

const response = await fetch(endpoint, { signal: AbortSignal.timeout(25_000) });
if (!response.ok) throw new Error(`Screenshot request failed: HTTP ${response.status}`);
const contentType = (response.headers.get('content-type') || '').split(';', 1)[0].toLowerCase();
if (!['image/png', 'image/jpeg', 'image/gif'].includes(contentType)) {
  throw new Error(`Expected image bytes; received ${contentType || 'no content type'}`);
}
const bytes = Buffer.from(await response.arrayBuffer());
const fs = await import('node:fs/promises');
await fs.writeFile('preview.png', bytes);

6. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its one-call API returns a screenshot image or PDF, and the same request can be extended with its documented options. See the ScreenshotNeo API documentation.

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

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for ScreenshotNeo and start with 1,000 free screenshots a month, no card required.

7. Security, caching, and operational details

Protect the WordPress server and API key

  • Keep the API key in server-side configuration and rotate it if it appears in logs, source control, HTML, or browser network tools.
  • Do not create an unauthenticated public endpoint that accepts arbitrary URLs and proxies them through WordPress. If visitors can request previews, require appropriate authorization, rate-limit requests, and enforce destination restrictions.
  • Allow only HTTP and HTTPS. Reject unexpected schemes, credentials in URLs, localhost names, and private or reserved network destinations. Re-check redirects and DNS resolution in a robust implementation.
  • Use WordPress escaping when rendering attributes. The example validates the response MIME type before embedding it, but a media-attachment workflow is generally more suitable for public pages.
  • Do not log the full API request URL: the sample key is sent as a query parameter in the vendor’s displayed example and could otherwise leak into logs.

Cache and refresh

Cache on a normalized URL plus all visual settings: viewport, output format, injected CSS version, and capture delay. The shortcode example caches for six hours; choose a lifetime that matches how often the target pages change. Refresh previews on a schedule or when your content changes. For popular pages, generate in a background job and serve the last known good image while a refresh runs. Avoid retrying every visitor request after a failure; use backoff and a short negative-cache window for transient errors.

Latency and throughput

A live capture depends on DNS, network access, target-page rendering, and the screenshot service response. A WordPress request waits until the HTTP call finishes or times out. Set a bounded timeout, show a graceful fallback, and move generation off the visitor request path when latency matters. Abstract’s product page describes caching improvements in its changelog, but application-level caching still prevents repeated requests for an unchanged preview.

Cost and reliability

Review Abstract’s current plan selector, quota period, request rate, and overage rules before estimating cost. The pricing view reviewed for this article displayed a free option with 100 requests and a one-request-per-second rate, while paid figures differed between monthly and annual billing views; those figures are time-sensitive and should not be used as a current quote without checking the live page. Cache hits in WordPress reduce duplicate API requests. Treat timeouts, non-200 responses, rate limits, malformed responses, and target sites that block automation as expected failure cases, and retain a last known good preview where practical.

8. Troubleshooting

Symptom Likely cause Fix
“Service is not configured” fallback The PHP process cannot read the environment variable, or the constant is missing. Configure the variable in the PHP hosting environment, confirm wp-config.php defines the constant, and never print the key to debug it.
HTTP 401 or 403 from Abstract Invalid key, wrong account/API access, or changed authentication requirements. Check the key and current vendor docs; ensure it is sent using the currently documented method.
HTTP 400 Malformed URL, unsupported parameter, or invalid capture option. Check URL encoding and remove optional parameters until a minimal documented request succeeds.
HTTP 429 Rate limit or account quota reached. Reduce capture frequency, cache results, queue refreshes, and check the current account limits.
Timeout or WordPress WP_Error The target page or remote API took longer than the configured timeout, or outbound network access failed. Check hosting outbound HTTPS access and target availability; use a bounded longer timeout only when justified, and serve a cached image during refresh.
Response is JSON or HTML instead of an image The API returned an error document, or its response behavior changed. Inspect status and content type in protected server logs, never embed the body as an image, and confirm the current response contract.
Preview is blank or incomplete The target renders content after the capture, requires interaction, or blocks automated browsers. Use documented capture delay/timing options, choose the right viewport, or provide a fallback; do not assume every page can be captured.
“Destination is not allowed” The target host is absent from the sample allowlist. Add the exact trusted hostname to $allowed_hosts; avoid broad suffix matching unless the subdomain policy is intentional.
Image appears stale The transient cache has not expired or the target content changed without a refresh. Clear the relevant transient or implement a deliberate refresh path and cache versioning.
Page output becomes very large Base64 image bytes are embedded directly in HTML. Store the bytes as a media attachment or object file and render its URL instead.

9. FAQ

Is there an official Abstract WordPress plugin?

The reviewed material does not establish an official WordPress plugin or tested WordPress integration. The example here is a custom shortcode implementation using WordPress’s HTTP API.

Can I capture a page that is not publicly accessible?

Abstract says the API accepts raw HTML, and its changelog notes expanded support for password-protected website capture. That does not establish support for every login flow or access control. Check the current documentation and avoid sending private page content unless your security and data handling requirements allow it.

Can the preview show a particular geographic location?

The Abstract product page reviewed says captures from different IP-geographic locations are not supported at that time. Verify current availability if location-specific rendering is a requirement.

Should I put the screenshot in the WordPress Media Library?

For public, reusable previews, storing an attachment or durable object URL is usually easier to optimize and serve than embedding base64 bytes in each page’s HTML. The transient example is meant to make the API flow concrete.

Can I use raw HTML as the capture source?

Abstract’s product page says raw HTML is supported. Follow its current input and encoding rules, and keep user-supplied markup and scripts under strict control.