Back to BlogTroubleshooting

Property Portal Slow With Thousands of Listings: Diagnose and Fix

Rupak Amin

Founder & Lead Engineer, RAITHub

8 min read

RAITHub ships and tests production software. See QA as a Service or talk to us.

A property portal slows at scale for three reasons: OFFSET pagination, so deep pages scan every earlier row; search filters (city, price, bedrooms) with no matching index, so each query scans the table; and one extra query per listing card. Measure one slow request first, then fix the cause it points to. Keyset pagination and a composite index handle most of it.

If you would rather have it diagnosed and fixed for you, see how RAITHub would fix this below. First, how to find the cause.

Why does a property portal get slow only after it grows?

Because the queries that are instant on a few hundred listings scale badly. A full table scan on 300 rows is invisible; on 30,000 it is a visible pause; on 300,000 it times out. The database was never the bottleneck during the demo, so nobody indexed for it. The same thing happens to any data-heavy app as it grows (why a Next.js app gets slow in production), but property portals hit it early because every visitor filters and paginates a large, shared listings table.

Start by measuring one real request, not the whole app. Find the slowest endpoint in your host's analytics, then run its query with EXPLAIN ANALYZE in Postgres. A Seq Scan on a large table, or a row count far larger than what the page shows, tells you where the time goes.

SymptomLikely causeFirst check
First pages fast, deep pages slowOFFSET paginationDoes the query use OFFSET that grows with the page number?
Every filtered search is slowNo index on the filter columnsDoes EXPLAIN show a Seq Scan on listings?
Results page slow, each card does a queryN+1 on the listCount queries per request in the database log
Map view slow with many pinsUnbounded result setIs the query limited to the current map bounds?
Trivial queries all slowDatabase far from the serverCompare the function region with the database region

Why is OFFSET pagination slow on large tables?

Because LIMIT 20 OFFSET 10000 still reads the first 10,000 rows, orders them, then throws them away and returns the next 20. The deeper the page, the more work. On an infinite-scroll portal where crawlers and users page far in, this dominates. Keyset pagination (also called cursor or seek pagination) fixes it: instead of an offset, you remember the last row seen and ask for rows after it, which the index can jump straight to.

-- Slow: OFFSET scans and discards every earlier row.
SELECT id, title, price, created_at
FROM listings
WHERE city = 'Berlin' AND status = 'published'
ORDER BY created_at DESC, id DESC
LIMIT 20 OFFSET 10000;

-- Fast: keyset. Pass the last row's (created_at, id) as the cursor.
SELECT id, title, price, created_at
FROM listings
WHERE city = 'Berlin' AND status = 'published'
  AND (created_at, id) < ('2026-09-01 10:00:00+00', '…last-id…')
ORDER BY created_at DESC, id DESC
LIMIT 20;

The trade-off is honest: keyset pagination gives "next" and "previous", not "jump to page 500". For a listings feed that is the right trade; users scroll, they do not type a page number. Keep OFFSET only where a real page index matters, such as an admin table of a few thousand rows.

Which indexes does a listings table actually need?

Index the columns you filter and sort by, in the order the query uses them, and include the sort tiebreaker so the index satisfies the whole ORDER BY. For the query above, a composite index on the filter plus the sort keys lets Postgres return the page without a separate sort step.

-- Covers the WHERE filter and the ORDER BY in one index.
CREATE INDEX listings_city_status_recent
  ON listings (city, status, created_at DESC, id DESC);

-- Price-range and bedroom filters are common too; add indexes that
-- match your real queries, not every column speculatively.
CREATE INDEX listings_city_price
  ON listings (city, status, price);

Confirm it works by re-running EXPLAIN ANALYZE: you want an Index Scan, not a Seq Scan, and a small number of rows read. The wider principles, including when a partial or covering index helps and how to read a query plan, are in the PostgreSQL indexing guide. Do not add an index per column by reflex; each one slows writes and most go unused.

How do you stop one query per listing card?

An N+1 query is one query for the list of listings, then one more for each card's agent, photos or price history. Twenty cards become 21 or more round trips. Fetch the related data in the first query with a join or an ORM include/select, run independent queries in parallel, and cache the filter facets (cities, price bands) that rarely change. On a Next.js portal, serve the results page with caching so crawlers and repeat searches do not re-run the database every time; the pattern is in the production performance guide. For the map view, bound every query to the visible map rectangle so it never tries to return all listings at once.

Buy, build or hire?

OptionWhat you getChoose this when
A managed search service (hosted search engine)Fast faceted search and typo tolerance, kept in sync with your databaseSearch relevance and filters are the core problem, not just pagination
A quick-fix yourselfKeyset pagination plus the two or three indexes your slow queries needOne or two endpoints are slow and you can read a query plan
A custom performance passMeasured diagnosis, indexes, keyset pagination, caching and a load testThe portal is slow across the board and you cannot pinpoint why
A managed team on the whole platformThe build plus the regression and load tests that keep it fastYou are scaling the product and need it to stay fast as listings grow

How long does a fix take, and what is the risk of doing it yourself?

If you can read a Postgres query plan and already use an ORM, swapping one feed to keyset pagination and adding the matching indexes is roughly a half-day to two days of work per slow endpoint, plus a load test. The main risk of doing it yourself is adding indexes by guesswork: speculative indexes slow every insert and update, bloat the database, and often do not match the query planner's choice. Measure the real slow query first, add the one index it needs, and confirm with EXPLAIN ANALYZE.

How RAITHub would fix this

  • Scope: measure the slowest listing and search endpoints; convert deep OFFSET feeds to keyset pagination; add the composite indexes the real queries need; remove N+1 queries on the results and map views; add caching for public pages and filter facets.
  • Timeline: a focused performance pass sits in the 2 to 4-week code-rescue range; a larger scale-up is phased alongside a fixed-scope build.
  • What you receive: before-and-after query plans and timings, a load test, the fixes with regression tests in CI, handover docs, the work on accounts you own, IP assigned to you and an NDA as standard.
  • Next step: a free 15-minute technical audit, then a fixed written quote.

RAITHub built BlockEstate, a multi-tenant listings and inquiry platform delivered as a 6-week MVP (no MLS integration), and PropDesk, property-management software with 1,024 tests. See the SaaS development service and the real-estate industry page, or read the related incident posts on a double-booked viewing and outgrowing spreadsheets. For a direct comparison with a tenant-per-agency portal, see isolation for multi-agency portals. To get the slow URL diagnosed, book the free 15-minute audit.

Frequently asked questions

Why does my property portal slow down as listings grow?

Usually three causes stack up: OFFSET pagination that re-scans earlier rows on deep pages, filter columns with no matching index so every search scans the table, and one query per listing card on the results page. Each is invisible on a few hundred rows and painful on tens of thousands.

What is keyset pagination?

Instead of OFFSET, you pass the last row seen as a cursor and ask for rows after it, which the index jumps straight to. It stays fast at any depth. The trade-off is that it gives next and previous rather than jump-to-page, which suits a scrolling listings feed.

Which index should I add to a listings table?

A composite index on the columns you filter and sort by, in query order, including the sort tiebreaker, for example city, status, created_at, id. Confirm with EXPLAIN ANALYZE that it produces an index scan. Add indexes that match real slow queries, not one per column.

Do I need a dedicated search engine like Elasticsearch?

Not for pagination and simple filters: Postgres with the right indexes handles those. A hosted search service pays off when relevance ranking, typo tolerance and complex faceted search are the core requirement. Fix the database queries first, then decide.

How do I know if N+1 queries are the problem?

Count the queries per request in your database log while loading the results page. If the number grows with the number of cards shown, you have an N+1. Fetch the related data in the list query with a join or ORM include, and run independent queries in parallel.

property portal performancekeyset paginationpostgres indexproptechn+1 queriesslow search

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.