> ## Documentation Index
> Fetch the complete documentation index at: https://www.meilisearch.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Federated search

> In this tutorial you will see how to perform a query searching multiple indexes at the same time to obtain a single list of results.

Meilisearch allows you to make multiple search requests at the same time with the `/multi-search` endpoint. A federated search is a multi-search that returns results from multiple queries in a single list.

In this tutorial you will see how to create separate indexes containing different types of data from a CRM application. You will then perform a query searching all these indexes at the same time to obtain a single list of results.

## Create three indexes

Download the following datasets: <a href="https://www.meilisearch.com/docs/assets/datasets/crm-chats.json" download="crm-chats.json">`crm-chats.json`</a>, <a href="https://www.meilisearch.com/docs/assets/datasets/crm-profiles.json" download="crm-profiles.json">`crm-profiles.json`</a>, and <a href="https://www.meilisearch.com/docs/assets/datasets/crm-tickets.json" download="crm-tickets.json">`crm-tickets.json`</a> containing data from a fictional CRM application.

Add the datasets to Meilisearch and create three separate indexes, `profiles`, `chats`, and `tickets`:

<CodeGroup>
  ```sh theme={null}
  curl -X POST 'MEILISEARCH_URL/indexes/profiles' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary @crm-profiles.json &&
  curl -X POST 'MEILISEARCH_URL/indexes/chats' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary @crm-chats.json &&
  curl -X POST 'MEILISEARCH_URL/indexes/tickets' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary @crm-tickets.json
  ```
</CodeGroup>

[Use the tasks endpoint](/docs/capabilities/indexing/tasks_and_batches/monitor_tasks) to check the indexing status. Once Meilisearch successfully indexed all three datasets, you are ready to perform a federated search.

## Perform a federated search

When you are looking for Natasha Nguyen's email address in your CRM application, you may not know whether you will find it in a chat log, among the existing customer profiles, or in a recent support ticket. In this situation, you can use federated search to search across all possible sources and receive a single list of results.

Use the `/multi-search` endpoint with the `federation` parameter to query the three indexes simultaneously:

<CodeGroup>
  ```bash cURL theme={null} theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/multi-search' \
    -H 'Authorization: Bearer API_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "federation": {},
      "queries": [
        {
          "indexUid": "chats",
          "q": "natasha"
        },
        {
          "indexUid": "profiles",
          "q": "natasha"
        },
        {
          "indexUid": "tickets",
          "q": "natasha"
        }
      ]
    }'
  ```
</CodeGroup>

Meilisearch should respond with a single list of search results:

<CodeGroup>
  ```json theme={null}
  {
    "hits": [
      {
        "id": 0,
        "client_name": "Natasha Nguyen",
        "message": "My email is natasha.nguyen@example.com",
        "time": 1727349362,
        "_federation": {
          "indexUid": "chats",
          "queriesPosition": 0
        }
      },
      …
    ],
    "processingTimeMs": 0,
    "limit": 20,
    "offset": 0,
    "estimatedTotalHits": 3,
    "semanticHitCount": 0
  }
  ```
</CodeGroup>

## Promote results from a specific index

Since this is a CRM application, users have profiles with their preferred contact information. If you want to search for Riccardo Rotondo's preferred email, you can boost documents in the `profiles` index.

Use the `weight` property of the `federation` parameter to boost results coming from a specific query:

<CodeGroup>
  ```bash cURL theme={null} theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/multi-search' \
    -H 'Authorization: Bearer API_KEY' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "federation": {},
      "queries": [
        {
          "indexUid": "chats",
          "q": "rotondo"
        },
        {
          "indexUid": "profiles",
          "q": "rotondo",
          "federationOptions": { "weight": 1.2 }
        },
        {
          "indexUid": "tickets",
          "q": "rotondo"
        }
      ]
    }'
  ```
</CodeGroup>

This request will lead to results from the query targeting `profile` ranking higher than documents from other queries:

<CodeGroup>
  ```json theme={null}
  {
    "hits": [
    {
      "id": 1,
      "name": "Riccardo Rotondo",
      "email": "riccardo.rotondo@example.com",
        "_federation": {
          "indexUid": "profiles",
          "queriesPosition": 1
        }
      },
      …
    ],
    "processingTimeMs": 0,
    "limit": 20,
    "offset": 0,
    "estimatedTotalHits": 3,
    "semanticHitCount": 0
  }
  ```
</CodeGroup>

## Paginate federated results

By default, federated search returns a limited number of results using `offset` and `limit`. If you need exhaustive pagination, use the `federation.page` and `federation.hitsPerPage` parameters instead. These work like traditional page-based pagination across the merged result set.

Send a federated search request with pagination:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/multi-search' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "federation": {
        "page": 2,
        "hitsPerPage": 10
      },
      "queries": [
        { "indexUid": "profiles", "q": "Nguyen" },
        { "indexUid": "chats", "q": "Nguyen" },
        { "indexUid": "tickets", "q": "Nguyen" }
      ]
    }'
  ```
</CodeGroup>

The response includes `page`, `hitsPerPage`, and `totalPages` instead of `offset`, `limit`, and `estimatedTotalHits`:

<CodeGroup>
  ```json theme={null}
  {
    "hits": [ … ],
    "processingTimeMs": 1,
    "page": 2,
    "hitsPerPage": 10,
    "totalHits": 25,
    "totalPages": 3
  }
  ```
</CodeGroup>

This makes it straightforward to build paginated UIs that display merged results from multiple indexes.

## Deduplicate results across indexes

The same client may appear in a profile, a chat log, and a support ticket at once. Use the `distinct` property of the `federation` parameter to keep at most one result per value of a given attribute, computed across every query in the request.

`distinct` requires the attribute to be filterable in each participating index, so add `client_name` to `filterableAttributes` for all three indexes first:

<CodeGroup>
  ```sh theme={null}
  curl -X PUT 'MEILISEARCH_URL/indexes/profiles/settings/filterable-attributes' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary '["client_name"]' &&
  curl -X PUT 'MEILISEARCH_URL/indexes/chats/settings/filterable-attributes' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary '["client_name"]' &&
  curl -X PUT 'MEILISEARCH_URL/indexes/tickets/settings/filterable-attributes' -H 'Content-Type: application/json' -H 'Authorization: Bearer MEILISEARCH_KEY' --data-binary '["client_name"]'
  ```
</CodeGroup>

Then set `distinct` inside `federation`:

<CodeGroup>
  ```bash theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/multi-search' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer MEILISEARCH_KEY' \
    --data-binary '{
      "federation": {
        "distinct": "client_name"
      },
      "queries": [
        { "indexUid": "profiles", "q": "Nguyen" },
        { "indexUid": "chats", "q": "Nguyen" },
        { "indexUid": "tickets", "q": "Nguyen" }
      ]
    }'
  ```
</CodeGroup>

Natasha Nguyen now occupies a single entry in the merged list instead of one per source.

Federation-level `distinct` behaves differently from the [index-level distinct attribute](/docs/capabilities/full_text_search/how_to/configure_distinct_attribute) in three ways:

* It applies across every query in the request, whatever index or remote they target
* It overrides the distinct attribute configured on each participating index
* Setting `distinct` on an individual query and on `federation` at the same time returns an HTTP 400 error

<Note>
  When queries reach remote instances, Meilisearch computes facet distribution as accurately as it can but cannot guarantee exact counts, because the distinct computation is not applied to documents held on every remote.
</Note>

## Conclusion

You have created three indexes, then performed a federated multi-index search to receive all results in a single list. You then used `weight` to boost results from the index most likely to contain the information you wanted, paginated through merged results using `federation.page` and `federation.hitsPerPage`, and collapsed duplicate matches with `federation.distinct`.

## Next steps

<CardGroup cols={2}>
  <Card title="Boost results across indexes" href="/docs/capabilities/multi_search/how_to/boost_results_across_indexes">
    Fine-tune result ranking when combining results from multiple indexes.
  </Card>

  <Card title="Build a unified search bar" href="/docs/capabilities/multi_search/how_to/build_unified_search_bar">
    Create a single search interface that queries multiple indexes at once.
  </Card>

  <Card title="Multi-search overview" href="/docs/capabilities/multi_search/overview">
    Learn about multi-search capabilities and when to use them.
  </Card>
</CardGroup>
