Skip to main content

Meilisearch vs Typesense vs Algolia: Search Engine APIs (2026)

·PkgPulse Team

TL;DR

Meilisearch is the open-source search engine — instant search, typo tolerance, faceted filtering, easy setup, Rust-powered, great developer experience. Typesense is the open-source search engine built for speed — typo tolerance, geo search, vector search, high availability, also Rust/C++. Algolia is the hosted search-as-a-service — instant search UI widgets, analytics, A/B testing, AI search, the industry standard for commercial search. In 2026: Meilisearch for easy self-hosted search, Typesense for fast open-source search with vector support, Algolia for enterprise search-as-a-service.

Key Takeaways

  • Meilisearch: meilisearch ~50K weekly downloads — open-source, Rust, instant search, easy setup
  • Typesense: typesense ~20K weekly downloads — open-source, fast, vector search, geo search
  • Algolia: algoliasearch ~500K weekly downloads — hosted, analytics, AI, InstantSearch widgets
  • Meilisearch and Typesense are open-source (self-hosted or cloud)
  • Algolia has the richest UI component library (InstantSearch)
  • All three handle typo tolerance out of the box

Meilisearch

Meilisearch — open-source instant search:

Setup and indexing

import { MeiliSearch } from "meilisearch"

const client = new MeiliSearch({
  host: "http://localhost:7700",
  apiKey: process.env.MEILI_MASTER_KEY,
})

// Create index and add documents:
const index = client.index("packages")

await index.addDocuments([
  { id: 1, name: "react", description: "UI library", downloads: 25000000, tags: ["frontend", "ui"] },
  { id: 2, name: "vue", description: "Progressive framework", downloads: 5000000, tags: ["frontend", "ui"] },
  { id: 3, name: "express", description: "Web framework", downloads: 30000000, tags: ["backend", "http"] },
  { id: 4, name: "fastify", description: "Fast web framework", downloads: 5000000, tags: ["backend", "http"] },
])

// Configure searchable attributes and ranking:
await index.updateSettings({
  searchableAttributes: ["name", "description", "tags"],
  filterableAttributes: ["tags", "downloads"],
  sortableAttributes: ["downloads", "name"],
  rankingRules: [
    "words", "typo", "proximity", "attribute", "sort", "exactness",
  ],
})
const index = client.index("packages")

// Basic search (typo-tolerant by default):
const results = await index.search("reac")
// Finds "react" despite typo

console.log(results.hits)
// [{ id: 1, name: "react", ... }]
console.log(results.estimatedTotalHits)
console.log(results.processingTimeMs)  // ~1ms

// With filters:
const filtered = await index.search("framework", {
  filter: ["tags = frontend", "downloads > 1000000"],
  sort: ["downloads:desc"],
  limit: 10,
  offset: 0,
})

// Faceted search:
const faceted = await index.search("", {
  facets: ["tags"],
})
console.log(faceted.facetDistribution)
// { tags: { frontend: 2, backend: 2, ui: 2, http: 2 } }

Highlighted results

const results = await index.search("web framework", {
  attributesToHighlight: ["name", "description"],
  highlightPreTag: "<mark>",
  highlightPostTag: "</mark>",
  attributesToCrop: ["description"],
  cropLength: 30,
})

results.hits.forEach((hit) => {
  console.log(hit._formatted?.name)
  // "express" or "<mark>web</mark> <mark>framework</mark>"
  console.log(hit._formatted?.description)
  // "<mark>Web</mark> <mark>framework</mark>"
})

React InstantSearch

import { InstantSearch, SearchBox, Hits, RefinementList } from "react-instantsearch"
import { instantMeiliSearch } from "@meilisearch/instant-meilisearch"

const { searchClient } = instantMeiliSearch(
  "http://localhost:7700",
  process.env.MEILI_SEARCH_KEY
)

function PackageSearch() {
  return (
    <InstantSearch searchClient={searchClient} indexName="packages">
      <SearchBox placeholder="Search packages..." />
      <RefinementList attribute="tags" />
      <Hits hitComponent={PackageHit} />
    </InstantSearch>
  )
}

function PackageHit({ hit }: { hit: any }) {
  return (
    <div>
      <h3>{hit.name}</h3>
      <p>{hit.description}</p>
      <span>{hit.downloads.toLocaleString()} downloads/week</span>
    </div>
  )
}

Typesense

Typesense — fast open-source search:

Setup and indexing

import Typesense from "typesense"

const client = new Typesense.Client({
  nodes: [{ host: "localhost", port: 8108, protocol: "http" }],
  apiKey: process.env.TYPESENSE_API_KEY!,
  connectionTimeoutSeconds: 2,
})

// Create collection with schema:
await client.collections().create({
  name: "packages",
  fields: [
    { name: "name", type: "string" },
    { name: "description", type: "string" },
    { name: "downloads", type: "int64" },
    { name: "tags", type: "string[]", facet: true },
    { name: "version", type: "string" },
  ],
  default_sorting_field: "downloads",
})

// Index documents:
await client.collections("packages").documents().import([
  { name: "react", description: "UI library", downloads: 25000000, tags: ["frontend", "ui"], version: "19.0.0" },
  { name: "vue", description: "Progressive framework", downloads: 5000000, tags: ["frontend", "ui"], version: "3.5.0" },
  { name: "express", description: "Web framework", downloads: 30000000, tags: ["backend", "http"], version: "5.0.0" },
])

Search

// Basic search:
const results = await client.collections("packages")
  .documents()
  .search({
    q: "reac",           // Typo-tolerant
    query_by: "name,description",
    sort_by: "downloads:desc",
    per_page: 10,
    page: 1,
  })

console.log(results.found)       // Total matches
console.log(results.search_time_ms)  // ~0.5ms
results.hits?.forEach((hit) => {
  console.log(hit.document.name, hit.text_match)
})

// Filtered search:
const filtered = await client.collections("packages")
  .documents()
  .search({
    q: "framework",
    query_by: "name,description",
    filter_by: "tags:=[frontend] && downloads:>1000000",
    sort_by: "downloads:desc",
    facet_by: "tags",
  })

console.log(filtered.facet_counts)
// [{ field_name: "tags", counts: [{ value: "frontend", count: 2 }] }]

Vector search (semantic)

// Create collection with vector field:
await client.collections().create({
  name: "packages-semantic",
  fields: [
    { name: "name", type: "string" },
    { name: "description", type: "string" },
    { name: "embedding", type: "float[]", num_dim: 384 },
  ],
})

// Index with embeddings:
await client.collections("packages-semantic").documents().import([
  {
    name: "react",
    description: "A JavaScript library for building user interfaces",
    embedding: [0.1, 0.2, ...],  // 384-dim vector
  },
])

// Vector search:
const results = await client.collections("packages-semantic")
  .documents()
  .search({
    q: "*",
    vector_query: "embedding:([0.1, 0.2, ...], k:10)",
  })

// Hybrid search (keyword + vector):
const hybrid = await client.collections("packages-semantic")
  .documents()
  .search({
    q: "UI library",
    query_by: "name,description",
    vector_query: "embedding:([0.1, 0.2, ...], k:10, alpha:0.5)",
  })
// Collection with geo field:
await client.collections().create({
  name: "events",
  fields: [
    { name: "name", type: "string" },
    { name: "location", type: "geopoint" },
    { name: "date", type: "string" },
  ],
})

// Search within radius:
const nearby = await client.collections("events")
  .documents()
  .search({
    q: "javascript meetup",
    query_by: "name",
    filter_by: "location:(37.7749, -122.4194, 10 km)",  // 10km from SF
    sort_by: "location(37.7749, -122.4194):asc",        // Nearest first
  })

Algolia

Algolia — search-as-a-service:

Setup and indexing

import algoliasearch from "algoliasearch"

const client = algoliasearch(
  process.env.ALGOLIA_APP_ID!,
  process.env.ALGOLIA_ADMIN_KEY!
)

const index = client.initIndex("packages")

// Index documents:
await index.saveObjects([
  { objectID: "1", name: "react", description: "UI library", downloads: 25000000, tags: ["frontend", "ui"] },
  { objectID: "2", name: "vue", description: "Progressive framework", downloads: 5000000, tags: ["frontend", "ui"] },
  { objectID: "3", name: "express", description: "Web framework", downloads: 30000000, tags: ["backend", "http"] },
])

// Configure index settings:
await index.setSettings({
  searchableAttributes: ["name", "description", "tags"],
  attributesForFaceting: ["tags", "filterOnly(downloads)"],
  customRanking: ["desc(downloads)"],
  typoTolerance: true,
  minWordSizefor1Typo: 3,
  minWordSizefor2Typos: 7,
})

Search

const searchClient = algoliasearch(
  process.env.ALGOLIA_APP_ID!,
  process.env.ALGOLIA_SEARCH_KEY!  // Search-only key
)

const index = searchClient.initIndex("packages")

// Basic search:
const { hits, nbHits, processingTimeMS } = await index.search("reac")

console.log(nbHits)           // Total matches
console.log(processingTimeMS)  // ~1ms

hits.forEach((hit) => {
  console.log(hit.name)
  console.log(hit._highlightResult?.name?.value)
  // "<em>reac</em>t" — auto-highlighted
})

// Filtered search:
const filtered = await index.search("framework", {
  filters: "tags:frontend AND downloads > 1000000",
  facets: ["tags"],
  hitsPerPage: 10,
  page: 0,
})

console.log(filtered.facets)
// { tags: { frontend: 2, backend: 2 } }

React InstantSearch

import algoliasearch from "algoliasearch/lite"
import {
  InstantSearch, SearchBox, Hits,
  RefinementList, Pagination, Stats,
} from "react-instantsearch"

const searchClient = algoliasearch(
  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
)

function PackageSearch() {
  return (
    <InstantSearch searchClient={searchClient} indexName="packages">
      <div className="flex gap-8">
        <aside className="w-64">
          <h3>Tags</h3>
          <RefinementList attribute="tags" />
        </aside>

        <main className="flex-1">
          <SearchBox placeholder="Search packages..." />
          <Stats />
          <Hits hitComponent={PackageHit} />
          <Pagination />
        </main>
      </div>
    </InstantSearch>
  )
}

function PackageHit({ hit }: { hit: any }) {
  return (
    <article>
      <h3>{hit.name}</h3>
      <p>{hit.description}</p>
      <div className="flex gap-2">
        {hit.tags?.map((tag: string) => (
          <span key={tag} className="badge">{tag}</span>
        ))}
      </div>
    </article>
  )
}

Analytics and A/B testing

import algoliasearch from "algoliasearch"

const client = algoliasearch(appId, adminKey)

// Enable analytics:
const index = client.initIndex("packages")
await index.setSettings({
  enablePersonalization: true,
  enableRules: true,
})

// Query rules (merchandising):
await index.saveRule({
  objectID: "promote-react",
  conditions: [{
    anchoring: "contains",
    pattern: "ui library",
  }],
  consequence: {
    promote: [{
      objectID: "1",  // react
      position: 0,
    }],
  },
})

// A/B test:
const response = await client.addABTest({
  name: "ranking-test",
  variants: [
    { index: "packages", trafficPercentage: 50, description: "Current" },
    { index: "packages_v2", trafficPercentage: 50, description: "New ranking" },
  ],
  endAt: "2026-04-01T00:00:00Z",
})

Feature Comparison

FeatureMeilisearchTypesenseAlgolia
Open-source❌ (proprietary)
Self-hosted❌ (cloud only)
Cloud offeringMeilisearch CloudTypesense Cloud✅ (primary)
Typo tolerance
Faceted search
Geo search
Vector search✅ (experimental)✅ (NeuralSearch)
InstantSearch UI✅ (via adapter)✅ (via adapter)✅ (native)
Analytics
A/B testing
Query rules✅ (overrides)
Multi-tenancy✅ (tenant tokens)✅ (scoped keys)✅ (API keys)
Written inRustC++C++
Indexing speedFastVery fastFast
Search latency~1-5ms~0.5-2ms~1-5ms
Free tierSelf-hostedSelf-hosted10K searches/mo

When to Use Each

Use Meilisearch if:

  • Want easy-to-setup open-source search
  • Need instant search with great developer experience
  • Building search for small to medium datasets
  • Want self-hosted search with minimal configuration

Use Typesense if:

  • Need the fastest open-source search engine
  • Want vector search for semantic/AI-powered search
  • Need geo search capabilities
  • Building search for large datasets with strict latency requirements

Use Algolia if:

  • Want fully managed search-as-a-service
  • Need analytics, A/B testing, and query rules
  • Building commercial search for e-commerce or SaaS
  • Want the richest InstantSearch UI component library

Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on Meilisearch v1.x, Typesense v0.25.x/v27.x, and Algolia v4.x/v5.x.

Compare search tooling and backend libraries on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.