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_ttlon the API, a directory of 200 sites costs about 200 renders on day one and near zero afterwards. wp_remote_getreturns aWP_Erroron network failure and a non-200 code on capture failure; both paths returnnullabove, 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.