How to Create Your Own Website Screenshot Service by Leveraging WordPress’ mShots API Service

on in Blog
Last modified on

Table of Contents

This is an example-based tutorial where you will learn how to create a custom post type called Site, add a URL custom post meta field and generate a screenshot automatically based on that URL (using Automattic’s mShots API service).

The code below uses’s mShots API to generate screenshots on the fly. These images are not stored in your WordPress media library. They are served directly from servers.

As a bonus, we’ll create a shortcode to display everything in a nice loop, and add an option to show or hide the site.

Let’s create our website showcase section!

Custom Post Type

First, we create the Site custom post type:

function shirley_create_sites() {
    $labels = [
        'name'                  => _x('Sites', 'Post Type General Name', 'shirley'),
        'singular_name'         => _x('Site', 'Post Type Singular Name', 'shirley'),
        'menu_name'             => __('Sites', 'shirley'),
        'name_admin_bar'        => __('Site', 'shirley'),
        'archives'              => __('Site Archives', 'shirley'),
        'attributes'            => __('Site Attributes', 'shirley'),
        'parent_item_colon'     => __('Parent Site:', 'shirley'),
        'all_items'             => __('All Sites', 'shirley'),
        'add_new_item'          => __('Add New Site', 'shirley'),
        'add_new'               => __('Add New', 'shirley'),
        'new_item'              => __('New Site', 'shirley'),
        'edit_item'             => __('Edit Site', 'shirley'),
        'update_item'           => __('Update Site', 'shirley'),
        'view_item'             => __('View Site', 'shirley'),
        'view_items'            => __('View Sites', 'shirley'),
        'search_items'          => __('Search Site', 'shirley'),
        'not_found'             => __('Not found', 'shirley'),
        'not_found_in_trash'    => __('Not found in Trash', 'shirley'),
        'featured_image'        => __('Featured Image', 'shirley'),
        'set_featured_image'    => __('Set featured image', 'shirley'),
        'remove_featured_image' => __('Remove featured image', 'shirley'),
        'use_featured_image'    => __('Use as featured image', 'shirley'),
        'insert_into_item'      => __('Insert into site', 'shirley'),
        'uploaded_to_this_item' => __('Uploaded to this site', 'shirley'),
        'items_list'            => __('Sites list', 'shirley'),
        'items_list_navigation' => __('Sites list navigation', 'shirley'),
        'filter_items_list'     => __('Filter sites list', 'shirley'),

    $args = [
        'label'                 => __('Site', 'shirley'),
        'description'           => __('Site Showcase', 'shirley'),
        'labels'                => $labels,
        'supports'              => ['title', 'thumbnail', 'editor', 'custom-fields'],
        'hierarchical'          => false,
        'public'                => true,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'menu_position'         => 5,
        'menu_icon'             => 'dashicons-format-image',
        'show_in_admin_bar'     => false,
        'show_in_nav_menus'     => false,
        'can_export'            => true,
        'has_archive'           => false,
        'exclude_from_search'   => true,
        'publicly_queryable'    => false,
        'rewrite'               => false,
        'capability_type'       => 'post',
        'show_in_rest'          => true,

    register_post_type( 'site', $args);
add_action('init', 'shirley_create_sites', 0);

Custom Post Meta

Next, we create the custom post meta field:

 * Add meta box
 * @param post $post The post object
function shirley_add_meta_boxes($post) {
    add_meta_box('site_meta_box', 'Site Settings', 'shirley_build_meta_box', ['site'], 'normal', 'low');

 * Build custom field meta box
 * @param post $post The post object
function shirley_build_meta_box($post) {
    wp_nonce_field(basename(__FILE__), 'site_meta_box_nonce');

    $currentSiteUrl = get_post_meta($post->ID, '_site_url', true);
    $currentSiteVisibility = get_post_meta($post->ID, '_site_visibility', true);
    <div class="inside">
            <label for="site-url">Site URL</label>
            <input type="text" name="site_url" id="site-url" class="regular-text" style="width: 100%;" value="<?php echo $currentSiteUrl; ?>">

            <label for="site-visibility">Is the site visible in the <b>Showcase</b> section?</label>
            <select name="site_visibility" id="site-visibility">
                <option value="0" <?php echo ((int) $currentSiteVisibility === 0) ? 'selected' : ''; ?>>No</option>
                <option value="1" <?php echo ((int) $currentSiteVisibility === 1) ? 'selected' : ''; ?>>Yes</option>

 * Store custom field meta box data
 * @param int $post_id The post ID.
function shirley_save_meta_box_data($post_id) {
    if (!isset($_POST['site_meta_box_nonce']) || !wp_verify_nonce($_POST['site_meta_box_nonce'], basename(__FILE__))) {

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
    if (!current_user_can('edit_post', $post_id)) {

    $siteUrl = isset($_POST['site_url']) ? $_POST['site_url'] : '';
    $siteVisibility = isset($_POST['site_visibility']) ? $_POST['site_visibility'] : '';

    update_post_meta($post_id, '_site_url', $siteUrl);
    update_post_meta($post_id, '_site_visibility', $siteVisibility);

add_action('add_meta_boxes', 'shirley_add_meta_boxes');
add_action('save_post', 'shirley_save_meta_box_data');

Showcase Shortcode

And last, we display the sites in a nice list:

function shirley_sites() {
    $out = '<div class="projects">';

    $args = [
        'post_type' => 'site',
        'posts_per_page' => -1,
        'orderby' => 'menu_order'

    $the_query = new WP_Query($args);
    if ($the_query->have_posts()) {
        while ($the_query->have_posts()) {

            $siteId = get_the_ID();
            $siteVisibility = get_post_meta($siteId, '_site_visibility', true);

             * mShots
             * Example:
            $siteUri = get_post_meta($siteId, '_site_url', true);
            $siteUri = urlencode($siteUri);
            $siteThumbnail = '' . $siteUri . '?w=1280&h=960';

            // Override automated post thumbnail with a featured image
            if (has_post_thumbnail($siteId)) {
                $siteThumbnail = get_the_post_thumbnail_url($siteId, 'full');

            if ((int) $siteVisibility !== 0) {
                $out .= '<div class="item" style="background: url(' . $siteThumbnail . ') no-repeat top center; background-size: cover;">
                    <div class="item-info">
                        <h2>' . get_the_title($siteId) . '</h2>
                            <small>' . get_post_meta($siteId, '_site_url', true) . '</small><br>
                            <a href="' . get_post_meta($siteId, '_site_url', true) . '" rel="external" target="_blank">Visit Site</a>

    $out .= '</div>';

    return $out;

add_shortcode('sites', 'shirley_sites');

Shortcode Usage

Use the [sites] shortcode to display your sites.

That’s it! Enjoy!

Note that there are no other screenshot options. You can’t change the image quality, image format, the IP address, geolocation, or anything else. Images will be cached for an unspecified period, so you’ll need to mangle the link if you want to regenerate it.

mShots is licensed under GNU General Public License v2.0.

Related posts