Medusa Search
In this guide, you'll learn about Medusa Search and how to use it in your Cloud projects.
What is Medusa Search?#
Medusa Search is a managed search service for Cloud projects. It stores your search indexes outside of your database and serves search queries for them.
Medusa Search gives your commerce application a search engine that serves storefront-grade search at scale, from full-text queries with relevance ranking to semantic search over embeddings. It's a low-cost yet highly performant solution, so you get sub-second search over large catalogs without paying for a dedicated search cluster or maintaining one.
Set Up Medusa Search with an AI Agent#
If you use an AI agent, such as Claude Code or Codex, ask it to fetch this page:
It receives a prompt version of this guide that walks it through confirming your project's setup, verifying your indexes, searching your products, and adding index definitions for your own data models. The agent runs what it can with the Cloud CLI and hands the rest back to you.
Get Started with Medusa Search on Cloud#
Medusa Search is enabled by default on Cloud for projects using Medusa v2.21.1+ with zero configuration. Medusa provisions the search resources for every Cloud environment and passes the credentials to your application, so it registers the provider and uses it for your indexes.
1. Upgrade Your Medusa Application#
To use Medusa Search, your Medusa application must be running v2.21.1 or later. If it isn't, upgrade it first, then deploy it to Cloud. Refer to the Update Medusa guide for the upgrade steps.
2. Define the Product Index#
Medusa doesn't declare any search index by default. If you installed your Medusa application after v2.21.1, it already has a product index definition. Otherwise, you need to define a product index in your Medusa application to make your products searchable.
Create the file src/search/product.ts with the following content:
1import {2 defineSearchIndex,3 graphConsume,4 graphSeed,5 search,6} from "@medusajs/framework/utils"7 8const source = {9 fields: [10 "id",11 "title",12 "description",13 "handle",14 "thumbnail",15 "status",16 "categories.id",17 "categories.name",18 "categories.handle",19 "tags.id",20 "tags.value",21 ],22 transform: (products) => {23 return products24 .filter((p) => p.status === "published")25 .map(({ status, ...product }) => product)26 },27}28 29export const productIndex = defineSearchIndex({30 name: "product",31 entity: "product",32 fields: search.define({33 id: search.keyword().filterable(),34 title: search.text().searchable({ weight: 3 }),35 description: search.text().searchable(),36 handle: search.keyword().filterable(),37 thumbnail: search.keyword(),38 categories: search39 .object({40 id: search.keyword().filterable(),41 name: search42 .keyword()43 .searchable()44 .facetable(),45 handle: search.keyword().filterable(),46 })47 .array(),48 tags: search49 .object({50 id: search.keyword().filterable(),51 value: search52 .keyword()53 .searchable()54 .facetable(),55 })56 .array(),57 }),58 events: [59 "product.created",60 "product.updated",61 "product.deleted",62 ],63 consume: graphConsume(source),64 seed: graphSeed(source),65})
The definition names the fields the engine holds, fills the index with graphSeed, and keeps it current with graphConsume. Add to it any core or custom field that your storefront searches, filters, sorts, or facets on.
3. Allow the Index on the Store Search Route#
Medusa exposes the POST /store/search API route, which searches any index of your application. However, an index isn't searchable through it until a middleware allows it, so a storefront never reaches an index you didn't expose.
To allow the product index, use the configureStoreSearch middleware in src/api/middlewares.ts:
The route narrows a product index to published products in the publishable API key's sales channels, and you can narrow any index further with the middleware's filters option. Learn more in the Store Search API Route guide.
4. Push and Deploy Your Changes#
The index definition and the middleware are part of your Medusa application's code, so Cloud picks them up on the next deployment.
Commit both files and push them to the branch that your environment tracks.
Cloud then creates a deployment for the commit. Once it finishes, Medusa runs the index migrations, creates the physical product index, and fills it with your products.
You can follow the deployment's progress in the Cloud dashboard, as explained in Find Environment Deployments.
5. Confirm Products as Indexed#
In your Medusa Admin dashboard, you can track the indexing of your products and other entities with the Search Module.
To view your indexes, go to Settings -> Search. Each registered index shows as a card with its name, the provider backing it, its status, and the fields it stores. Hover over a field to see its type and whether it's searchable, filterable, sortable, or facetable.
Find the product card and check its status, which is one of the following:
Status | Description |
|---|---|
Pending | Medusa created the index, but nothing has filled it yet. |
Building | A seed or reindex is in progress. Search results may be incomplete until it finishes. |
Ready | The index is serving documents, so you can search your products. |
Error | The last seed or reindex failed. |
Once the product index is Ready, your products are searchable.
6. Search Products#
You can search your indexed products by sending a POST request to the Store Search API route with the index to search in the entity field.
For example, to search for products matching sweatshirt:
The route accepts the q free-text query among the filters, along with pagination, sorting, facets, and the other options of a search query.
You'll receive a JSON response like the following:
1{2 "results": [3 {4 "hits": [5 {6 "id": "prod_01KXR3J9J610DT161E2E4ZS6P1",7 "score": 1.42,8 "document": {9 "id": "prod_01KXR3J9J610DT161E2E4ZS6P1",10 "title": "Medusa Sweatshirt",11 "description": "A classic sweatshirt, reimagined.",12 "handle": "sweatshirt"13 }14 },15 {16 "id": "prod_01KXR3J9J610DT161E2E4ZS6P3",17 "score": 1.18,18 "document": {19 "id": "prod_01KXR3J9J610DT161E2E4ZS6P3",20 "title": "Medusa Zip-Up Sweatshirt",21 "description": "A zip-up take on the classic sweatshirt.",22 "handle": "zip-up-sweatshirt"23 }24 }25 ],26 "metadata": {27 "skip": 0,28 "take": 20,29 "count": 2,30 "query": "sweatshirt",31 "processing_time_ms": 732 }33 }34 ]35}
The route answers with one result per posted query, each holding the matching hits ranked by relevance and the query's metadata. You can also post a batch of queries at once in a queries array.
Medusa Search Features#
Medusa Search provides the following features:
- Full-text search with relevance ranking that respects the weight you set on each field.
- Typo tolerance on every searchable field, which you opt into per query.
- Semantic search over embeddings, either ones you compute and store in your index, or ones Medusa Search creates for you from a text field.
- Flexible filters that support every operator the Search Module offers, so you can narrow results down to complex conditions, such as products of two brands that are in stock and priced under a certain amount.
- Facets for values, ranges, and aggregate statistics, so a storefront can show counts per category or price bucket.
- Highlighting of the matched terms in a hit's fields, so a storefront can mark what a result matched on.
- Sorting by any number of fields.
- Isolated indexes per environment, so a preview environment never writes to your production indexes.
- Settings of its own on an index, a field, and a query, such as the distance metric it compares embeddings with, or how current an index must be for a query.
- Search analytics that show what customers searched for, how often searches came back empty, and how fast Medusa Search responded.
Compare Medusa Search to Other Search Engines#
If you're considering or already using another search engine, refer to Compare Medusa Search to Other Search Engines for a summarized comparison of all of them. The following guides go into detail on one engine each, across setup, integration, search capabilities, and cost:
Search Indexes per Environment#
Each Cloud environment has its own search indexes, so a change in one environment never affects another. Medusa Search scopes them by the environment's handle. Preview environments are the exception, since they can start from another environment's data.
Indexes in Preview Environments#
A preview environment can start from another environment's indexes. When you set a base environment in the shared previews settings, Cloud branches that environment's indexes into every preview it creates afterwards, the same way it replicates the base environment's database. A preview of a catalog with tens of thousands of products is then searchable as soon as it deploys, with no seed or reindex of your own.
The branch is a copy, so a write in the preview changes only the preview's indexes. Your base environment's indexes stay as they were.
Without a base environment for search, a preview starts with no indexes, and Medusa creates them as your application first writes to them.
Index Location and Latency#
Medusa Search stores an environment's indexes in the same region as the environment's backend, so a search query never leaves the region. A query your Medusa application sends to Medusa Search takes single-digit milliseconds on the network, and a filtered query that also computes facets and hydrates its hits returns in around 30 milliseconds at the 95th percentile.
Two choices affect that number the most:
- The number of facets a query computes. Facets don't add to your search request count, but they do add to a query's latency, as explained in How Medusa Search Counts Search Requests.
- The
consistencyquery option. The defaultstrongvalue waits for every write made before the query started, whileeventualskips that check and returns faster.
Start Building with Search#
Use Search in Local Development#
In local development, Medusa registers the PostgreSQL Search Module Provider by default. This is the recommended approach for testing search functionality locally.
You can also set up your local Medusa instance to connect to Medusa Search with a connection string.
To get the connection string for your local Medusa instance:
- If you're in a different organization, switch to the organization.
- Click Projects in the sidebar and select the project that contains the environment you want to view.
- In the project's dashboard, click on the name of the environment. For example, "Production".
- Click Search in the sidebar under the environment's section.
- Toggle the "Search endpoint" setting. If the environment is Production, only the organization owner can toggle it.
- Copy the connection string that appears after toggling the "Search endpoint" setting.
Set the connection string as the MEDUSA_SEARCH_ENDPOINT environment variable in your local project:
Then, register the Search Module in medusa-config.ts with Medusa Search as its default provider, but only in development:
1const isDevelopment = process.env.NODE_ENV === "development"2 3module.exports = defineConfig({4 // ...5 modules: [6 // ...7 ...(isDevelopment8 ? [9 {10 resolve: "@medusajs/medusa/search",11 options: {12 default_provider: "search-medusa",13 cloud: {14 endpoint: process.env.MEDUSA_SEARCH_ENDPOINT,15 },16 },17 },18 ]19 : []),20 ],21})
The endpoint carries the API key and the environment handle, so Medusa Search needs no other option. Your indexes then read from and write to your Cloud environment's search resources instead of your local PostgreSQL database.
Search Custom Data Models#
To search data other than products, including the custom data models of your own modules, you can declare an index definition, then either allow the index on the /store/search API route or add an API route of your own that searches it.
For example, to index a custom brand data model, create the file src/search/brand.ts with the following content:
1import {2 defineSearchIndex,3 graphConsume,4 graphSeed,5 search,6} from "@medusajs/framework/utils"7 8const fields = ["id", "name", "country", "created_at"]9 10export const brandIndex = defineSearchIndex({11 name: "brand",12 entity: "brand",13 fields: search.define({14 id: search.keyword().filterable(),15 name: search.text().searchable({ weight: 3 }),16 country: search.keyword().filterable().facetable(),17 created_at: search.date().sortable(),18 }),19 events: [20 "brand.created",21 "brand.updated",22 "brand.deleted",23 ],24 consume: graphConsume({ fields }),25 seed: graphSeed({ fields }),26})
Refer to the Search Other Entities guide for more details.
Monitor Search Analytics#
An environment's Search page in the Cloud dashboard shows analytics for each of its indexes, including the searches that ran, the searches that returned no results, how fast Medusa Search responded, and the most searched terms.
Refer to the Medusa Search Analytics guide for more details.
Medusa Search Usage#
Medusa Search is available on all Cloud plans, with the exception of vector search, which is available on the Scale and Enterprise plans. The number of search requests your Medusa application makes is a metered resource. Every plan includes an allowance of search requests, and requests beyond it count as Flex Usage.
Medusa meters full-text and vector search requests separately, so each has its own allowance and price. A search that ranks hits with an embedding counts as a vector search request.
Refer to the Usage guide to learn how to monitor your organization's usage, and to the Plans & Pricing guide for the allowance your plan includes.
How Medusa Search Counts Search Requests#
Every query.search call counts as one search request, no matter how many facets it computes, whether it returns a count, or how many hits it returns. Medusa Search runs the hits, the count, and every facet of a call as one search.
Writes count toward the same allowance. Every record that Medusa Search writes to an index, whether it's created, updated, or deleted, counts as one search request. So, a seed that indexes 50,000 products costs 50,000 requests, and a reindex costs another one per record.
Reindex on Medusa Search#
Medusa rebuilds an index when its definition changes, and you can also rebuild one on demand. Admin users rebuild an index from Settings -> Search in the Medusa Admin dashboard, and you can rebuild one in code with the Search Module's reindex method.
Frequently Asked Questions#
Can I boost a field's value without sorting by it?#
Not yet. Medusa Search ranks hits by the weight you set on each searchable field, so you influence relevance through field weights rather than through the value of an attribute. To put in-stock or fast-delivery products first, sort by the field, which overrides the relevance order, or run a second search for the products that don't qualify and append them to the first search's hits.
Which languages does Medusa Search stem?#
Medusa Search stems eighteen languages, including Dutch and German. Stemming is off by default, so set a searchable field's language and stemming with the full_text_search field option to turn it on. Refer to the list of supported languages for more details.
Can I query a Cloud environment's indexes from my local project?#
Yes. Copy the environment's search endpoint from the Cloud dashboard and set it in your local project, as explained in Use Search in Local Development. The connection string carries read and write access to that environment's indexes, so use a preview or staging environment rather than production.