Every SaaS team building search makes the same decision in the wrong order.
They reach for Elasticsearch because that's the canonical answer, or wire up Algolia before testing whether Postgres could handle the workload. Or, most commonly, they bolt ILIKE '%query%' onto a text field, which works at a hundred records and produces a spinning cursor at a hundred thousand.
I've wired up search across enough real SaaS products that the failure modes are familiar. The split between "Postgres is fine" and "you need a dedicated engine" is narrower than most posts suggest, and far more specific than "just use Algolia."
This is the decision tree I actually use.
When is Postgres full-text search actually enough?

Postgres FTS handles small-to-medium catalogs from your existing database, with zero new infrastructure to operate or fail.
The Supabase team benchmarked Postgres FTS against MeiliSearch, Typesense, and OpenSearch on a 32MB movie dataset. At that size, Postgres matched the dedicated engines on query latency. The result matters less as a performance claim and more as a scope claim: if your dataset fits in RAM and your query rate is in the hundreds per second or below, you do not need a dedicated engine. A tsvector column, a GIN index, and a plainto_tsquery call around your input is the whole implementation.
The use cases it covers well: internal admin search (staff searching patient names, looking up order records), simple keyword matching on structured fields (ticket subjects, article titles, project names), and any path where relevance ranking does not differentiate your product.
You've felt the limitation before. User types "invoce" and gets nothing back. Thursday afternoon, they open a support ticket: "Why does search not find anything when I type wrong?" Postgres FTS has no typo tolerance out of the box. pg_trgm adds similarity matching, but at the cost of adding and tuning another extension — complexity on top of a tool that was not designed for this problem.
The cliff is precise. You hit it when you need any of these:
- Typo tolerance without installing additional extensions
- Prefix/instant-as-you-type search returning results on every keystroke
- Faceted filters with real-time counts updating as the user drills down
- Ranking tuned to click-through or conversion signals
- Languages that Postgres's bundled dictionaries do not cover (Japanese, Chinese, Korean)
When you're already running on Supabase + Stripe, to_tsvector + GIN index is an afternoon. For early-stage B2B SaaS where most tenants have under 50,000 records, this covers you well past your first paying customers. Build on it. Migrate when the gap shows up in your support queue, not before.
What does Typesense get right that Postgres FTS doesn't?

Typesense gives you typo-tolerant, sub-50ms search with native faceting and a self-contained binary — no JVM, no heap tuning, no managed cloud required.
Let me back up on that "sub-50ms" claim. Typesense's C++ engine stores the entire index in RAM. Warm queries on a running node return almost immediately. Cold queries on a freshly rebooted node take longer. The in-memory model produces stable, predictable latency in steady-state production, but it also means your node needs enough RAM to hold the full index. Typesense describes its design target as "instant search-as-you-type for data sets that fit in RAM, up to 24 TB."
What you actually get over Postgres FTS:
- Typo tolerance on by default — a user typing "resevation" gets "reservation" with zero configuration
- Real prefix search: type "inv" and results update character by character
- Field-level weighting without flattening everything into one
tsvector - Faceting with counts returned at query time, not via a separate GROUP BY
- Scoped API keys for per-tenant data isolation (more below)
- Vector and semantic search since 26.x — hybrid keyword + semantic retrieval in one system
Have you been writing custom correction logic to paper over Postgres FTS limitations? That's the migration signal right there.
On MeiliSearch. It sits between Postgres FTS and Typesense on the complexity spectrum. Easy to self-host, has a clean admin dashboard, comparable typo tolerance. But it is single-node only as of mid-2026 — no native clustering, no fault tolerance. A search service that cannot survive a node failure without downtime is a liability in production SaaS. Typesense's RAFT-based multi-node clustering is the specific reason it is the self-hosted default for SaaS teams that need real uptime.
Algolia vs Typesense: Where the cost difference becomes real

Algolia's free Build plan includes 1 million records and 10,000 searches per month. Past that free tier, the per-unit costs compound quickly.
| Metric | Algolia (Grow) | Typesense Cloud (4 GB) | Self-hosted Typesense | |--------|----------------|------------------------|----------------------| | Monthly base | Pay-as-you-go | ~$58/month | ~$25/month (VPS) | | Per 1K searches above free | $0.50 | Included in plan | Infrastructure only | | 5M searches/month (est.) | ~$2,500 | ~$58/month | ~$40/month | | Typo tolerance | ✅ | ✅ | ✅ | | Faceting with counts | ✅ | ✅ | ✅ | | AI recommendations | ✅ | ❌ | ❌ | | Visual merchandising dashboard | ✅ | ❌ | ❌ | | Self-hosted option | ❌ | ✅ | ✅ |
In October 2025, Algolia launched a Grow Plus tier adding AI synonyms, AI ranking, and advanced personalization to self-serve accounts. The per-search price on Grow Plus is $1.75 per 1,000 requests — 3.5x the base Grow rate. According to Algolia's announcement, the premium reflects inference cost on every query for running ML ranking models.
Algolia earns its price in one scenario: a merchandising team or an e-commerce catalog where AI ranking and click-through analytics directly drive conversion. Their globally distributed network handles over 1.75 trillion searches annually, and the 99.999% SLA is genuinely enterprise-grade — not a marketing number. That reliability matters when search downtime means lost revenue at scale.
For most multi-tenant SaaS tools in B2B — project management, clinic software, property management, CRM — those capabilities do not apply. You do not have a merchandising team. Search is not a conversion lever where AI ranking moves the needle. In that case, Typesense at $58/month for a production node is the better spend by a wide margin.
Multi-tenant search isolation: the part most tutorials skip
In multi-tenant SaaS, tenant A must never see tenant B's records in search results — no exceptions, no edge cases where a partial leak is acceptable.
This isolation is structural with Postgres FTS. Every query has a WHERE tenant_id = $1 filter. RLS policies on Supabase enforce it at the database level. You cannot accidentally break it without also breaking the query entirely.
With a dedicated search engine, you build the isolation explicitly. Three patterns, in ascending order of operational overhead:
-
Shared collection with scoped API keys. Store all tenants' data in one Typesense collection, embed a
filter_by=tenant_id:=<id>constraint inside an HMAC-signed scoped key, generate one per tenant at onboarding. Typesense cryptographically enforces the filter — the user cannot override it client-side, even with the key. Algolia offers the equivalent under the name Secured API Keys. This is the right default for most B2B SaaS with up to thousands of tenants. -
Separate collection per tenant. Hard isolation, simpler mental model, more schema management overhead. At a dozen tenants, invisible. At ten thousand tenants, you need automated collection provisioning and a migration playbook that runs across all collections simultaneously.
-
Separate cluster per tenant. Full isolation for regulated industries where contractual data-residency requirements apply. Cost scales linearly with tenant count — only justifiable for high-value enterprise accounts.
On Callidus, the multi-tenant clinic SaaS I built solo between February and April 2026, patient records live under tenants/{tenantId}/... in Firestore with isolation enforced through Firebase Security Rules and JWT claims. Callidus runs React + Firebase + Stripe Connect — not Postgres — so the FTS vs Typesense decision didn't arise directly. But the isolation requirement was identical. Every record query is scoped by tenantId. Fourteen clinics onboarded. Zero cross-tenant data issues.
BookBed, a Flutter + Firebase multi-tenant property management SaaS, followed the same pattern: Firestore queries scoped by tenant, no dedicated search engine at launch.
The rule I've landed on: use structural isolation — Postgres WHERE clauses or Firestore security rules — while you can get away with it. Add a dedicated engine with scoped keys when customer-facing search quality becomes the documented reason people churn.
Ranking signals most SaaS products ship without
Default Postgres ts_rank sorts by text relevance. Adequate for internal tooling. Not enough for customer-facing search where result quality is a surface users evaluate every session.
Three signals worth adding, ordered by implementation effort:
-
Recency. Prefer newer records when relevance scores are close. In Typesense: a
sort_byexpression combining_text_matchwithcreated_at. In Postgres: a manual expression in ORDER BY. -
Popularity. Weight results by view or click count. In Typesense: embed as a numeric field, reference in the sort expression. In Algolia: tracked automatically via analytics and fed back into ranking — one concrete thing Algolia's analytics pipeline does that Typesense does not replicate natively.
-
Field weighting. A title match should rank above a description match. In Typesense: per-field weights in the collection schema. In Postgres:
setweight()on concatenated tsvectors works, but collapses field boundaries and prevents independent field-level querying.
Faceting performance is the less-discussed scaling concern. Facets on a 50,000-record Typesense collection return sub-100ms with counts included in the same query. On Postgres, equivalent facet queries use GROUP BY and compete directly with your OLTP write load on the same connection pool. A workload separation decision, not a raw capability gap — but one that matters at production traffic levels.
The practical next step: run EXPLAIN ANALYZE on your current search query. Count your indexed records. Then type a misspelled version of a common search term into your own product. If Postgres returns nothing — or if you have correction logic already patching that gap — that's the migration signal. Typesense setup is an afternoon. The scoped-key multi-tenant isolation pattern is another half-day. Start there before evaluating Algolia.
