Key Takeaways
- get_posts() is a flexible WordPress function that lets you fetch and display posts dynamically using categories, custom post types, tags, or other parameters.
- Always manage the loop correctly by using setup_postdata() and wp_reset_postdata() to avoid conflicts with other content on the page.
- Creating a custom related posts section improves user engagement without relying on plugins, while keeping your theme code clean and lightweight.
WordPress runs millions of websites. Why is it so flexible? A big reason is the built-in PHP tools that let you work with your content dynamically. Take get_posts. This one function lets you grab content straight from your WordPress database. Then you can put that content anywhere you want on your site.
- Do you need to list recent stuff?
- Want to show related articles?
- Building a totally custom area in your theme?
The get_posts function gives you total control over what posts show up and how they look when they display.
In this guide, we will look at how get_posts works. We’ll build a small project, something like a related posts area. We will also talk about the main settings, or “parameters,” for get_posts.
Finally, I’ll show you how good hosting, like Cloudways, can speed up these database requests so your site performs better.
- What is the WordPress get_posts Function?
- Understanding get_post vs get_posts in WordPress
- Key Parameters of get_posts and get_post_type Queries
- Building Simple and Advanced Queries With get_posts
- Displaying get_posts Results in WordPress
- Build a Custom Related Posts Section Using the get_posts Function in WordPress
- How Cloudways Optimizes WordPress get_posts Queries
- Best Practices and Common Pitfalls
- Conclusion
What is the WordPress get_posts Function?
The get_posts function in WordPress is a core PHP utility. It lets you pull a specific list of posts from your database. The default WordPress loop just shows posts on the main blog pages. But get_posts lets you query content anywhere else and completely control what posts you get back.
By default, get_posts returns an array of things called WP_Post objects. Each object holds all the details about one post: the title, the content, categories, tags, custom fields—everything.
This makes it easy to do custom things. Show posts in unique layouts. Create “related posts” areas. Build specialized widgets. All without ever touching your main blog feed.
You can use get_posts for tons of things:
- Showing new posts right on your homepage layout.
- Pulling up related reading on your single post pages.
- Making custom lists for certain tags or categories.
- Fetching content by specific authors or custom types you made.
This control is exactly why developers use get_posts constantly when they need custom control over their content.
Understanding get_post vs get_posts in WordPress
The names look almost identical, but get_post and get_posts do very different jobs.
- get_post is for grabbing just one thing. You usually know its ID already (a featured post, maybe). It returns one single WP_Post object.
- get_posts is for fetching a list of multiple things. Think lists of recent posts, or posts from one category. It returns an array of WP_Post objects, so you can easily loop through them all.
Look at this quick code example:
$args = [
'numberposts' => 5, // Give me 5 posts
'category' => 12, // Only from category ID 12
'orderby' => 'date', // Sort them by date
'order' => 'DESC', // Newest ones first
'post_type' => 'post', // Only standard posts (not pages)
];
$recent_posts = get_posts($args);
foreach ($recent_posts as $post) {
setup_postdata($post);
echo get_the_title($post->ID) . '<br>';
}
wp_reset_postdata();
In this code, I used the $args array to define exactly what posts I wanted. Then I ran get_posts and used a foreach loop to show their titles. I must use setup_postdata() so basic WordPress functions work, and always reset the data after (wp_reset_postdata()) to stop things from breaking somewhere else.
A quick summary:
- Need only one item? Use get_post.
- Need a list? Use get_posts. It handles all the list filtering.
- They both return the same type of object, so accessing the title or content is done the same way.
Key Parameters of get_posts and get_post_type Queries
get_posts is powerful because it’s so flexible. You can control nearly every part of the posts it pulls using its “parameters,” or settings. Knowing these lets you build the exact queries you need without writing custom database code (SQL).
Here are the settings I use the most:
- numberposts – How many posts to pull back. Default is 5.
- category – Get posts just from a certain category ID.
- orderby and order – These control the sorting. You can sort by date, title, or custom data.
- post_type – Grab custom types you created, not just standard blog posts.
- meta_key and meta_value – Use these for filtering by custom fields. Example: show posts that have a specific custom value.
- post_status – By default, it only fetches published posts. Change this if you need drafts or private posts.
Here’s a quick example combining a few of these parameters:
$args = [
'numberposts' => 5,
'category' => 12,
'orderby' => 'date',
'order' => 'DESC',
'post_type' => 'post',
];
$recent_posts = get_posts($args);
foreach ($recent_posts as $post) {
setup_postdata($post);
echo get_the_title($post->ID) . '<br>';
}
wp_reset_postdata();
Two final rules I live by:
- Do not pull too many posts at once. Even with caching, those huge queries slow everything down.
- get_posts does not handle pagination. If you need page numbers (Page 1 of 5), use WP_Query instead.
Once you’re comfortable with these parameters, you can build queries that fetch posts based on almost any condition you can imagine. Later, we’ll put this into practice in the mini project.
Building Simple and Advanced Queries With get_posts
Writing get_posts queries becomes straightforward once you know the core parameters. Most real-world uses start simple. They only get complex when a layout or filtering rule demands it.
The most basic example? Just grab the newest posts:
$posts = get_posts([ 'numberposts' => 5 ]);
This code pulls the last five published posts. Good for sidebars, footers, or any basic content block you need.
Want a more specific set? Filtering by category is usually next.
$posts = get_posts([ 'numberposts' => 5, 'category' => 8 ]);
WordPress only returns posts from that one category ID now. This is very common for “featured” sections or specific topic lists.
Custom post types work the exact same way. If your site uses something like projects or case_studies, tell get_posts which type to look for:
$posts = get_posts([ 'post_type' => 'projects', 'numberposts' => 6 ]);
Ordering is a small detail, but it changes results a lot. Posts sort by date by default, but you might need something else:
$posts = get_posts([ 'numberposts' => 5, 'orderby' => 'title', 'order' => 'ASC' ]);
This setup works great for alphabetical lists where order matters, like resource pages or directories.
For more complex needs, custom fields come into the picture. Say you flag certain posts as featured? You can filter specifically based on that custom field and its value:
$posts = get_posts([ 'numberposts' => 5, 'meta_key' => 'featured', 'meta_value' => 'yes' ]);
You are still using the same function here. You are just being much more specific about the exact content you want back.
The key thing is always to be intentional. Fetch only what you absolutely need. Do not stack unnecessary conditions. As these custom queries show up across many pages, performance becomes a huge concern, especially on busy sites.
Next, we will look at how to properly show the data that get_posts returns. We’ll also cover how to avoid common mistakes when working with these custom loops.
Displaying get_posts Results in WordPress
Grabbing posts with get_posts is step one. Showing them correctly on your site is step two. Since get_posts returns an array of WP_Post objects, you must run a loop and use setup_postdata() so standard WordPress template functions pull the right data.
Look at this example:
$posts = get_posts([
'numberposts' => 5,
]);
foreach ($posts as $post) {
setup_postdata($post);
echo '<h2>' . get_the_title($post->ID) . '</h2>';
echo '<p>' . get_the_excerpt($post->ID) . '</p>';
}
wp_reset_postdata();
Here, the loop goes through each post. It prints the title and excerpt. Using setup_postdata($post) makes sure functions like get_the_title() and get_the_excerpt() work correctly. And wp_reset_postdata() restores the global variables. This is essential so other loops on the page do not break.
This method works for simple lists, sidebars, or full-width sections. You can easily add links, featured images, or meta info inside the loop to build richer designs. The most important rule? Always reset the post data at the very end.
You can now use the parameters we talked about earlier to control exactly which posts are returned and how many. Combine those queries with this loop to display the content you want, anywhere on your site.
Build a Custom Related Posts Section Using the get_posts Function in WordPress
Now that we’ve covered get_posts, its parameters, and how to display posts, it’s time to put it into action. We’re going to build a simple related posts section that shows other posts from the same category at the end of a single post.
Here’s the approach:
- Get the current post’s category: We’ll use WordPress functions to grab the category ID of the post the user is reading.
- Set up a get_posts query: Using the category ID, we’ll create an $args array with parameters like numberposts (how many posts to show) and exclude (to skip the current post).
- Loop through the results: We’ll loop through the returned posts, displaying the title and a short excerpt for each. Using setup_postdata() ensures all WordPress functions like get_the_title() work correctly.
- Reset post data: After the loop, we’ll call wp_reset_postdata() to prevent conflicts with other loops on the page.
For this mini project, I’m using a WordPress site hosted on Cloudways, but the steps work the same on any hosting. We’ll build a simple related posts section that shows posts from the same category at the bottom of a single post.
This project is where we bring everything together. We’ll practice querying posts with parameters, looping through the results, and showing content dynamically.
Step 0: Check your site for categories
Before writing any code, let’s make sure your posts actually have categories:
- Go to your WordPress dashboard.

- Click Posts → Categories in the sidebar.

- You should see a list of categories. If you don’t, create one by entering a name like “News” or “Tutorials” and clicking Add New Category.

- Go to Posts → All Posts, open one of your posts, and make sure it has a category assigned. You can check the Categories box in the post editor.

If your posts don’t have categories, this project won’t work properly. Assign at least one category to a few posts.
Step 1: Decide where to put the code
For this mini project, we want the related posts to appear at the bottom of single post pages. That means we’ll add the code to the single.php file of your theme.
Most themes have a template file called single.php that controls how individual posts are displayed.
Steps to access the file:
- Go to Appearance → Theme File Editor.

- On the right-hand sidebar, find single.php.

- Make a backup of this file first (copy all its content into a text file) so you can restore it if something breaks. I’ll just copy all the code to a text file.
We’ll put the related posts code just before the get_footer() line at the bottom of single.php, so it shows at the end of the post content.
For Astra users: The main post content is displayed with astra_content_loop(). We won’t touch that. Instead, we’ll add our related posts after astra_primary_content_bottom() but before the closing </div><!– #primary –>. This ensures the section appears at the end of the post without interfering with the main loop.
To do this, I’ll add a new Related Posts Section to our single.php file. Like this:

For other themes: Look for where your main post content ends (usually after the_content() or your theme’s content loop) and place the related posts code immediately after.
Step 2: Get the current post’s category
Now we need to figure out which category the current post belongs to. This is important because we want to show related posts from the same category.
$categories = wp_get_post_categories(get_the_ID()); $category_id = $categories[0]; // Use the first category
What this does:
- get_the_ID() gets the ID of the post currently being viewed.
- wp_get_post_categories() returns an array of all category IDs assigned to that post.
- $categories[0] picks the first category. If a post has multiple categories, you could expand this later to include more.
Where to add it:
Since I’m using Astra, I’ll add this code right at the start of the Related Posts Section we created earlier.
Like this:
<?php // Start of Related Posts Section $categories = wp_get_post_categories(get_the_ID()); $category_id = $categories[0]; // Use the first category // End of Related Posts Section ?>
After adding the code, press “Update File” in the Theme File Editor. At this point, WordPress now knows the category of the post, and $category_id holds that value.
For other themes, the approach is the same: find the end of your main post content in single.php and start your related posts section there. Then, place the category code at the very beginning of that new section so get_posts knows which category to use.
Step 3: Fetch related posts with get_posts
Now that we have the current post’s category stored in $category_id, we can fetch other posts from the same category. Add this code inside the same PHP block we create earlier:
$args = [ 'numberposts' => 3, // Show 3 posts 'category' => $category_id, // Same category as current post 'exclude' => get_the_ID() // Don’t include the current post ]; $related_posts = get_posts($args);

Explanation:
- numberposts controls how many posts show.
- category filters posts by the category we got earlier.
- exclude ensures we don’t show the current post again.
Step 4: Loop through and display posts
Now that we have the related posts stored in $related_posts, we need to show them on the page. We’ll loop through the posts and display each post’s title and excerpt. Add this inside the same PHP block you’ve been using:
if ( $related_posts ) {
echo '<section class="related-posts"><h2>Related Posts</h2>';
foreach ( $related_posts as $post ) {
setup_postdata( $post ); // Prepare post data for WordPress functions
echo '<div class="related-post">';
echo '<h3><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></h3>';
echo '<p>' . get_the_excerpt( $post->ID ) . '</p>';
echo '</div>';
}
wp_reset_postdata(); // Reset after the loop
echo '</section>';
}
Don’t forget to hit “Update File” again.
What this does:
- Checks if $related_posts has any posts. If not, nothing is shown.
- Loops through each post and uses setup_postdata() so WordPress functions like get_the_title() and get_the_excerpt() work correctly.
- Displays the post title as a clickable link and the excerpt below it.
- Wraps the whole block in a <section> with a class .related-posts for styling.
- Calls wp_reset_postdata() at the end to make sure the rest of the page works normally.
Where to add it:
- Astra: Inside the PHP block you created after astra_primary_content_bottom(). This ensures it appears after the main content and before the closing </div><!– #primary –>.

- Other themes: Add this inside your new related posts section after fetching $related_posts.
Step 5: Test on your site
Open your website in a new browser tab. Go to any single blog post (not a page). Scroll to the bottom of the post content.
You should now see a Related Posts section showing up under the post, with titles and excerpts pulled from the same category. Like this:

If the post has no other posts in that category, the section may appear empty, which is normal.
And that is it! At this point, the related posts section is fully working. You’ve seen exactly where the code goes, what each part is doing, and how get_posts ties everything together. From here, you can adjust the number of posts, change what you show inside the loop, or refine the query to better match your content.
How Cloudways Optimizes WordPress get_posts Queries
By itself, get_posts is already a lightweight way to fetch content. But once your site grows and you start running multiple custom queries across templates, performance becomes a real concern. Every get_posts call still hits the database unless something is caching the result.
This is where your hosting setup starts to matter.
How Cloudways Improves Query Performance
At Cloudways, we focus on overall WordPress performance, ensuring your site runs fast and efficiently. This naturally benefits queries like get_posts, without needing extra manual optimization for each individual function.
1. Built-in caching layers
We implement multiple caching layers at the server level. That means when a get_posts query runs, the results don’t always hit the database directly. Pages and sections are cached, so repeated queries are served faster, reducing load time for your visitors.
2. Optimized PHP and database stack
Our platform runs modern PHP versions with database settings tuned for WordPress. Since get_posts relies on PHP execution and MySQL queries, this optimization ensures your custom loops execute efficiently, even when multiple queries appear on the same page.
3. Object caching with Redis
For larger or more dynamic sites, Cloudways supports object caching via Redis. This allows results from get_posts queries to be stored in memory. If the same query runs again, WordPress fetches data from cache instead of querying the database again, which speeds up sections like related posts or category lists.
Why this matters for your site
Even simple get_posts usage, like our related posts mini project, benefits from these optimizations. On real-world sites, multiple sections often rely on custom queries across pages and templates. With Cloudways, these queries stay fast without you needing to over-optimize your code manually.
Best Practices and Common Pitfalls
Using get_posts in WordPress is simple, but a few things can slow down your site if you’re not careful.
1. Don’t run too many queries in templates
Each get_posts call hits the database. If you run it inside loops that execute multiple times on a page, the site will slow down. Grab the posts once and reuse them if possible.
2. Cache repeated queries
If you need the same results in multiple places, caching them with a plugin or server-side caching avoids hitting the database every time.
3. Limit how many posts you fetch
Only fetch as many posts as you actually need, like 3–5 related posts. Fetching too many can make the page sluggish.
4. Keep queries targeted
Request only the data you need, like titles, excerpts, and links. Avoid fetching all metadata or comments unless it’s required.
Common mistakes to watch out for:
- Forgetting wp_reset_postdata() after loops using get_posts. This can break other WordPress functions on the page.
- Showing the current post in the related posts list. Always exclude it.
- Pulling unnecessary data like all custom fields or images for every post. This can impact performance.
Conclusion
We’ve walked through how to use wordpress get_posts to display related posts, structure the query, loop through the results, and show content dynamically. You also learned where to place this code in a theme like Astra and what to watch out for when building custom queries.
The mini project puts everything into practice, letting you add a functional related posts section without relying on plugins. Along the way, you handled categories, excluded the current post, and looped through results safely.
We also covered how Cloudways can help optimize WordPress performance and looked at best practices and common pitfalls when working with queries.
If you have any questions or need further help, feel free to get in touch in the comments below.
Frequently Asked Questions
Q1. Can I use get_posts to fetch posts from multiple categories at once?
Yes, you can. Instead of passing a single category ID, you can provide an array of IDs using the
category parameter or use category__in in your $args.
This allows you to fetch posts that belong to any of the specified categories.
Q2. Does get_posts work with custom post types?
Yes. While get_posts() fetches standard posts by default, you can query custom post
types by using the post_type parameter. For example,
'post_type' => 'portfolio' will retrieve posts from a custom Portfolio post type.
Q3. How do I exclude specific posts from a get_posts query?
You can exclude posts by passing their IDs using the exclude parameter in your
$args array. For example,
'exclude' => array(12, 45) will skip posts with IDs 12 and 45.
Abdul Rehman
Abdul is a tech-savvy, coffee-fueled, and creatively driven marketer who loves keeping up with the latest software updates and tech gadgets. He's also a skilled technical writer who can explain complex concepts simply for a broad audience. Abdul enjoys sharing his knowledge of the Cloud industry through user manuals, documentation, and blog posts.