← All posts

Why most WordPress directory plugins stop scaling past 5,000 listings

Faceted search built on meta_query degrades the moment your listing count crosses 5,000. Here's the architecture decision that lets Listora hold sub-100ms response past 100,000 listings.

The 5,000-listing wall

Directory plugins start fast. The first 200 listings render quickly because WordPress is fundamentally a CMS - reading 200 posts with a few meta values is what it’s good at. Search works. Filters work. The site feels snappy.

Then the listing count grows. By 2,000 listings, search starts feeling slow. By 5,000, the faceted filters take 3-4 seconds to return. By 10,000, the directory is unusable for any query more specific than “all listings.”

The cause is almost always the same: the plugin is running its search through meta_query.

Why meta_query doesn’t scale

meta_query joins the wp_posts table against wp_postmeta once per filter. A query like:

Restaurants in Austin with rating >= 4 and price = $$ and open_now = true

runs four JOINs against postmeta. Each JOIN is N rows where N is the total meta-value count across all listings. At 5,000 listings with 30 meta keys each, that’s 150,000 rows joined four times. Even with indexes, the planner has to scan through them.

WordPress was designed for content sites where meta is read once per post-fetch, not for directory sites where meta is the filter axis.

The fix: a denormalized search index

The correct architecture for a directory at scale is a dedicated, denormalized search-index table. One row per listing, every searchable + filterable attribute as a column (or a FULLTEXT-indexed text field), built at the time the listing is created or edited.

A search query then runs against a single table:

SELECT id FROM listora_search_index
WHERE category = 'restaurant'
  AND city = 'austin'
  AND rating >= 4
  AND price_band = '$$'
  AND open_now = 1
LIMIT 20;

One table, one scan, no JOINs. Indexes do the heavy lifting. Sub-100ms response at 100,000 rows.

What Listora does differently

Listora ships a dedicated listora_search_index table that’s populated by an indexer that runs:

  • On listing publish (synchronous, the listing is searchable the moment it goes live)
  • On listing edit (synchronous)
  • On bulk operations via Action Scheduler (background, no blocking)
  • On admin command via wp listora reindex (full rebuild)

Geo queries run against a separate listora_geo table indexed with (latitude, longitude). Haversine distance is computed in SQL with the index reducing the candidate set first.

Full-text search uses MySQL’s native FULLTEXT index on the search-index table’s title + content + tags columns.

The result: a directory that holds sub-100ms response past 100,000 listings. Tested on AWS RDS m5.large + a single Cloudways application instance. No premium infrastructure required.

Why this matters for your business case

A directory plugin that hits a 5,000-listing wall is a business problem, not a technical one. You can’t run paid acquisition that targets growth beyond 5,000 listings if your search times out. You can’t pitch the directory as a Yelp competitor if Yelp loads in 200ms and yours takes 4 seconds.

The architecture decision happens once, at install. Picking a plugin that ships with meta_query-based search is a decision that limits your business to the first 5,000 listings. Picking one with a denormalized index removes that ceiling.

Action Scheduler for the rest

Search is the obvious scaling bottleneck. The less-obvious ones are background jobs:

  • Sending listing-expiration emails
  • Reindexing on bulk updates
  • Cron-driven cleanup
  • Featured-rotation cycles

WordPress’s built-in WP-Cron is request-driven. On a busy site, it fires erratically. On a low-traffic site, it doesn’t fire at all. On a high-traffic site, it fires too often and slows down page requests.

Listora vendors WooCommerce’s Action Scheduler library. Every background job runs on AS, not WP-Cron. Jobs queue, retry on failure, scale horizontally. Same architecture that powers WooCommerce stores at the largest scale.

This is the kind of decision that doesn’t matter on day one and matters every day after.

TL;DR

If you’re picking a directory plugin and the listing count will plausibly grow past 5,000, the only question that matters is: does the search run on meta_query or on a dedicated index table? Test it with the wp-cli command to bulk-create 10,000 demo listings and run a few facet queries. The plugin that holds up is the one to build on.

Listora was designed around this exact constraint from day one. The search index is in the free plugin, not gated behind a Pro tier.

Try it: download Listora free and run wp listora demo seed --pack=all to load 1,000 listings. Then bulk-create 10x more. The search stays under 100ms.