Editor’s note: This article was updated in July 2026 to reflect the latest Meilisearch features and APIs, including hybrid and semantic search, conversational search and RAG, AI-powered retrieval, revised setup and authentication workflows, updated JavaScript SDK usage, and current best practices.
Meilisearch is an open-source search engine that supports keyword, semantic, and hybrid search through a single API. In this tutorial, you’ll learn how to set up Meilisearch, index and search documents, customize ranking rules, and explore its AI-powered retrieval capabilities.
Meilisearch began as a fast, typo-tolerant, open-source search engine you ran from a single binary. It still is. But it has grown into a full retrieval engine: keyword, semantic, and hybrid search from one API, on the same Rust core.
That evolution matters now that search has become a core part of AI infrastructure, powering retrieval-augmented generation (RAG), copilots, and other AI applications. Rather than combining a traditional search engine, a vector database, and a separate RAG framework, Meilisearch brings these capabilities together through a single API. The feel hasn’t changed, though: one Cloud project (or a single binary), one API, and results in milliseconds.
In this tutorial, we’ll cover everything from setting up Meilisearch and indexing documents to customizing ranking rules and exploring newer capabilities like hybrid search, conversational search, and AI-powered retrieval.
| Past | Present |
|---|---|
| Primarily a fast, typo-tolerant full-text search engine | Supports keyword, semantic, and hybrid search |
| Focused on traditional keyword search | Built-in embeddings and semantic retrieval |
| Search API only | Conversational search and RAG through the /chats endpoint |
| Basic search relevance tuning | Expanded ranking rules and customizable relevance pipeline |
| Separate tooling often needed for recommendations and AI retrieval | Built-in recommendations (/similar) and AI retrieval capabilities |
| Single binary or self-hosted deployment | Self-hosted or Meilisearch Cloud with the same API |
The quickest way to get started is with Meilisearch Cloud, the managed version of Meilisearch. It provides a 14-day free trial, a web dashboard, and a search preview, so you can start building without managing any infrastructure. Create a project at cloud.meilisearch.com.
Once your project is ready, copy the project host URL and API key from the overview page. Authentication now uses a standard Authorization: Bearer header (the old X-Meili-API-Key header is no longer used). You can verify that your project is reachable by listing its indexes:
curl 'MEILISEARCH_URL/indexes' \ -H 'Authorization: Bearer MEILISEARCH_API_KEY'
# => { "results": [], "total": 0, "offset": 0, "limit": 20 }
If the request succeeds, you’re ready to start indexing documents.
Prefer to run Meilisearch yourself? Since it’s fully open source, you can self-host it with a single command, and the API remains identical from this point onward.
# Install and launch (a master key is mandatory in production) curl -L https://install.meilisearch.com | sh ./meilisearch --master-key="aSampleMasterKey"
By default, a self-hosted instance runs on localhost:7700. You can also install Meilisearch using Docker, Homebrew, or APT; see the installation guide for additional options. Your master key serves the same purpose as the Cloud API key used earlier.
Once your Meilisearch instance is running, install the JavaScript client and initialize it with your project URL and API key:
npm install meilisearch
import { Meilisearch } from 'meilisearch'
const client = new Meilisearch({
host: 'MEILISEARCH_URL',
apiKey: 'MEILISEARCH_API_KEY'
})
If you’re updating code from an older Meilisearch project, note that the JavaScript client has changed slightly. The client is now a named export, and the class is Meilisearch (MeiliSearch still works as an alias). However, the older CommonJS syntax:
const MeiliSearch = require('meilisearch')
is no longer supported.
In Meilisearch, an index is a collection of documents. Each index has a primary key, which uniquely identifies every document. If your data already contains an id field, Meilisearch detects and uses it automatically.
We’ll use a sample movies dataset, where each document is represented as plain JSON:
{
"id": 287947,
"title": "Shazam!",
"overview": "A boy is given the ability to become an adult superhero...",
"genres": ["Action", "Comedy", "Fantasy"],
"release_date": "2019-03-23"
}
You can add the documents to an index like this:
import movies from './movies.json'
const task = await client.index('movies').addDocuments(movies)
console.log(task) // { taskUid: 0, status: 'enqueued', ... }
Indexing is asynchronous, so instead of returning a completed result, Meilisearch returns a task (called an updateId in older versions). You can monitor its progress using the returned taskUid:
await client.tasks.getTask(0) // status moves enqueued -> processing -> succeeded
Calling client.index('movies') automatically creates the index the first time you write to it, so you rarely need to call createIndex() yourself. If you’re updating older code, note that listIndexes() has been renamed to getIndexes().
Once your documents are indexed, you can search them using the search() method:
const results = await client.index('movies').search('botman')
/*
{
hits: [
{
id: 29751,
title: 'Batman Unmasked: ...'
},
...
],
query: 'botman',
processingTimeMs: 12,
limit: 20,
offset: 0,
estimatedTotalHits: 66
}
*/
Even though the query contains a typo, botman still returns results for Batman because typo tolerance is enabled by default. In this example, the search also completes in just 12 ms without any additional tuning.
The response includes several useful fields you’ll use frequently:
hits: The matching documentsestimatedTotalHits: An approximate count of matching documents, replacing the older nbHits fieldprocessingTimeMs: The time taken to process the search requestlimit and offset: Values used for paginationUpdating documents works much like adding them. If you send a document with an existing primary key, Meilisearch updates only the fields you provide, leaving the remaining fields unchanged.
await client.index('movies').updateDocuments([
{
id: 287947,
overview: 'Corrected synopsis text.'
}
])
Like indexing, updating documents is an asynchronous operation, so the request returns a task that you can track using its taskUid.
Ranking rules determine the order in which Meilisearch returns search results. You can reorder the built-in rules, remove them, or define custom ranking rules for each index to better match your application’s search experience.
The current default ranking rules are:
[ "words", "typo", "proximity", "attributeRank", "sort", "wordPosition", "exactness" ]
If you’re updating an older project, note that the defaults have changed since 2021. The sort rule was added, attribute was split into attributeRank and wordPosition, and the overall order was updated.
You can reorder the rules like any other Meilisearch setting:
await client.index('movies').updateRankingRules([
'words',
'typo',
'proximity',
'attributeRank',
'sort',
'wordPosition',
'exactness'
])
For example, moving exactness higher in the list prioritizes precise matches, while moving it lower favors broader recall. Because the rules are evaluated in order, even small changes to their sequence can noticeably affect the relevance of your search results. See the relevancy documentation for more details on each ranking rule.
This is one of the biggest additions since 2021. In addition to traditional keyword search, Meilisearch now supports semantic search using embeddings and can combine both approaches in a single query.
To enable semantic search, configure an embedder for your index. Meilisearch generates and caches embeddings for your documents automatically, with support for providers including OpenAI, Cohere, Mistral, Gemini, Voyage, Bedrock, Hugging Face, Ollama, or any compatible REST API.
curl -X PATCH 'MEILISEARCH_URL/indexes/movies/settings/embedders' \
-H 'Authorization: Bearer MEILISEARCH_API_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{
"movies-openai": {
"source": "openAi",
"model": "text-embedding-3-small",
"apiKey": "OPEN_AI_API_KEY",
"documentTemplate": "A movie titled {{doc.title}}: {{doc.overview}}"
}
}'
Once the embedder is configured, you can perform a hybrid search. The hybrid option’s semanticRatio parameter controls the balance between keyword matching (0) and semantic search (1):
await client.index('movies').search('space adventure with a robot', {
hybrid: {
embedder: 'movies-openai',
semanticRatio: 0.5
}
})
With a hybrid search, Meilisearch can return relevant results even when the query terms don’t appear exactly in your documents, while still preserving the precision of keyword matching.
Meilisearch also includes built-in support for retrieval-augmented generation (RAG) through a single /chats endpoint. When a user asks a question in natural language, Meilisearch retrieves relevant documents from your indexes and passes them to the LLM you configure, which generates a response grounded in those documents with source citations. That means you don’t need to wire together a separate vector database or orchestration layer yourself.
The endpoint is OpenAI-compatible, so you can use the OpenAI SDK, the Vercel AI SDK, or any similar client by pointing it directly at your Meilisearch instance (streaming is required):
import OpenAI from 'openai'
const openai = new OpenAI({
baseURL: 'MEILISEARCH_URL/chats/cloud',
apiKey: 'MEILISEARCH_API_KEY'
})
const stream = await openai.chat.completions.create({
model: 'PROVIDER_MODEL_UID',
stream: true,
messages: [
{
role: 'user',
content: 'What movies are about artificial intelligence?'
}
]
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '')
}
Beyond question answering, the same endpoint also supports multi-turn conversations, one-shot summaries, and retrieval-only workflows that you can integrate into your own applications. Since LLMs can still hallucinate, Meilisearch includes guardrails and system prompt controls to help keep responses grounded in your indexed data. See the conversational search documentation for more details.
Search relevance isn’t always enough. In some cases, you may want to promote specific results for business reasons, such as featuring a product during a sale or surfacing a help article for a common support query.
Meilisearch’s dynamic search rules let you pin selected documents to fixed positions whenever a query or time-based condition matches, without changing the underlying organic ranking. It’s an experimental feature that you enable once before adding rules.
# Enable the feature (once per project)
curl -X PATCH 'MEILISEARCH_URL/experimental-features' \
-H 'Authorization: Bearer MEILISEARCH_API_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{ "dynamicSearchRules": true }'
# Pin a document to the top when a query contains "invoice"
curl -X PATCH 'MEILISEARCH_URL/dynamic-search-rules/invoice-help' \
-H 'Authorization: Bearer MEILISEARCH_API_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{
"active": true,
"conditions": [
{
"scope": "query",
"contains": "invoice"
}
],
"actions": [
{
"selector": {
"indexUid": "support",
"id": "billing-workspace-overview"
},
"action": {
"type": "pin",
"position": 0
}
}
]
}'
The embedders used for hybrid search can also power recommendations. For example, the /similar endpoint returns documents that are semantically similar to a given item, making it useful for features such as “More like this” or “Customers also viewed.”
curl -X POST 'MEILISEARCH_URL/indexes/movies/similar' \
-H 'Authorization: Bearer MEILISEARCH_API_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{ "id": 192, "embedder": "movies-openai" }'
Meilisearch also supports personalization by reranking results in real time using a short preference prompt, allowing different users searching for the same term to see results ordered according to their individual preferences.
One advantage of Meilisearch is that it brings together capabilities that often require multiple systems. Traditional search, vector search, hybrid search, recommendations through /similar, and conversational retrieval through /chats all run on the same Rust core, over the same indexes, and behind the same API.
That means the index powering your application’s search experience can also serve as the retrieval layer for AI applications, reducing the need to maintain separate search and vector infrastructures.
What made Meilisearch compelling in 2021 still holds true: you can get started in minutes, index your data, return results in milliseconds, and fine-tune relevance when you need to.
Since then, Meilisearch has expanded well beyond traditional full-text search. Today, it supports semantic and hybrid search, retrieval-augmented generation (RAG), recommendations, and merchandising, all through the same API and on the same engine. Whether you choose Meilisearch Cloud or self-host the open-source version, the development experience remains largely unchanged.
This tutorial covered the core features to help you get started, but there’s much more to explore. For more advanced capabilities like custom hybrid ranking, geosearch, filtering and faceting, and multilingual search, refer to the Meilisearch documentation.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

I migrated 20 production-style components from Tailwind to StyleX. Here’s what the data showed about LOC, CSS bundle size, build time, and type safety.

A guide for using JWT authentication to prevent basic security issues while understanding the shortcomings of JWTs.

Discover how to build, render, and automate product demo videos with Remotion, replacing traditional screen recordings with reusable React code.

Chrome’s Modern Web Guidance embeds modern web platform skills into AI coding agents, helping them choose native HTML, CSS, and browser APIs over legacy patterns.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now