How To Query Multiple Custom Post Types with query_posts [WordPress Tip]

A very common WordPress function to modify the posts that are fetched on a page is called query_posts(). There are two ways to pass parameters to query_posts; you can pass an array or you can use the concatenated version. To display multiple post types on a page, the easiest way to do that is to pass parameters as an array argument to query_posts, like so:

$args = aray(
'post_type' => array ( 'post', 'page','event')

In some case you may want to use the concatenated version instead, for example because you are fetching posts using Ajax and are using GET variables. This is pretty straightforward if you are querying a single post type:


The question is, how do you query multiple post types? Looking at the WordPress codex, it doesn’t quite tell you how this works for post types. You could try similar examples for querying multiple category ids or tags, like this:

// like multiple category ids: doesn't work
// like multiple tags: also doesn't work

But you’ll quickly find neither of these examples work. I was about to give up and just use a serialized array for my use case, when I tried another simpler method for passing an array via a GET string:


Lo and behold, it works! In hindsight, this is a common way of passing arrays via a GET string, it just didn’t occur to me as an option because it wasn’t in the Codex.

If you have multiple post types on your WordPress site you can try it quickly by entering this as the url: ‘[]=post&post_type[]=page’.


  1. Thanks for getting me closer to the solution… I had to go another route though, so I thought I’d share my solution here, too.  I’m using the infinite scroll plugin, and it was having trouble loading additional posts when I used query_posts() — it kept repeating the same ones or not finding all of the available ones no matter what arguments I put into it.

    Using the pre_get_posts() function to add custom posts to the main query worked like a charm, though:

    I took the one for filtering the search results by post type and just took out the is_search() conditional to apply it to the main query.  They also recommend this solution for loading speed reasons — apparently using query_posts() can be a big drag on load time.

    Thanks again for getting me on the right track!

    • Franco Tanzarella says:

      First of, thanks for this article. This helped me a ton but like Benjamin I ran into the same issue using infinite scroll.

      My only problem is that I can’t get Benjamin’s solution to work because I don’t know how to implement it. Can you please give me some help with this?

      How will I modify this bit using pre_get_posts() ?

      query_posts( array(
       'post_type' => array( 'post', 'tattoo', 'staff' ),
       'showposts' => 20,
       'paged' => $paged,
       'orderby' => 'rand')


      • Peter Knight says:

        Hi Franco,

        What pages/template would be showing this loop you are trying to create?
        Code should look something like this:

        function tattoo_biz_blog_loop( $query ){
        if( $query->is_home() ){
        $query->set( 'post_type',
        array('post', 'tattoo', 'staff')
        $query->set( 'posts_per_page', 20);
        add_action( 'pre_get_posts', 'tattoo_biz_blog_loop' );

        Haven’t tested but should work.

  2. Also, I’m sure there’s more that can be done to be more specific about where this goes into effect.  For me, it worked fine to modify the main query anywhere it appeared.  But yeah, you can set it up to just filter search results this way, etc.

  3. Peter Knight says:

    Thanks for sharing that Benjamin. I hadn’t thought of the infinite scroll use case, that’s cool. Glad you found the pre_get_posts route, that filter is like magic. In the infinite scroll case it also means you didn’t need to touch any of the javascript.

    Re: query_posts, I need to update the example in this post, as using query_posts is almost always a suboptimal route to go with.

  4. Charls says:

    First of all, I would like to say you’re awsome, This is very useful. Thanks for sharing.

    I would like to know if you can help me with this. I need to show only the post of one category from a post type and the posts of another category of another post type.

    Actually i’m trying to use the next query:


    the categories : 6,4,126, etc. are from the post type(post).

    and I need to show the category conciertos(tag id=4) from post type ai1ec_event and the post of the other categories.

    I just tried another query and I can’t get what i need, the other query is this:


    where tag_ID=4 is the category conciertos from ai1ec_event post type.

    and the tag_ID=5 is from other catgory of ai1ec_event post type.

    Please, I really need help, thanks.


    • Peter Knight says:

      This sounds like you are trying to combine two separate queries, which can’t be done with query_posts unfortunately. Are you trying to show these posts in a template file (and your present code is placed there?), say for your blog home or some an archive page? Is pagination important (do visitors need to be able to click through to older posts? Is the order chronological?

      There are a couple of approaches to do this, unfortunately all of these are bit advanced.

      You could run 2 queries (use WP_Query) and merge them, sort the results and show them. Something similar is demonstrated in the first answer here: The one disadvantage is that this query will not be efficient if you want to show 10 (or any fixed number of) posts per page, especially when it comes to pagination. You could do something different if pagination is important, such as to limit the query by date (say posts from the last week) and not care about the max number of posts per page. This too creates a lot more code though.

      Another solution with a complex query such as this can be achieved with custom SQL magic, don’t really recommend that though. Alternatively, you could give all eligible posts a unifying trait, like a custom field value. You could assign the custom field automatically when a new post is made and apply a custom field value to all the existing posts. Then in your query you just need to set the post types and the custom field (meta key), without needing to specify categories.

      Sorry I don’t have a short and quick way to resolve that specific use case – it’s a complex query I’m afraid, whatever solution you pick will involve quite a bit of code.

  5. Charls says:

    Thanks for answer,

    Actually I really appreciate your answer and your suggest. I don’t really care about pagination because the posts will be at home-page and I will show them in a box with a jquery-scrollbar so I’m using the excerpt content and I will try this solution (combine two queries) and post the results.


  6. Charls says:

    Hi again,

    actually I couldn’t solve my problem to combine 2 queries and I don’t really care for pagination.

    I will show the two queries I need to combine:

    		$args = array(
    	'post_type'=> 'ai1ec_event',
    	'events_categories'    => 'Eventos Locales',
    	'order'    => 'ASC'
    query_posts( $args );
    $args = array(
    	'post_type'=> 'post',
    	'category'    => 'Noticias',
    	'order'    => 'ASC'
    query_posts( $args );
    		if(have_posts()) : while(have_posts()) : the_post(); ?>

    Please I really need help with this, thanks.

  7. Charls says:

    Finally I could solve the problem to combine 2 queries.

    The solution is the next:

    $blogposts = get_posts(array(
                        'cat' => 6,
                        'depth' => 0,
                        'post_type' => 'post',
                        'post_status' => 'publish',
                        'posts_per_page' => 1000
                // second query
                $eventos = get_posts(array(
                        'post_type' => 'ai1ec_event',
                        'events_categories' => 'eventos-conciertos',
                        'post_status' => 'publish',
                        'posts_per_page' => 1000
                $mergedposts = array_merge( $blogposts, $eventos ); //combine queries
                $postids = array();
                foreach( $mergedposts as $item ) {
                $postids[]=$item->ID; //create a new query only of the post ids
                $uniqueposts = array_unique($postids); //remove duplicate post ids
                $args = array(
                        'post_type' => array('ai1ec_event','post'),
                        'post__in' => $uniqueposts,
                        'orderby' => 'rand'
                $loop = new WP_Query($args); 
    if($loop->have_posts()) : while($loop->have_posts()) : $loop->the_post(); ?>
    'cat' => 6,


    'category'    => 'Noticias',

    in the first code I Published

    I hope this could help somebody else.

    Special Thanks to seltzdesign who comment the solution here:

Leave a Reply

Your email address will not be published.