Caching expensive queries and API responses with WordPress Transients
Transients are WordPress’s built-in caching layer. They store any serializable value in the database (or in an object cache like Redis/Memcached if one is installed) with an optional expiry time. They’re ideal for caching expensive queries, external API responses, or computed data.
Problem: How do you cache an expensive database query or external API response in WordPress so it runs only once per expiry period?
Solution: The three functions you need:
// Store a value for one hour
set_transient( 'my_transient_key', $data, HOUR_IN_SECONDS );
// Retrieve it (returns false if expired or not set)
$data = get_transient( 'my_transient_key' );
// Delete it manually
delete_transient( 'my_transient_key' );
A practical example — caching a slow database query:
function get_popular_posts() {
$cache_key = 'popular_posts';
$posts = get_transient( $cache_key );
if ( false !== $posts ) {
return $posts;
}
$posts = new WP_Query( [
'posts_per_page' => 10,
'orderby' => 'comment_count',
'order' => 'DESC',
'no_found_rows' => true,
] );
set_transient( $cache_key, $posts, 12 * HOUR_IN_SECONDS );
return $posts;
}
// Bust the cache when a post is updated
add_action( 'save_post', function() {
delete_transient( 'popular_posts' );
} );
WordPress defines these handy time constants: MINUTE_IN_SECONDS, HOUR_IN_SECONDS, DAY_IN_SECONDS, WEEK_IN_SECONDS, MONTH_IN_SECONDS, YEAR_IN_SECONDS.
NOTE: Transients with no expiry (0) are persistent — they stay until deleted. If you use a persistent object cache, transients never touch the database at all, which is significantly faster. Always invalidate transients when the underlying data changes.