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.
Try it on your URL
Live capture, no signup. You get the screenshot and the exact API call that produced it.
This is the exact API call that made the image above. Swap in your key and it runs anywhere.
200 renders a month free.
You hit the free demo limit here. The full tool can verify you are human and keep going.
Continue in the full toolThe 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.
Or skip the setup entirely
Everything in this guide is one GET request with SnapRender. No browser to babysit, no timeouts to tune.
200 screenshots a month free. First render in under a minute.
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.