WP REST API – Multisite – get posts from all sites

Here’s a REST API recipe for latest post per site on a multi-site, for relatively few sites:

  • Use get_sites(),
  • loop over sites and switch to each site,
  • query latest post on each site,
  • collect data,
  • order data by utime,
  • serve data

Example REST Routes

https://example.com/wp-json/wpse/v1/latest-post-per-site
https://example.com/wp-json/wpse/v1/latest-post-per-site?number=10
https://example.com/wp-json/wpse/v1/latest-post-per-site?debug

Example Plugin

Here’s a demo plugin implementing the above recipe (needs testing):

<?php
/**
 * Plugin Name: WPSE-307040: Rest API - Latest Post Per Site
 * Description: Rest API - latest post per site (multisite).
 * Plugin URI:  https://wordpress.stackexchange.com/a/307051/26350
 * Author:      birgire
 * Version:     1.0.0
 */

namespace WPSE\RestAPI\LatestPostPerSite;

/**
 * Register Rest Route
 */

add_action( 'rest_api_init', function() {
    register_rest_route(
        'wpse/v1',
        '/latest-post-per-site/',
        [
           'methods'       => 'GET',
           'callback'      => __NAMESPACE__.'\rest_results'
        ]
    );
} );


/**
 * Rest Results
 */
function rest_results( $request ) {
    $parameters = $request->get_query_params();

    // Default 10 items
    $number = isset( $parameters['number'] ) ? (int) $parameters['number'] : 10;

    // Max 30 items
    if( $number > 30 ) {
        $number = 30;
    }

    $items = [];
    $i = 0;

    $blogs = get_sites( [
        'orderby' => 'last_updated',
        'order'   => 'DESC',
        'number'  => (int) $number         
    ] );

    foreach ( $blogs as $blog ) {
        switch_to_blog( $blog->blog_id );

        // Site info
        $details =  get_blog_details( $blog->blog_id );

        $items[$i]['sitename'] = esc_html( $details->blogname );
        $items[$i]['siteid']   = (int) $blog->blog_id;
        $items[$i]['homeurl']  = esc_url( $details->home );

        // Latest post
        $args = [
            'orderby'                 => 'post_date',
            'order'                   => 'DESC',
            'posts_per_page'          => 1,
            'post_type'               => 'post',
            'post_status'             => 'publish',
            'ignore_sticky_posts'     => true,
            'no_found_rows'           => true,
            'update_post_term_cache'  => false,
            'update_post_meta_cache'  => false,
        ];

        $query = new \WP_Query( $args );

        if( $query->have_posts() ) {
            while( $query->have_posts() ) {
                $query->the_post();
                // Title
                $items[$i]['title']   = esc_html( get_the_title( get_the_ID() ) );
                // Date
                $items[$i]['date']    = esc_html( get_the_date( 'Y-m-d H:i:s', get_the_ID() ) );
                // Unix time
                $items[$i]['utime']   = esc_html( get_the_date( 'U', get_the_ID() ) );
                // Excerpt
                $items[$i]['excerpt'] = esc_html( wp_trim_words( strip_shortcodes( get_the_content() ), 50 ) );
                 // Permalink
                 $items[$i]['url']    = esc_url( get_permalink( get_the_ID() ) );
            }
            wp_reset_postdata();
       }

       $i++;
       restore_current_blog();

       // Sort by utime.
       $items = wp_list_sort( $items, 'utime', 'DESC' );

       $data = [
           'success' => true,
           'count'   => count( $items ),
           'items'   => $items,
       ];

       // Debug for super admins.
       if( isset(  $parameters['debug'] ) && current_user_can( 'manage_networks' ) ) {
           $data['qrs'] = get_num_queries();
           $data['sec'] = timer_stop(0);
       }

       return $data;
   }

For larger number of sites, I would instead consider hooking into the create/update/delete for post/meta/taxonomy/term on each site, to sync and collect site wide data to a dedicated data site in the multi-site network.

Hope it helps!

Leave a Comment