Get Posts or Terms from All Blogs in Multisite Network

How to query all posts or terms in a single loop within WordPress Multisite install. With working pagination.

Query All Network Posts

If you're familiar with WP_Query class, you will be stunningly surprised. Network_Query is its multisite analogue.

$args = array(
	'posts_per_page' => 50,
	'orderby' => 'name',
	'order' => 'ASC'
	// all WP_Query arguments are supported including meta_query, tax_query etc.
$network_query = new Network_Query( $args ); // just like WP_Query!
if( $network_query->have_posts() ) :
	while( $network_query->have_posts() ) : $network_query->the_post();
		// $network_query->post is a post object - print_r( $network_query->post ) to find out more
		// use build-in functions like network_get_permalink() and network_get_post_thumbnail() to get post URL and thumbnail accordingly
		echo '<h3 id="post-' . $network_query->post->ID . '" class="blog-' . $network_query->post->BLOG_ID . '">
			<a href="' . network_get_permalink( $network_query ) . '">' . $network_query->post->post_title . '</a>
	echo 'No posts in the network for this criteria.';

Global Search

One search form. Multiple blogs search resuts.
You can search for posts, pages, tags, categories, custom post types and taxonomies.

Multiple blogs search results


Choose Post Types and Blogs to Index / to Query

post types and global search customization

Unlimited Blogs

Hundreds of blogs in your network? Not a problem — my plugin uses cron jobs to prevent server overload.

Unlimited number of blogs is supported


I'm always here if you need my help.
Please leave your question here or contact me if you need assistance with the plugin.

$args = array(
	'posts_per_page' => 50,
	'orderby' => 'name',
	'order' => 'ASC'
	// all WP_Query arguments are supported including meta_query, tax_query etc.
$network_query = new Network_Query( $args ); // just like WP_Query!
if( $network_query->have_posts() ) :
	while( $network_query->have_posts() ) : $network_query->the_post();
		// $network_query->post is a post object - print_r( $network_query->post ) to find out more
		// use build-in functions like network_get_permalink() and network_get_post_thumbnail() to get post URL and thumbnail accordingly
		echo '<h3 id="post-' . $network_query->post->ID . '" class="blog-' . $network_query->post->BLOG_ID . '">
			<a href="' . network_get_permalink( $network_query ) . '">' . $network_query->post->post_title . '</a>
	echo 'No posts in the network for this criteria.';

Global Multisite Search and Custom Post Queries

Just for 29$

Get the plugin or to learn more

So, How to Get All Network Posts in a One Query?

Click the links below to jump to a specific part of the documentation.

A few notes about plugin installation #

This plugin will index all posts in your Multisite Install with custom fields meta_query and terms tax_query. The posts and terms index will be stored in the following database tables.

phpMyAdmin: MySQL database tables with posts and terms indexed from all sites in a network.
MySQL database with the global content inside phpMyAdmin

Now some notes about the plugin installation.

  1. You can download the plugin by clicking the button in the top right part of the screen.
  2. By default this plugin will index only the new posts you publish. If you want to add to the index your old posts, go to Settings > Network Index and then switch to Rebuild Index tab.
Go to the Rebuld Index tab if you want to add your old posts to the network index.
That’s where you can completely rebuild the index

One important thing to know — when you rebuild your global index, it may take a time — how much — depends on the size of your Network. Maybe 10 minutes or maybe a hour.

Example 1. Just Get the Posts like WP_Query #

Let me introduce you the awesome Network_Query class — it works just like WP_Query but for all the network websites and supports all the same parameters + blog_id parameter (I will show this parameter in use in the next example).

Now let’s begin with something simple.

$network_q = new Network_Query('posts_per_page=5'); // display 5 latest global posts
if( $network_q->have_posts() ) :
	// run the loop for each post
	while( $network_q->have_posts() ) : $network_q->the_post();
		// we just get post titles
		// but you can print_r( $network_q->post ) to view all post data
		echo '<h3>' . $network_q->post->post_title . '</h3>';
else :
	echo 'No posts found for this criteria';

Something a little more interesting.

// Network_Query parameters
$args = array(
	'posts_per_page' => 14,
	'orderby' => 'name',
	'order' => 'ASC',
	'meta_query' => array(
			'key' => 'featured', // we display only posts with custom field 'featured' = 'on'
			'value' => 'on'
$network_q = new Network_Query( $args );
// if there are posts, then print <ul>
if( $network_q->have_posts() ) :
	echo '<ul>';
	// run the loop
	while( $network_q->have_posts() ) : $network_q->the_post();
		// the get_permalink() function won't work without switch_to_blog()
		// you can use network_get_permalink() instead but it is a little slower
		switch_to_blog( $network_q->post->BLOG_ID );
		// you can obtain the post title from $network_q->post object
		echo '<li class="post-' . $network_q->post->ID . ' blog-' . $network_q->post->BLOG_ID . '">
			<a href="' . get_permalink( $network_q->post->ID ) . '">' . $network_q->post->post_title . '</a>
		// restore_current_blog() to switch to the previous (!) website
	echo '</ul>';
network_reset_postdata(); // add it after the loop if you plan to use Network_Query multiple times on the page

Some comments:

  • In the loop we obtain $network_q->post object — it has all WP_Post object parameters with $network_q->post->BLOG_ID, the ID of the network blog.
  • If you need to do something and the WP_Post object is not enough for your purposes, then use the switch_to_blog() function – it allows you run any WordPress loop functions like get_the_post_thumbnail(), get_permalink() etc. Just do not forget to pass Post ID into these functions.
  • We need restore_current_blog() to cancel the switch_to_blog(), the function switches us to the previous blog — so, use it in a loop.
  • The network_reset_postdata() works similar to wp_reset_postdata().

The next very important feature of the plugin is global search. And you can search not only for posts and pages (post types), you can also perform search for categories and post tags (taxonomies).

Starting with the plugin version 4.4 it is even not required to do anything in the code in order to enable a global search. Just go to Settings > Network Index and then proceed to the Global Settings tab.

If you use the widget option, the global search widget will appear in Appearance > Widgets, or if you would like to replace the default search results, you should also proceed to the Sites admin page and turn it on there as well.
If you use the Widget option, the Global Search Widget will appear in Appearance > Widgets, or if you would like to replace the default search results with the global ones, you should proceed to the Sites admin page and turn this feature on there for each site individually.

With the code you also can:

  • Use WordPress default search engine across the whole network. I mean s= parameter of the Network_Query class.
    $args = array(
    	's' => 'Search query'
    $query_search = new Network_Query( $args );
  • Search for posts by their custom fields values — meta_query parameter of the Network_Query class.
    $args = array(
    	'meta_query' => array(
    		'relation' => 'OR',
    			'key'     => 'key1', // key1 must contain "wordcamp"
    			'value'   => 'wordcamp',
    			'compare' => 'LIKE'
    			'key'     => 'key2', // OR key2 must contain "wordpress"
    			'value'   => 'wordpress'
    			'compare' => 'LIKE'
    $query_search = new Network_Query( $args );
  • Search terms by part of their titles and descriptions.
    $network_cats = network_get_terms('category', 'description__like=wordcamp');
    $network_cats = network_get_terms('category', 'name__like=wordpress');

In full global search tutorial I will explain you each of the above examples and show how to replace standart WordPress search form and results page with the global one.

Everything else is like in WP_Query.

Example 3. Get Posts only from the specific blogs in a network. Choose post types to index / to query #

Actually you can:

  • pass post_type or blog_in parameter to the Network_Query() class,
  • configure it in settings (globally for the whole network, or for each blog separately).
(string|array) It is default WP_Query parameter and I suppose you should already know how it works.
$args = array( 'post_type' => array( 'post', 'page' ) ); // get only posts and pages
(int|array) Specify the IDs of the blogs you want to query posts from.
$args = array( 
	'post_type' => 'post', // only posts
	'blog_id' => array( 1, 3 ) // only from blogs 1 and 3

Do not know where to get blog ID? Not a problem — my plugin will show blog ID column in your network dashboard Sites > All sites.

Column with Blog ID on the All Sites page
Plugin automatically adds the Blog ID column to the table with the all websites.

You should also know one thing about post_type query parameter — it will work only for indexed post types, for example here I added to the index the attachment post type in the plugin settings:

Add the attachment post type to global network index.

And as I mentioned before, the post types for indexing can be configured individually for each network website. To do this, in your multisite dashboard go to Sites > All sites and click Edit link under the Indexing tab.

Here you can configure what post types to index for each blog individually.
For each blog you can choose what post types to index or completely disable indexing.

Example 4. Query Network Posts with Pagination #

It is always very important for me to implement the pagination for network posts. And this is the simple way how you can do it:

global $wp_query;
// get the current page number from $wp_query, I mean the URL parameter, i.e. /page/2
$current_page = (get_query_var('paged')) ? get_query_var('paged') : 1; // you can use $wp_query->query['paged'] as well
// similiar to query_posts()
$network_q_posts = network_query_posts( array('posts_per_page' => 3, 'paged' => $current_page) ); 
// run the loop
foreach( $network_q_posts as $network_q_post ) :
	// we need it to work with get_the_post_thumbnail() and get_permalink()
	switch_to_blog( $network_q_post->BLOG_ID );
	echo '<li>' . get_the_post_thumbnail( $network_q_post->ID ) . '<a href="' . get_permalink( $network_q_post->ID ) . '">' . $network_q_post->post_title . '</a></li>';
	// switch back
// we should change the global $wp_query value to work correctly with pagination
$wp_query = $GLOBALS['network_query'];
// I use the popular WP PageNavi plugin
// reset the $wp_query

If you have problems with this example, you can read full detailed tutorial here.

Example 5. Get categories, post tags globally using get_terms() analogue #

My plugin allows you to get not only post types but taxonomy terms as well. It can be implemented easily with network_get_terms() (click the link to view detailed tutorial).

And here is a simple example:

$network_categories = network_get_terms('category', 'orderby=name&hide_empty=0');
if( $network_categories ){
	echo '<select>';
	foreach ( $network_categories as $network_category ){
		echo "<option value='{$network_category->term_id}'>{$network_category->name}</option>";
	echo '</select>';
in this example is obvious that
$network_category->name - category name,
$network_category->term_id - category global unique ID across the network
but you can get more info about the category if you look into the object using
print_r( $network_category );

4.7.1 – Sep 18, 2017

  • Added: Feature Images support in VC widget.
  • Fixed: Bug with post type selection in Visual Composer element.

4.7 – Sep 16, 2017

  • Added: Visual Composer element that allows to display posts or pages from any blogs of your Network.
  • Changes in plugin update mechanism.

4.5 – July 25, 2017

  • Added: You can now manage posts from all blogs in a centralized place in Network Dashboard, more info here.

4.4.1 – December 5, 2016

  • Added: network_get_term() function, the get_term() equivalent. The first parameter is $blog_id.

4.4 – December 4, 2016

  • Now you can completely replace the website default search results with the global network search results in the plugin settings without code at all.
  • As an option you can use both default and global search widgets in your website sidebar.

4.3.1 – November 15, 2016

  • Fixed: Warnings when indexing post meta.

4.3 – November 7, 2016

  • Improved: Plugin option pages are optimized for both new and old versions of WordPress.
  • Improved: Plugin performance in /wp-admin/.
  • Fixed: Problem with database tables, when default WordPress database has uncommon columns.
  • Fixed: Problem when saving blog index settings caused by WP-Cirrus plugin.

4.2 – September 23, 2016

  • Bug fixes.
  • Plugin update improvements.


  • Now the blog_id parameter of Network_Query supports array values.


  • Fixed redirect location for manual updates check.


  • Code optimization and UI improvements.


  • You can now get terms (categories, post_tags etc) from all blogs in Multisite network in a one loop using network_get_terms() function.
  • Changes in plugin update mechanism.


  • Changes in plugin update mechanism.
Awesome support from @rudrastyh for his essential plugin "the #wordpress network query for multisite"
Valentin GUENICHON @IFight4TheUsers
I've found best solution to pull all sub-site blog in network into main site. Thanks to @rudrastyh develop #WordPress True Multisite Indexer
Fikri Mastor @FikriMastor
Thank you @rudrastyh for the great plugin, intuitive tutorial and efficient service. I am very happy :) Great job! #wordpress #multisite
Daryl Glass @darylglass
Get Posts or Terms from All Blogs in Multisite Network. Awesome plugin by @rudrastyh!
Mo @M6J33D
  • Hi,

    I am building a multisite which will have the main site, then 7 other sites. I want the front page of the main site to show all recent posts made by all the sites laid out in a normal blog format. Does this plugin achieve that?

    Just checking as so far I have found things that won’t work with every other global/network plugin.


  • Hello Misha.

    This plugins look exactly what i need.

    Please, could you tell me if you have done some test working your plugin with relevanssi search plugin.

    We use this plugin to index ACF fields in our search in a non-multisite websites. Now we are working in a project with multisite features. We think your plugin we will help us a lot, if we can make this one work with relevanssi.

    Do you have some experience working with relevanssi and your plugin before ? Or, maybe experience indexing ACF fields in search for multisite networks.


    • MishaAuthor August 28, 2017 at 16:08

      Hello Daniel,

      ACF are WordPress Custom Fields and my plugin indexes custom fields with no problems. Everything is OK. But I didn’t test it with relevanssi.

  • I am trying to implement the multisite search. I’m using the automatic method to replace the wordpress search with the network search. It is finding the pages, but it is not inserting the url for the subsites into the path for the results.

    it is returning the link like this:

    instead of what it should be

    • Hi Kris,

      Automatic method is in beta and tested in default WordPress themes (Twenty Seventeen etc). It looks like your theme doesn’t support this method. You have to create your own loop template and get URLs of the posts with get_permalink( $post_id ) function.

  • Valentin GUENICHON September 27, 2017 at 12:29

    I’ve tried so many plugins to get network queries that I have to ask you if it will really work in my case…
    My query will take place in a shortcode. I need to get un number of posts (shortcode argument) from some sites, from a particular post-type (shortcode argument), ordered (shortcode argument) by a meta value (in a meta query). The posts will be rendered through a template (get_template_part) enclosed in output buffering (ob_start). My template is full of ACF fields requests.

    Here’s my actual code. Obviously the number of posts is applied for each site (and not for a potential network query) and the ordering is actually done by javascript.

    add_shortcode( 'events_vc_output', 'events_vc');
    function events_vc( $atts, $content = null ) {
    	extract( shortcode_atts( array (
        	'compare' => '>=',
        	'posts_per_page' => '-1',
        	'order' => 'ASC',
        	'el_class' => '',
    	), $atts ));
    	$output = '';
    	$output .= '<div class="cards cards-3 events-list '.$el_class.'">'; // container des événements
    	$subsites = get_sites(); // on cherche la liste des sites sur le réseau
    	foreach($subsites as $subsite) { // pour chaque site
    	switch_to_blog($subsite->blog_id); // on switch de site avec l'ID
    	$events = new WP_Query( array( // on fait la requête sur les events avec les filtres
    		'post_type'			=> 'events',
    		'post_status'		=> 'publish',
    		'meta_key'	 		=> 'date_debut',
    		'orderby' 			=> 'meta_value',
    	    'order' 			=> $order, // pas forcément utile car order côté client après
    	    'posts_per_page'	=> $posts_per_page,
    		'meta_query' 		=> array(
    	        array( // on compare avec la date du jour
    	            'key'		=> 'date_debut',
    	            'value'     => date('Ymd'),
    	            'compare'   => $compare == '&lt;' ? '<' : $compare, // le symbole est stocké encodé
    	            'type'		=> 'DATE',
    	set_query_var( 'blogid', $subsite->blog_id ); // on récupère l'ID du site pour le passer au template
    	if ( $events->have_posts() ) {
    		while ( $events->have_posts() ) : $events->the_post();
    		    get_template_part('components/modules/event'); // on appelle le template
    		    $output .= ob_get_clean();
    	restore_current_blog(); // on revient au site 1
    	if($order == 'ASC') { // on ajoute des cartes fantomes pour la mise en page avec un data-date max pour que l'ordering fonctionne sur les événements à venir
    		$output .= '<div class="card event phantom" data-date="99999999"></div>';
      		$output .= '<div class="card event phantom" data-date="99999999"></div>';
      	} else { // on ajoute des cartes fantomes pour la mise en page avec un data-date min pour que l'ordering fonctionne sur les événements passés
      		$output .= '<div class="card event phantom" data-date="00000000"></div>';
      		$output .= '<div class="card event phantom" data-date="00000000"></div>';
    	$output .= '</div>'; // on ferme le container
    	return $output;

    Thanks in advance for your answer.

    • Hello Valentin,

      Yes, the plugin will work for your case. You can get posts with it from a particular post-type, ordered by a meta value.

      Of course you have to optimize your template event.php. Not sure about ACF functions, but plugin works great with get_post_meta() which is the equivalent.

      • Valentin GUENICHON September 28, 2017 at 10:30

        Thanks a lot Misha. Hope my case is not so weird.
        For you to know, it’s a shortcode for VC and I read that you have recently interest in VC.

      • Valentin GUENICHON September 28, 2017 at 17:39

        Well… I have 2 questions after trying to get the posts in my configuration.
        First, I don’t figure out how to get my template work. What I have to modify (sorry I’m not a developer and sometimes it takes me a lot of time and try to understand).
        Then, during my test I realize that I’ve forgotten to tell you that my sites are multilingual too (I work with Polylang). The network query output all posts in every language. Maybe there’s a Polylang solution but not sure they knows your plugin. Si if you can help it’ll be great.
        Thanks a lot

      • Valentin GUENICHON September 28, 2017 at 17:55


        set_query_var( 'postid', $events->post->ID );

        in the loop and

        $postid = get_query_var('postid');

        in the template allow me to display what I need with


        I hope I can get all the functions like that.

        Still the problem with the languages.

      • Valentin GUENICHON September 28, 2017 at 18:09

        And to help with ACF, we can pass the post ID as a parameter to its functions, like that :

        get_field_object('field_slug', $network_q->post->ID);

        and in my particular case :

        get_field_object('field_slug', $postid);
      • Valentin GUENICHON September 28, 2017 at 18:49

        With this last message I resolved all my issues.
        Here’s my trick to get it work with Polylang, even if I think it’ll have better performance if you could consider getting your plugin compliant with Polylang.
        So, here it is.
        I get the current language in the loop:

        $current_lang = pll_current_language();
        set_query_var( 'current_lang', $current_lang );

        And use it to compare to the post language in the template:

        $current_lang = get_query_var('current_lang');
        $post_lang = pll_get_post_language( $postid );
        if( $post_lang == $current_lang ) { bla bla bla }
      • Thank you very much for your useful comments! 😊

        • Valentin GUENICHON September 29, 2017 at 10:15

          You’re welcome Misha. I think you have something to do with Polylang and in general multilingual plugins (like WPML). I’m pretty sure it could be more efficient if the class could query only the current language’s posts. Obviously, I can guess it’s not so easy to implement.
          Thanks a lot for this plugin, it deserves to be more visible as it’s so useful.

        • Valentin GUENICHON September 29, 2017 at 11:15

          Well, I still have an issue and I think it’s due to the languages.
          As I filter the languages after the query, the posts_per_page doesn’t work. I’ll check if I can do something and ask Polylang but I guess I will not have the knowledge to resolve this issue.

        • Valentin GUENICHON September 29, 2017 at 15:51

          Good news Misha. In my case, with Polylang, language is a custom taxonomy (language). So I tried to setup a tax_query. Unfortunately, it does not work. No more results.
          Do you think you can check it ?

        • Valentin GUENICHON September 29, 2017 at 17:28

          Sorry, the code:

          $events = new Network_Query( array(
            'tax_query' => array(
                'taxonomy' => 'language',
                'field' => 'slug',
                'terms' => 'en',
            'post_type'			=> 'events',
            'post_status'		=> 'publish',
        • Your code seems ok, please check if the language taxonomy terms are indexed in wp_network_terms database table.

          • Valentin GUENICHON October 2, 2017 at 10:36

            The table is empty

          • Valentin GUENICHON October 2, 2017 at 12:27

            From the author of Polylang : The language is no a public taxonomy. It is registered with ‘public’ => false and ‘publicly_queryable’ => true.

          • MishaAuthor October 3, 2017 at 13:59

            It shouldn’t matter. I mean if a taxonomy is related to any of indexable post types, its terms should be indexed.

          • Valentin GUENICHON October 3, 2017 at 14:22

            Well it seems that it’s not indexed. Is there something I can do ?

          • MishaAuthor October 3, 2017 at 14:29

            You can contact me by email and send the access to your website and I will try to help you with Polylang.

          • Valentin GUENICHON October 11, 2017 at 16:23

            Well, after re-installing plugin from scratch (with tables deletion), it works like a charm !
            Here’s a tip to get it work with Polylang. First, get the current language:

            $current_lang = pll_current_language();

            Then, use it through the query:

            $myquery = new Network_Query( array(
              'tax_query' => array(
                  'taxonomy' => 'language',
                  'field' => 'slug',
                  'terms' => $current_lang,
              'post_type' => 'post',
              'post_status' => 'publish',

            Thanks a lot Misha for your awesome help.

  • Hi,

    is it possibile Rebuild Index without clearing index?

    Thank you.

    • MishaAuthor October 3, 2017 at 14:02

      Hi Simone,

      Yes, all new posts will be indexed automatically. Rebuilding index is only necessary just once – after the plugin installation.

  • Hello Misha,
    is it possible to perform a Network Query for displaying all posts created by a specific author?

  • Valentin GUENICHON October 19, 2017 at 17:45

    Hi Misha,
    It seems that post_status publish is not applied in my network query. I get drafts too.
    Was there any issue with post_status before ?

Leave your question or feedback

Please, enter a comment
Please, enter a name
Incorrect email

Some of the companies that use this plugin

Make Do (Barnsley, UK) Modularte (Hanau, Germany) Connective DX (Sydney, Australia) Old River Creative (Woodstock, Virginia, USA)
DotDev (Australia, Brown Hill) Miux (Switzerland, Chur) PurposeWP (United States, Fort Worth) Rovecom (Netherlands, Hoogeveen)

14-Day Money Back Guarantee

Secure Checkout. Instant Download



$129 one-time payment
  • Lifetime priority support
  • Lifetime updates
  • Unlimited websites


$29 one-time payment
  • 2 weeks/support
  • 1 year/updates
  • Unlimited websites

View my refund policy here

The plugin is a one-time purchase which includes a license for plugin updates and support. To continue receiving updates & support after your license has expired you may renew at a discounted rate of 50%.