Blog 3 min read

Website Screenshots in WordPress with wp_remote_get (Plugin Snippet)

A small WordPress plugin that captures a screenshot of any URL through a screenshot API: a shortcode, transient caching, saving the image to the media library, and setting it as a featured image.

Link directories, curated lists, "sites we like" pages: WordPress sites keep needing a thumbnail of some other website. Most plugins for this either stopped working years ago or run a fragile headless browser on shared hosting. The version below is a few dozen lines, uses only WordPress core functions, and lets an API do the rendering.

The plugin

Create wp-content/plugins/site-screenshots/site-screenshots.php:

<?php
/**
 * Plugin Name: Site Screenshots
 * Description: [site_screenshot url="https://example.com"] renders a live thumbnail of any website.
 */

if (! defined('ABSPATH')) {
    exit;
}

function ssr_capture_url(string $url, string $format = 'jpeg'): ?string
{
    if (! defined('SNAPRENDER_API_KEY')) {
        return null;
    }

    $key = 'ssr_' . md5($url . $format);
    $cached = get_transient($key);
    if ($cached) {
        return $cached;
    }

    $endpoint = add_query_arg([
        'url' => rawurlencode($url),
        'format' => $format,
        'block_cookie_banners' => 'true',
        'cache_ttl' => 86400,
    ], 'https://app.snap-render.com/v1/screenshot');

    $response = wp_remote_get($endpoint, [
        'headers' => ['X-API-Key' => SNAPRENDER_API_KEY],
        'timeout' => 60,
    ]);

    if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
        return null;
    }

    $body = wp_remote_retrieve_body($response);
    $upload = wp_upload_bits(md5($url) . '.' . $format, null, $body);
    if (! empty($upload['error'])) {
        return null;
    }

    set_transient($key, $upload['url'], DAY_IN_SECONDS);
    return $upload['url'];
}

add_shortcode('site_screenshot', function ($atts) {
    $atts = shortcode_atts(['url' => '', 'width' => '640'], $atts);
    if (! $atts['url']) {
        return '';
    }
    $src = ssr_capture_url(esc_url_raw($atts['url']));
    if (! $src) {
        return '';
    }
    return sprintf(
        '<img src="%s" alt="%s" width="%d" loading="lazy">',
        esc_url($src),
        esc_attr(wp_parse_url($atts['url'], PHP_URL_HOST)),
        (int) $atts['width']
    );
});

Add the key to wp-config.php:

define('SNAPRENDER_API_KEY', 'sk_live_your_key');

Activate the plugin and [site_screenshot url="https://example.com"] in any post renders the thumbnail. The first view captures and saves the file to the uploads folder; every view for the next day reads the transient and never calls out.

Screenshots as an API call

Send a GET request, get a PNG back. Ads and cookie banners blocked by default.

200 free renders a month. Paid plans start at $9.

Featured image from a URL

For a directory where each post is a site, set the screenshot as the featured image when the post is saved:

add_action('save_post', function ($post_id, $post) {
    if ($post->post_type !== 'site' || has_post_thumbnail($post_id)) {
        return;
    }
    $url = get_post_meta($post_id, 'site_url', true);
    if (! $url) {
        return;
    }

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

    $src = ssr_capture_url($url);
    if (! $src) {
        return;
    }
    $attachment_id = media_sideload_image($src, $post_id, null, 'id');
    if (! is_wp_error($attachment_id)) {
        set_post_thumbnail($post_id, $attachment_id);
    }
}, 10, 2);

This assumes a custom post type site with a site_url meta field; adjust the names to your setup. Because the save hook runs on the admin request, a slow target page delays the editor's save by the capture time. If that bothers editors, move the call into a WP-Cron event.

Notes on limits and costs

  • The free plan is 200 renders a month; with the transient cache and cache_ttl on the API, a directory of 200 sites costs about 200 renders on day one and near zero afterwards.
  • wp_remote_get returns a WP_Error on network failure and a non-200 code on capture failure; both paths return null above, so the page renders without an image instead of breaking.
  • For a full-page capture add 'full_page' => 'true' to the query; for a mobile look add 'device' => 'iphone_15_pro'.

No key at all

For a preview image with no account, the keyless embed is one tag: <img src="https://app.snap-render.com/v1/embed?url=https://example.com" width="640">. It shares a daily budget and serves a neutral placeholder when the budget is gone, so use it for low-volume pages and the plugin above when you want guaranteed renders.

Related: PHP quickstart, website thumbnails, link previews.

Frequently asked questions

Does this need anything installed on the hosting server?

No. It uses wp_remote_get, which every WordPress install has. The browser that renders the page runs behind the API.

Where should the API key be stored?

As a constant in wp-config.php (SNAPRENDER_API_KEY). Keep it out of theme files and out of the database options table if you can.

Will it slow down page loads?

Not after the first render: the snippet caches the image URL in a transient for a day, and the API caches the render on its side, so repeat views cost nothing.

Can I save the screenshot as a featured image?

Yes. The second function downloads the image into the media library with media_sideload_image and sets it as the post thumbnail.

Screenshots as an API call

200 free renders a month. Paid plans start at $9.

Sign up free