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

# Implementing MCM

> Learn how to implement multi-cluster management to distribute and manage your data across several machines.

<Warning>
  Multi-cluster management is **deprecated** and will be sunset.
  If you have issues with your Algolia infrastructure due to large volumes of data,
  contact the [Algolia support](https://support.algolia.com/hc/en-us/requests/new) team.
</Warning>

When your data no longer fits on a single machine, you have to add new clusters. Multi-Cluster Management (MCM) simplifies this process by letting you **distribute and manage your data across several machines**.

For example, a music streaming application lets users create public and private playlists.
With MCM, the number of playlists can grow without fearing they might exceed your current cluster's size limit.
You also get dedicated user access out of the box.

## Splitting data across multiple clusters

### Assigning user data to a cluster

Without MCM, an application is bound to a single cluster. It means that splitting data across several clusters requires you to assign data to a specific application ID, so you can manually maintain and orchestrate the distribution by yourself.

With MCM, all clusters use the same application ID.
MCM keeps a mapping on each cluster to route requests to the correct cluster.
All you need to do is assign a cluster to a user with the [`assignUserId`](/doc/rest-api/search/assign-user-id) method.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.AssignUserIdAsync(
    "user42",
    new AssignUserIdParams { Cluster = "d4242-eu" }
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.assignUserId(
    xAlgoliaUserID: "user42",
    assignUserIdParams: AssignUserIdParams(cluster: "d4242-eu"),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.AssignUserId(client.NewApiAssignUserIdRequest(
  	"user42",
  	search.NewEmptyAssignUserIdParams().SetCluster("d4242-eu")))
  if err != nil {
  	// handle the eventual error
  	panic(err)
  }
  ```

  ```java Java theme={"system"}
  client.assignUserId("user42", new AssignUserIdParams().setCluster("d4242-eu"));
  ```

  ```js JavaScript theme={"system"}
  const response = await client.assignUserId({
    xAlgoliaUserID: "user42",
    assignUserIdParams: { cluster: "d4242-eu" },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response = client.assignUserId(
    xAlgoliaUserID = "user42",
    assignUserIdParams = AssignUserIdParams(
      cluster = "d4242-eu",
    ),
  )
  ```

  ```php PHP theme={"system"}
  $response = $client->assignUserId(
      'user42',
      ['cluster' => 'd4242-eu',
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.assign_user_id(
      x_algolia_user_id="user42",
      assign_user_id_params={
          "cluster": "d4242-eu",
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.assign_user_id("user42", Algolia::Search::AssignUserIdParams.new(cluster: "d4242-eu"))
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.assignUserId(
      xAlgoliaUserID = "user42",
      assignUserIdParams = AssignUserIdParams(
        cluster = "d4242-eu"
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response = try await client.assignUserId(
      xAlgoliaUserID: "user42",
      assignUserIdParams: AssignUserIdParams(cluster: "d4242-eu")
  )
  ```
</CodeGroup>

### Adding private user data

Suppose you want to index your users' private playlists.

```js JavaScript icon=code theme={"system"}
const playlists = [
  {
    user: "user42",
    name: "My peaceful playlist",
    tracks: [
      // ...
    ],
    createdAt: 1500000181,
  },
  {
    user: "user4242",
    name: "My workout playlist",
    tracks: [
      // ...
    ],
    createdAt: 1500040452,
  },
];
```

Without MCM, every record needs a `userID` attribute to tag the right user. Additionally, you need to set the `userID` attribute as a filter at query time.

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class SetSettingsThenSaveObjects
  {
    private static readonly List> playlists = []; // Your records

    private static string GetAppIdFor(string user)
    {
      return ""; // Implement your own logic here
    }

    private static string GetIndexingApiKeyFor(string user)
    {
      return ""; // Implement your own logic here
    }

    async Task Main(string[] args)
    {
      foreach (var playlist in playlists)
      {
        // Fetch from your own data storage and with your own code
        // the associated application ID and API key for this user
        var appId = GetAppIdFor((playlist.GetValueOrDefault("user", "") as string)!);
        var apiKey = GetIndexingApiKeyFor((playlist.GetValueOrDefault("user", "") as string)!);

        try
        {
          var client = new SearchClient(new SearchConfig(appId, apiKey));
          var settings = new IndexSettings { AttributesForFaceting = ["searchable(playlistName)"] };
          await client.SetSettingsAsync("", settings);

          await client.SaveObjectsAsync("", playlists);
        }
        catch (Exception e)
        {
          Console.WriteLine(e.Message);
        }
      }
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  final playlists = []; // Your records

  String getAppIDFor(String _) {
    return ""; // Implement your own logic here
  }

  String getIndexingApiKeyFor(String _) {
    return ""; // Implement your own logic here
  }

  void setSettingsThenSaveObjects() async {
    for (final playlist in playlists) {
      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      final appId = getAppIDFor(playlist["user"]);
      final apiKey = getIndexingApiKeyFor(playlist["user"]);

      final client = SearchClient(appId: appId, apiKey: apiKey);
      final settings = IndexSettings(
        attributesForFaceting: ['filterOnly(userID)'],
      );

      await client.setSettings(
        indexName: "",
        indexSettings: settings,
      );

      final batchParams = BatchWriteParams(
          requests: playlists
              .map((record) => BatchRequest(
                    action: Action.addObject,
                    body: record,
                  ))
              .toList());
      await client.batch(
        indexName: "",
        batchWriteParams: batchParams,
      );
    }
  }
  ```

  ```go Go theme={"system"}
  package main

  import (
  	"fmt"

  	"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
  )

  func getAppIDFor(_ string) (string, error) {
  	return "", nil // Implement your logic here
  }

  func getIndexingApiKeyFor(_ string) (string, error) {
  	return "", nil // Implement your logic here
  }

  func setSettingsThenSaveObjects() {
  	playlists := []map[string]any{{ /* Your records */ }}

  	for _, playlist := range playlists {
  		// Fetch from your own data storage and with your own code
  		// the associated application ID and API key for this user
  		appID, err := getAppIDFor(playlist["user"].(string))
  		if err != nil {
  			fmt.Println(err)
  			return
  		}

  		apiKey, err := getIndexingApiKeyFor(playlist["user"].(string))
  		if err != nil {
  			fmt.Println(err)
  			return
  		}

  		client, err := search.NewClient(appID, apiKey)
  		if err != nil {
  			fmt.Println(err)
  			return
  		}

  		settings := &search.IndexSettings{
  			AttributesForFaceting: []string{"filterOnly(user)"},
  		}

  		_, err = client.SetSettings(client.NewApiSetSettingsRequest(
  			"", settings))
  		if err != nil {
  			panic(err)
  		}

  		_, err = client.SaveObjects(
  			"", playlists)
  		if err != nil {
  			panic(err)
  		}
  	}
  }
  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.List;
  import java.util.Map;

  public class setSettingsThenSaveObjects {

    private static final List> playlists = List.of(
      /* Your records */
    );

    private static String getAppIDFor(String user) {
      return ""; // Implement your own logic here
    }

    private static String getIndexingApiKeyFor(String user) {
      return ""; // Implement your own logic here
    }

    public static void main(String[] args) throws Exception {
      playlists.forEach(playlist -> {
        // Fetch from your own data storage and with your own code
        // the associated application ID and API key for this user
        String appID = getAppIDFor((String) playlist.get("user"));
        String apiKey = getIndexingApiKeyFor((String) playlist.get("user"));

        try (SearchClient client = new SearchClient(appID, apiKey)) {
          IndexSettings settings = new IndexSettings().setAttributesForFaceting(List.of("searchable(playlistName)"));
          client.setSettings("", settings);

          client.saveObjects("", playlists);
        } catch (Exception e) {
          System.out.println("An error occurred: " + e.getMessage());
        }
      });
    }
  }
  ```

  ```js JavaScript theme={"system"}
  import type { IndexSettings } from 'algoliasearch';
  import { algoliasearch } from 'algoliasearch';

  const playlists: Record[] = [
    /* Your records */
  ];

  const getAppIDFor = (_: string) => {
    return ''; // Implement your own logic here
  };
  const getIndexingApiKeyFor = (_: string) => {
    return ''; // Implement your own logic here
  };

  playlists.forEach(async (playlist) => {
    // Fetch from your own data storage and with your own code
    // the associated application ID and API key for this user
    const appID = getAppIDFor(playlist['user']);
    const apiKey = getIndexingApiKeyFor(playlist['user']);

    try {
      const client = algoliasearch(appID, apiKey);
      const settings: IndexSettings = {
        attributesForFaceting: ['filterOnly(user)'],
      };
      await client.setSettings({ indexName: 'indexName', indexSettings: settings });

      await client.saveObjects({ indexName: 'indexName', objects: playlists });
    } catch (e: any) {
      console.error(e);
    }
  });
  ```

  ```kotlin Kotlin theme={"system"}
  import kotlinx.serialization.json.JsonObject

  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.transport.*
  import com.algolia.client.extensions.*

  import com.algolia.client.model.search.*

  val playlists = listOf() // Your records

  val getAppIDFor: (String) -> String = {
    "" // Implement your own logic here
  }
  val getIndexingApiKeyFor: (String) -> String = {
    "" // Implement your own logic here
  }

  suspend fun setSettingsThenSaveObjects() {
    playlists.forEach { playlist ->
      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      val appID = getAppIDFor(playlist["user"].toString())
      val apiKey = getIndexingApiKeyFor(playlist["user"].toString())

      val client = SearchClient(appID, apiKey)
      val settings = IndexSettings(
        attributesForFaceting = listOf("filterOnly(user)"),
      )

      try {
        client.setSettings(
          indexName = "",
          indexSettings = settings,
        )

        client.saveObjects(
          indexName = "",
          objects = playlists,
        )
      } catch (exception: Exception) {
        println(exception.message)
      }
    }
  }
  ```

  ```php PHP theme={"system"}
  setAttributesForFaceting(['filterOnly(user)']),
          ];
          $client->setSettings(
              '',
              $settings,
          );

          $client->saveObjects(
              '',
              $playlists,
          );
      } catch (Exception $e) {
          echo $e->getMessage().PHP_EOL;
      }
  }
  ```

  ```python Python theme={"system"}
  from algoliasearch.search.client import SearchClientSync
  from algoliasearch.search.models.index_settings import IndexSettings


  def _get_app_id_for(_user):
      # Implement your own logic here
      return ""


  def _get_indexing_api_key_for(_user):
      # Implement your own logic here
      return ""


  playlists = []  # Your records

  for playlist in playlists:
      app_id = _get_app_id_for(playlist["user"])
      api_key = _get_indexing_api_key_for(playlist["user"])

      _client = SearchClientSync(app_id, api_key)
      settings = IndexSettings(attributes_for_faceting=["filterOnly(user)"])
      try:
          _client.set_settings(
              index_name="",
              index_settings=settings,
          )

          _client.save_objects(
              index_name="",
              objects=playlists,
          )
      except Exception as e:
          print(f"Error: {e}")
  ```

  ```ruby Ruby theme={"system"}
  require "algolia"

  def get_app_id_for(_user)
    # Implement your own logic here
    ""
  end

  def get_indexing_api_key_for(_user)
    # Implement your own logic here
    ""
  end

  # Your records
  playlists = []

  playlists.each do |playlist|
    begin
      app_id = get_app_id_for(playlist["user"])
      api_key = get_indexing_api_key_for(playlist["user"])

      client = Algolia::SearchClient.create(app_id, api_key)

      settings = Algolia::Search::IndexSettings.new(
        attributes_for_faceting: ["filterOnly(user)"]
      )
      client.set_settings("", settings)

      client.save_objects("", playlists)
    rescue Exception => e
      puts("An error occurred: #{e}")
    end
  end
  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent.Await
  import scala.concurrent.duration.Duration

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions

  import algoliasearch.search.IndexSettings

  val getAppIDFor: String => String = _ => {
    "" // Implement your own logic here
  }
  val getIndexingApiKeyFor: String => String = _ => {
    "" // Implement your own logic here
  }

  def setSettingsThenSaveObjects(): Future[Unit] = {
    val playlists: Seq[Map[String, Any]] = Seq() // Your records

    playlists.foreach { playlist =>
      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      val appID = getAppIDFor(playlist("user").toString)
      val apiKey = getIndexingApiKeyFor(playlist("user").toString)

      val client = SearchClient(appID, apiKey)
      val settings = IndexSettings(
        attributesForFaceting = Some(Seq("filterOnly(user)"))
      )

      Await.result(
        client.setSettings(
          indexName = "",
          indexSettings = settings
        ),
        Duration(5, "sec")
      )

      Await.result(
        client.saveObjects(
          indexName = "",
          objects = playlists
        ),
        Duration(5, "sec")
      )
    }

    Future.unit
  }
  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import Core
  import Search

  let playlists: [[String: AnyCodable]] = [ /* Your records */ ]

  let getAppIDFor = { (_: String) in "" } // Implement your own logic here
  let getIndexingApiKeyFor = { (_: String) in "" } // Implement your own logic here

  func setSettingsThenSaveObjects() async throws {
      for playlist in playlists {
          // Fetch from your own data storage and with your own code
          // the associated application ID and API key for this user
          let appID = getAppIDFor(playlist["user"]?.value as! String)
          let apiKey = getIndexingApiKeyFor(playlist["user"]?.value as! String)

          do {
              let client = try SearchClient(appID: appID, apiKey: apiKey)
              let settings = IndexSettings(
                  attributesForFaceting: ["filterOnly(user)"]
              )
              try await client.setSettings(indexName: "", indexSettings: settings)

              try await client.saveObjects(indexName: "", objects: playlists)
          } catch {
              print(error)
          }
      }
  }
  ```
</CodeGroup>

With MCM, all records are automatically tagged using the `X-Algolia-User-ID` header you send at query time. The engine automatically adds an extra attribute `__userID__` inside records to identify the `userID` associated with each record.

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class SetHeaderUserIDThenSaveObjects
  {
    private static readonly List> playlists = []; // Your records

    async Task Main(string[] args)
    {
      var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
      foreach (var playlist in playlists)
      {
        try
        {
          var playlistUserID = playlist.GetValueOrDefault("userID", "") as string;
          await client.SaveObjectsAsync(
            "",
            playlists,
            false,
            1000,
            new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", playlistUserID).Build()
          );
        }
        catch (Exception e)
        {
          Console.WriteLine(e.Message);
        }
      }
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  final playlists = []; // Your records

  void setHeaderUserIDThenSaveObjects() async {
    final client =
        SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');

    for (final playlist in playlists) {
      final playlistUserID = playlist["userID"];
      final batchParams = BatchWriteParams(
          requests: playlists
              .map((record) => BatchRequest(
                    action: Action.addObject,
                    body: record,
                  ))
              .toList());
      await client.batch(
          indexName: "",
          batchWriteParams: batchParams,
          requestOptions: RequestOptions(
            headers: {
              'X-Algolia-User-ID': playlistUserID,
            },
          ));
    }
  }
  ```

  ```go Go theme={"system"}
  package main

  import "github.com/algolia/algoliasearch-client-go/v4/algolia/search"

  func setHeaderUserIDThenSaveObjects() {
  	playlists := []map[string]any{{ /* Your records */ }}

  	client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
  	if err != nil {
  		// The client can fail to initialize if you pass an invalid parameter.
  		panic(err)
  	}

  	for _, playlist := range playlists {
  		playlistUserID := playlist["userID"]

  		_, err := client.SaveObjects(
  			"",
  			playlists,
  			search.WithWaitForTasks(false),
  			search.WithBatchSize(1000),
  			search.WithHeaderParam("X-Algolia-User-ID", playlistUserID),
  		)
  		if err != nil {
  			panic(err)
  		}
  	}
  }
  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.List;
  import java.util.Map;

  public class setHeaderUserIDThenSaveObjects {

    private static final List> playlists = List.of(
      /* Your records */
    );

    public static void main(String[] args) throws Exception {
      try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")) {
        playlists.forEach(playlist -> {
          String playlistUserID = (String) playlist.get("userID");
          client.saveObjects(
            "",
            playlists,
            false,
            1000,
            new RequestOptions().addExtraHeader("X-Algolia-User-ID", playlistUserID)
          );
        });
      } catch (Exception e) {
        System.out.println("An error occurred: " + e.getMessage());
      }
    }
  }
  ```

  ```js JavaScript theme={"system"}
  import { algoliasearch } from 'algoliasearch';

  const playlists: Record[] = [
    /* Your records */
  ];

  const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

  playlists.forEach(async (playlist) => {
    try {
      const playlistUserID = playlist['userID'];
      await client.saveObjects(
        { indexName: 'indexName', objects: playlists, waitForTasks: false, batchSize: 1000 },
        {
          headers: { 'X-Algolia-User-ID': playlistUserID },
        },
      );
    } catch (e: any) {
      console.error(e);
    }
  });
  ```

  ```kotlin Kotlin theme={"system"}
  import kotlinx.serialization.json.JsonObject

  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.transport.*
  import com.algolia.client.extensions.*

  import com.algolia.client.model.search.*

  suspend fun setHeaderUserIDThenSaveObjects() {
    val playlists: List = listOf() // Your records

    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    playlists.forEach { playlist ->
      val playlistUserID = playlist["userID"].toString()
      try {
        client.saveObjects(
          indexName = "",
          objects = playlists,
          waitForTasks = false,
          batchSize = 1000,
          requestOptions = RequestOptions(
            headers = buildMap {
              put("X-Algolia-User-ID", playlistUserID)
            },
          ),
        )
      } catch (exception: Exception) {
        println(exception.message)
      }
    }
  }
  ```

  ```php PHP theme={"system"}
  saveObjects(
              '',
              $playlists,
              false,
              1000,
              [
                  'headers' => [
                      'X-Algolia-User-ID' => $playlistUserID,
                  ],
              ]
          );
      } catch (Exception $e) {
          echo $e->getMessage().PHP_EOL;
      }
  }
  ```

  ```python Python theme={"system"}
  from json import loads

  from algoliasearch.search.client import SearchClientSync

  playlists = []  # Your records

  _client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

  for playlist in playlists:
      try:
          playlist_user_id = playlist["userID"]
          _client.save_objects(
              index_name="",
              objects=playlists,
              wait_for_tasks=False,
              batch_size=1000,
              request_options={
                  "headers": loads("""{"X-Algolia-User-ID":playlistUserID}"""),
              },
          )
      except Exception as e:
          print(f"Error: {e}")
  ```

  ```ruby Ruby theme={"system"}
  require "algolia"

  # Your records
  playlists = []

  client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

  playlists.each do |playlist|
    playlist_user_id = playlist["userID"]
    client.save_objects(
      "",
      playlists,
      false,
      1000,
      {:header_params => {"X-Algolia-User-ID" => playlist_user_id}}
    )
  end
  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent.Await
  import scala.concurrent.duration.Duration

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions

  def setHeaderUserIDThenSaveObjects(): Future[Unit] = {
    val playlists: Seq[Map[String, Any]] = Seq() // Your records

    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    playlists.foreach { playlist =>
      val playlistUserID = playlist("userID").toString
      Await.result(
        client.saveObjects(
          indexName = "",
          objects = playlists,
          waitForTasks = false,
          batchSize = 1000,
          requestOptions = Some(
            RequestOptions
              .builder()
              .withHeader("X-Algolia-User-ID", "playlistUserID")
              .build()
          )
        ),
        Duration(5, "sec")
      )
    }

    Future.unit
  }
  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import Core
  import Search

  func setHeaderUserIDThenSaveObjects() async throws {
      let playlists: [[String: AnyCodable]] = [ /* Your records */ ]

      let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")

      for playlist in playlists {
          do {
              let playlistUserID = playlist["userID"]?.value as! String
              try await client.saveObjects(
                  indexName: "",
                  objects: playlists,
                  waitForTasks: false,
                  batchSize: 1000,
                  requestOptions: RequestOptions(
                      headers: ["X-Algolia-User-ID": playlistUserID]
                  )
              )
          } catch {
              print(error)
          }
      }
  }
  ```
</CodeGroup>

<Note>
  For simplicity's sake, the preceding snippets add records one by one.
  For better performance, you should group your records by `userID` first, then batch them.
</Note>

### Adding public data

If you decide you want to index public playlists as well.

```js JavaScript icon=code theme={"system"}
const playlists = [
  {
    user: "public",
    name: "Hot 100 Billboard Charts",
    tracks: [
      // ...
    ],
    createdAt: 1500240452,
  },
];
```

Without MCM, you need to tag every public record with a particular value (such as "public") for the `userID` attribute. Then, you need to filter on that value to search for public records.

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class SaveObjectsMcm
  {
    private readonly List> playlists = []; // Your records

    private static List> GetAllAppIdConfigurations()
    {
      return
      [ /* A list of your MCM AppID/ApiKey pairs */
      ];
    }

    async Task Main(string[] args)
    {
      // Fetch from your own data storage and with your own code
      // the list of application IDs and API keys to target each cluster
      var configurations = GetAllAppIdConfigurations();

      // Send the records to each cluster
      foreach (var config in configurations)
      {
        try
        {
          var client = new SearchClient(
            new SearchConfig(
              config.GetValueOrDefault("appID", ""),
              config.GetValueOrDefault("apiKey", "")
            )
          );
          await client.SaveObjectsAsync("", playlists);
        }
        catch (Exception e)
        {
          Console.WriteLine(e.Message);
        }
      }
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  List> getAllAppIDConfigurations() {
    return [/* A list of your MCM AppID/ApiKey pairs */];
  }

  List playlists = [/* Your records */];

  void saveObjectsMCM() async {
    final configurations = getAllAppIDConfigurations();

    for (var configuration in configurations) {
      final appId = configuration['appID'] ?? "";
      final apiKey = configuration['apiKey'] ?? "";
      final client = SearchClient(appId: appId, apiKey: apiKey);

      try {
        final batchParams = BatchWriteParams(
            requests: playlists
                .map((record) => BatchRequest(
                      action: Action.addObject,
                      body: record,
                    ))
                .toList());
        await client.batch(
          indexName: "",
          batchWriteParams: batchParams,
        );
      } catch (e) {
        throw Exception('Error for appID $appId: $e');
      }
    }
  }
  ```

  ```go Go theme={"system"}
  package main

  import (
  	"fmt"

  	"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
  )

  func getAllAppIDConfigurations() ([]struct{ appID, apiKey string }, error) {
  	return []struct{ appID, apiKey string }{ /* A list of your MCM AppID/ApiKey pairs */ }, nil
  }

  func saveObjectsMCM() {
  	playlists := []map[string]any{{ /* Your records */ }}

  	// Fetch from your own data storage and with your own code
  	// the list of application IDs and API keys to target each cluster
  	configurations, err := getAllAppIDConfigurations()
  	if err != nil {
  		fmt.Println(err)
  		return
  	}

  	// Send the records to each cluster
  	for _, configuration := range configurations {
  		client, err := search.NewClient(configuration.appID, configuration.apiKey)
  		if err != nil {
  			fmt.Println(err)
  			return
  		}

  		_, err = client.SaveObjects(
  			"", playlists)
  		if err != nil {
  			panic(err)
  		}
  	}
  }
  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.List;
  import java.util.Map;

  public class saveObjectsMCM {

    private static final List> playlists = List.of(
      /* Your records */
    );

    private static List> getAllAppIDConfigurations() {
      return List.of(
        /* A list of your MCM AppID/ApiKey pairs */
      );
    }

    public static void main(String[] args) throws Exception {
      // Fetch from your own data storage and with your own code
      // the list of application IDs and API keys to target each cluster
      var configurations = getAllAppIDConfigurations();

      // Send the records to each cluster
      configurations.forEach(config -> {
        try (SearchClient client = new SearchClient(config.get("appID"), config.get("apiKey"))) {
          client.saveObjects("", playlists);
        } catch (Exception e) {
          System.out.println("An error occurred: " + e.getMessage());
        }
      });
    }
  }
  ```

  ```js JavaScript theme={"system"}
  import { algoliasearch } from 'algoliasearch';

  const getAllAppIDConfigurations = (): Record[] => {
    return [
      /* A list of your MCM AppID/ApiKey pairs */
    ];
  };

  const playlists: Record[] = [
    /* Your records */
  ];

  // Fetch from your own data storage and with your own code
  // the list of application IDs and API keys to target each cluster
  const configurations = getAllAppIDConfigurations();

  // Send the records to each cluster
  Object.keys(configurations).forEach(async (appID) => {
    try {
      const client = algoliasearch(appID, configurations[appID]);

      await client.saveObjects({ indexName: 'indexName', objects: playlists });
    } catch (e: any) {
      console.error(e);
    }
  });
  ```

  ```kotlin Kotlin theme={"system"}
  import kotlinx.serialization.json.JsonObject

  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.transport.*
  import com.algolia.client.extensions.*

  import com.algolia.client.model.search.*

  val getAllAppIDConfigurations: () -> Map = {
    mapOf() // A map of your MCM AppID/ApiKey pairs
  }

  suspend fun saveObjectsMCM() {
    val playlists: List = listOf() // Your records

    val configurations = getAllAppIDConfigurations()

    configurations.map { (appID, apiKey) ->
      val client = SearchClient(appID, apiKey)

      try {
        client.saveObjects(
          indexName = "",
          objects = playlists,
        )
      } catch (e: Exception) {
        throw Exception("Error for appID $appID: ${e.message}")
      }
    }
  }
  ```

  ```php PHP theme={"system"}
  saveObjects(
              '',
              $playlists,
          );
      } catch (Exception $e) {
          echo $e->getMessage().PHP_EOL;
      }
  }
  ```

  ```python Python theme={"system"}
  from algoliasearch.search.client import SearchClientSync


  def _get_all_app_id_configurations():
      return []  # A list of your MCM AppID/ApiKey pairs


  playlists = []  # Your records

  # Fetch from your own data storage and with your own code
  # the list of application IDs and API keys to target each cluster
  configurations = _get_all_app_id_configurations()

  # Send the records to each cluster
  for appID, apiKey in configurations:
      try:
          _client = SearchClientSync(appID, apiKey)
          _client.save_objects(
              index_name="",
              objects=playlists,
          )
      except Exception as e:
          print(f"Error: {e}")
  ```

  ```ruby Ruby theme={"system"}
  require "algolia"

  def get_all_app_id_configurations
    # A list of your MCM AppID/ApiKey pairs
    []
  end

  # Your records
  playlists = []

  # Fetch from your own data storage and with your own code
  # the list of application IDs and API keys to target each cluster
  configurations = get_all_app_id_configurations

  # Send the records to each cluster
  configurations.each do |appID, apiKey|
    begin
      client = Algolia::SearchClient.create(appID, apiKey)
      client.save_objects("", playlists)
    rescue Exception => e
      puts("An error occurred: #{e}")
    end
  end
  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions

  import algoliasearch.config.RequestOptions

  val getAllAppIDConfigurations: () => Map[String, String] = () => {
    Map( /* A map of your MCM AppID/ApiKey pairs */ )
  }

  val playlists: Seq[Any] = Seq( /* Your records */ )

  def saveObjectsMCM(): Future[Unit] = {
    val configurations = getAllAppIDConfigurations()

    Future
      .sequence {
        configurations.map { case (appID, apiKey) =>
          val client = new SearchClient(appID, apiKey)

          client
            .saveObjects(
              indexName = "",
              objects = playlists
            )
            .recover { case ex: Exception =>
              println(s"Error for appID $appID: ${ex.getMessage}")
            }
        }
      }
      .map(_ => ())
  }
  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import Core
  import Search

  let getAllAppIDConfigurations: () -> [(String, String)] = {
      [ /* A list of your MCM AppID/ApiKey pairs */ ]
  }

  func saveObjectsMCM() async throws {
      let playlists: [[String: AnyCodable]] = [ /* Your records */ ]

      // Fetch from your own data storage and with your own code
      // the list of application IDs and API keys to target each cluster
      let configurations = getAllAppIDConfigurations()

      // Send the records to each cluster
      for (appID, apiKey) in configurations {
          do {
              let client = try SearchClient(appID: appID, apiKey: apiKey)

              try await client.saveObjects(indexName: "", objects: playlists)
          } catch {
              print(error)
          }
      }
  }
  ```
</CodeGroup>

With MCM, you can use the `userID` value `*` to flag records as public, allowing all users to see them. Public records are automatically replicated on all clusters to avoid network latency during search.

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class SaveObjectsPublicUser
  {
    private static readonly List> playlists = []; // Your records

    async Task Main(string[] args)
    {
      var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
      await client.SaveObjectsAsync(
        "",
        playlists,
        false,
        1000,
        new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "*").Build()
      );
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  final playlists = [/* Your records */];

  void saveObjectsPublicUser() async {
    final client =
        SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');

    final batchParams = BatchWriteParams(
        requests: playlists
            .map((record) => BatchRequest(
                  action: Action.addObject,
                  body: record,
                ))
            .toList());
    await client.batch(
        indexName: "",
        batchWriteParams: batchParams,
        requestOptions: RequestOptions(
          headers: {
            'X-Algolia-User-ID': "*",
          },
        ));
  }
  ```

  ```go Go theme={"system"}
  package main

  import "github.com/algolia/algoliasearch-client-go/v4/algolia/search"

  func saveObjectsPublicUser() {
  	playlists := []map[string]any{{ /* Your records */ }}

  	client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
  	if err != nil {
  		// The client can fail to initialize if you pass an invalid parameter.
  		panic(err)
  	}

  	_, err = client.SaveObjects(
  		"",
  		playlists,
  		search.WithWaitForTasks(false),
  		search.WithBatchSize(1000),
  		search.WithHeaderParam("X-Algolia-User-ID", "*"),
  	)
  	if err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.List;
  import java.util.Map;

  public class saveObjectsPublicUser {

    private static final List> playlists = List.of(
      /* Your records */
    );

    public static void main(String[] args) throws Exception {
      try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")) {
        client.saveObjects("", playlists, false, 1000, new RequestOptions().addExtraHeader("X-Algolia-User-ID", "*"));
      } catch (Exception e) {
        System.out.println("An error occurred: " + e.getMessage());
      }
    }
  }
  ```

  ```js JavaScript theme={"system"}
  import { algoliasearch } from 'algoliasearch';

  const playlists: Record[] = []; // Your records

  const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

  await client.saveObjects(
    { indexName: 'indexName', objects: playlists, waitForTasks: false, batchSize: 1000 },
    {
      headers: { 'X-Algolia-User-ID': '*' },
    },
  );
  ```

  ```kotlin Kotlin theme={"system"}
  import kotlinx.serialization.json.JsonObject

  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.transport.*
  import com.algolia.client.extensions.*

  import com.algolia.client.model.search.*
  import com.algolia.client.transport.RequestOptions

  suspend fun saveObjectsPublicUser() {
    val playlists: List = listOf() // Your records

    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    client.saveObjects(
      indexName = "",
      objects = playlists,
      waitForTasks = false,
      batchSize = 1000,
      requestOptions = RequestOptions(
        headers = buildMap {
          put("X-Algolia-User-ID", "*")
        },
      ),
    )
  }
  ```

  ```php PHP theme={"system"}
  saveObjects(
      '',
      $playlists,
      false,
      1000,
      [
          'headers' => [
              'X-Algolia-User-ID' => '*',
          ],
      ]
  );
  ```

  ```python Python theme={"system"}
  from json import loads

  from algoliasearch.search.client import SearchClientSync

  playlists = []  # Your records

  _client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

  _client.save_objects(
      index_name="",
      objects=playlists,
      wait_for_tasks=False,
      batch_size=1000,
      request_options={
          "headers": loads("""{"X-Algolia-User-ID":"*"}"""),
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  require "algolia"

  # Your records
  playlists = []

  client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

  client.save_objects("", playlists, false, 1000, {:header_params => {"X-Algolia-User-ID" => "*"}})
  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent.Await
  import scala.concurrent.duration.Duration

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions

  import algoliasearch.config.RequestOptions

  def saveObjectsPublicUser(): Future[Unit] = {
    val playlists: Seq[Any] = Seq() // Your records

    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    Await.result(
      client
        .saveObjects(
          indexName = "",
          objects = playlists,
          waitForTasks = false,
          batchSize = 1000,
          requestOptions = Some(
            RequestOptions
              .builder()
              .withHeader("X-Algolia-User-ID", "*")
              .build()
          )
        )
        .map(_ => Future.unit),
      Duration(5, "sec")
    )
  }
  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import Core
  import Search

  func saveObjectsPublicUser() async throws {
      let playlists: [[String: AnyCodable]] = [] // Your records

      let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")

      try await client.saveObjects(
          indexName: "",
          objects: playlists,
          waitForTasks: false,
          batchSize: 1000,
          requestOptions: RequestOptions(
              headers: ["X-Algolia-User-ID": "*"]
          )
      )
  }
  ```
</CodeGroup>

## Searching the data

Without MCM, you need to manually handle the filtering logic to filter on private data for a specific user and public data.

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class McmSearchWithout
  {
    private static string GetAppIdFor(string user)
    {
      return ""; // Implement your own logic here
    }

    private static string GetIndexingApiKeyFor(string user)
    {
      return ""; // Implement your own logic here
    }

    async Task Main(string[] args)
    {
      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      var appId = GetAppIdFor("user42");
      var apiKey = GetIndexingApiKeyFor("user42");

      var client = new SearchClient(new SearchConfig(appId, apiKey));
      var searchParams = new SearchParams(
        new SearchParamsObject
        {
          Query = "",
          FacetFilters = new FacetFilters(
            [new FacetFilters("user:user42"), new FacetFilters("user:public")]
          ),
        }
      );

      await client.SearchSingleIndexAsync("", searchParams);
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  String getAppIDFor(String _) {
    return ""; // Implement your own logic here
  }

  String getIndexingApiKeyFor(String _) {
    return ""; // Implement your own logic here
  }

  void mcmSearchWithout() async {
    // Fetch from your own data storage and with your own code
    // the associated application ID and API key for this user
    final appId = getAppIDFor("user42");
    final apiKey = getIndexingApiKeyFor("user42");

    final client = SearchClient(appId: appId, apiKey: apiKey);
    final searchParams = SearchParamsObject(
        query: "",
        facetFilters: ["user:user42", "user:public"]);

    await client.searchSingleIndex(
      indexName: "",
      searchParams: searchParams,
    );
  }
  ```

  ```go Go theme={"system"}
  package main

  import (
  	"fmt"

  	"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
  )

  func mcmSearchWithout() {
  	getAppIDFor := func(_ string) (string, error) {
  		return "", nil // Implement your logic here
  	}

  	getIndexingApiKeyFor := func(_ string) (string, error) {
  		return "", nil // Implement your logic here
  	}

  	// Fetch from your own data storage and with your own code
  	// the associated application ID and API key for this user
  	appID, err := getAppIDFor("user42")
  	if err != nil {
  		fmt.Println(err)
  		return
  	}

  	apiKey, err := getIndexingApiKeyFor("user42")
  	if err != nil {
  		fmt.Println(err)
  		return
  	}

  	client, err := search.NewClient(appID, apiKey)
  	if err != nil {
  		fmt.Println(err)
  		return
  	}

  	searchParams := search.SearchParamsObjectAsSearchParams(
  		search.NewSearchParamsObject().
  			SetQuery("").
  			SetFacetFilters(
  				search.ArrayOfFacetFiltersAsFacetFilters(
  					[]search.FacetFilters{
  						*search.StringAsFacetFilters("user:user42"),
  						*search.StringAsFacetFilters("user:public"),
  					},
  				),
  			),
  	)

  	_, err = client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
  		"").WithSearchParams(searchParams))
  	if err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.List;

  public class mcmSearchWithout {

    private static String getAppIDFor(String user) {
      return ""; // Implement your own logic here
    }

    private static String getIndexingApiKeyFor(String user) {
      return ""; // Implement your own logic here
    }

    public static void main(String[] args) throws Exception {
      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      String appID = getAppIDFor("user42");
      String apiKey = getIndexingApiKeyFor("user42");

      try (SearchClient client = new SearchClient(appID, apiKey)) {
        SearchParams searchParams = new SearchParamsObject()
          .setQuery("")
          .setFacetFilters(FacetFilters.of(List.of(FacetFilters.of("user:user42"), FacetFilters.of("user:public"))));

        client.searchSingleIndex("", searchParams, Hit.class);
      } catch (Exception e) {
        System.out.println("An error occurred: " + e.getMessage());
      }
    }
  }
  ```

  ```js JavaScript theme={"system"}
  import type { SearchParams } from 'algoliasearch';
  import { algoliasearch } from 'algoliasearch';

  const getAppIDFor = (_: string) => {
    return ''; // Implement your own logic here
  };
  const getIndexingApiKeyFor = (_: string) => {
    return ''; // Implement your own logic here
  };

  // Fetch from your own data storage and with your own code
  // the associated application ID and API key for this user
  const appID = getAppIDFor('user42');
  const apiKey = getIndexingApiKeyFor('user42');

  const client = algoliasearch(appID, apiKey);
  const searchParams: SearchParams = {
    query: '',
    facetFilters: ['user:user42', 'user:public'],
  };

  await client.searchSingleIndex({ indexName: 'indexName', searchParams: searchParams });
  ```

  ```kotlin Kotlin theme={"system"}
  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.transport.*
  import com.algolia.client.extensions.*

  import com.algolia.client.model.search.*

  suspend fun mcmSearchWithout() {
    val getAppIDFor: (String) -> String = {
      "" // Implement your own logic here
    }
    val getIndexingApiKeyFor: (String) -> String = {
      "" // Implement your own logic here
    }

    // Fetch from your own data storage and with your own code
    // the associated application ID and API key for this user
    val appID = getAppIDFor("user42")
    val apiKey = getIndexingApiKeyFor("user42")

    val client = SearchClient(appID, apiKey)
    val searchParams = SearchParamsObject(
      query = "",
      facetFilters = FacetFilters.of(
        listOf(
          FacetFilters.of("user:user42"),
          FacetFilters.of("user:public"),
        ),
      ),
    )

    client.searchSingleIndex(
      indexName = "",
      searchParams = searchParams,
    )
  }
  ```

  ```php PHP theme={"system"}
  setQuery('')
      ->setFacetFilters(['user:user42', 'user:public'])
  ;

  $client->searchSingleIndex(
      '',
      $searchParams,
  );
  ```

  ```python Python theme={"system"}
  from algoliasearch.search.client import SearchClientSync


  def _get_app_id_for(_user):
      # Implement your own logic here
      return ""


  def _get_indexing_api_key_for(_user):
      # Implement your own logic here
      return ""


  app_id = _get_app_id_for("user42")
  api_key = _get_indexing_api_key_for("user42")

  _client = SearchClientSync(app_id, api_key)

  search_params = {
      "query": "",
      "facetFilters": ["user:user42", "user:public"],
  }

  _client.search_single_index(
      index_name="",
      search_params=search_params,
  )
  ```

  ```ruby Ruby theme={"system"}
  import(time)

  require "algolia"

  def get_app_id_for(_user)
    # Implement your own logic here
    ""
  end

  def get_indexing_api_key_for(_user)
    # Implement your own logic here
    ""
  end

  app_id = get_app_id_for("user42")
  api_key = get_indexing_api_key_for("user42")

  client = Algolia::SearchClient.create(app_id, api_key)

  search_params = Algolia::Search::SearchParamsObject.new(
    query: "",
    facet_filters: %w[user:user42 user:public]
  )

  client.search_single_index("", search_params)
  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent.Await
  import scala.concurrent.duration.Duration

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions

  import algoliasearch.search.{FacetFilters, SearchParamsObject}

  def mcmSearchWithout(): Future[Unit] = {
    val getAppIDFor: String => String = _ => {
      "" // Implement your own logic here
    }
    val getIndexingApiKeyFor: String => String = _ => {
      "" // Implement your own logic here
    }

    // Fetch from your own data storage and with your own code
    // the associated application ID and API key for this user
    val appID = getAppIDFor("user42")
    val apiKey = getIndexingApiKeyFor("user42")

    val client = SearchClient(appID, apiKey)
    val searchParams = SearchParamsObject(
      query = Some(""),
      facetFilters = Some(
        FacetFilters.SeqOfFacetFilters(
          Seq(
            FacetFilters.StringValue("user:user42"),
            FacetFilters.StringValue("user:public")
          )
        )
      )
    )

    client
      .searchSingleIndex(
        indexName = "",
        searchParams = Some(searchParams)
      )
      .map { response =>
        println(response)
      }
      .recover { case ex: Exception =>
        println(s"An error occurred: ${ex.getMessage}")
      }
  }
  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import Core
  import Search

  func mcmSearchWithout() async throws {
      let getAppIDFor = { (_: String) in "" } // Implement your own logic here
      let getIndexingApiKeyFor = { (_: String) in "" } // Implement your own logic here

      // Fetch from your own data storage and with your own code
      // the associated application ID and API key for this user
      let appID = getAppIDFor("user42")
      let apiKey = getIndexingApiKeyFor("user42")

      let client = try SearchClient(appID: appID, apiKey: apiKey)
      let searchParams = SearchSearchParams.searchSearchParamsObject(
          SearchSearchParamsObject(
              query: "",
              facetFilters: .arrayOfSearchFacetFilters([.string("user:user42"), .string("user:public")])
          )
      )

      let response: SearchResponse = try await client.searchSingleIndex(
          indexName: "",
          searchParams: searchParams
      )
      print(response)
  }
  ```
</CodeGroup>

With MCM, all the engine needs is the `userID` (with the `X-Algolia-User-ID` header) that the query targets, so it can route the request to the right cluster. It automatically adds the right filters to retrieve both the user's data and public data.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SearchSingleIndexAsync(
    "INDEX_NAME",
    new SearchParams(new SearchParamsObject { Query = "peace" }),
    new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "user42").Build()
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchParamsObject(query: "peace"),
    requestOptions: RequestOptions(headers: {'X-Algolia-User-ID': 'user42'}),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
    "INDEX_NAME").WithSearchParams(search.SearchParamsObjectAsSearchParams(
    search.NewEmptySearchParamsObject().SetQuery("peace"))), search.WithHeaderParam("X-Algolia-User-ID", "user42"))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  client.searchSingleIndex(
    "INDEX_NAME",
    new SearchParamsObject().setQuery("peace"),
    Hit.class,
    new RequestOptions().addExtraHeader("X-Algolia-User-ID", "user42")
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.searchSingleIndex(
    { indexName: "playlists", searchParams: { query: "peace" } },
    {
      headers: { "X-Algolia-User-ID": "user42" },
    },
  );
  ```

  ```kotlin Kotlin theme={"system"}
  var response = client.searchSingleIndex(
    indexName = "INDEX_NAME",
    searchParams = SearchParamsObject(
      query = "peace",
    ),
    requestOptions = RequestOptions(
      headers = buildMap {
        put("X-Algolia-User-ID", "user42")
      },
    ),
  )
  ```

  ```php PHP theme={"system"}
  $response = $client->searchSingleIndex(
      'INDEX_NAME',
      ['query' => 'peace',
      ],
      [
          'headers' => [
              'X-Algolia-User-ID' => 'user42',
          ],
      ]
  );
  ```

  ```python Python theme={"system"}
  response = client.search_single_index(
      index_name="INDEX_NAME",
      search_params={
          "query": "peace",
      },
      request_options={
          "headers": loads("""{"X-Algolia-User-ID":"user42"}"""),
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.search_single_index(
    "INDEX_NAME",
    Algolia::Search::SearchParamsObject.new(query: "peace"),
    {:header_params => {"X-Algolia-User-ID" => "user42"}}
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams = Some(
        SearchParamsObject(
          query = Some("peace")
        )
      ),
      requestOptions = Some(
        RequestOptions
          .builder()
          .withHeader("X-Algolia-User-ID", "user42")
          .build()
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response: SearchResponse = try await client.searchSingleIndex(
      indexName: "INDEX_NAME",
      searchParams: SearchSearchParams.searchSearchParamsObject(SearchSearchParamsObject(query: "peace")),
      requestOptions: RequestOptions(
          headers: ["X-Algolia-User-ID": "user42"]
      )
  )
  ```
</CodeGroup>

## Accessing secured data

You can't rely on filters to select what data to return to a specific user because users can change these parameters.
If all you have to pick specific data is query time filters, anyone can alter these to access the data of another user.

To properly restrict access of a user to only their data (and the public data), you need to use [secured API keys](/doc/guides/security/api-keys).

Without MCM, you need to set the filters to authorize manually.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = client.GenerateSecuredApiKey(
    "ALGOLIA_SEARCH_API_KEY",
    new SecuredApiKeyRestrictions { Filters = "user:user42 AND user:public" }
  );
  ```

  ```dart Dart theme={"system"}
  final response = client.generateSecuredApiKey(
    parentApiKey: "ALGOLIA_SEARCH_API_KEY",
    restrictions: SecuredApiKeyRestrictions(
      filters: "user:user42 AND user:public",
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.GenerateSecuredApiKey(
  	"ALGOLIA_SEARCH_API_KEY",
  	search.NewEmptySecuredApiKeyRestrictions().SetFilters("user:user42 AND user:public"))
  if err != nil {
  	// handle the eventual error
  	panic(err)
  }
  ```

  ```java Java theme={"system"}
  client.generateSecuredApiKey("ALGOLIA_SEARCH_API_KEY", new SecuredApiKeyRestrictions().setFilters("user:user42 AND user:public"));
  ```

  ```js JavaScript theme={"system"}
  const response = client.generateSecuredApiKey({
    parentApiKey: "ALGOLIA_SEARCH_API_KEY",
    restrictions: { filters: "user:user42 AND user:public" },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response = client.generateSecuredApiKey(
    parentApiKey = "ALGOLIA_SEARCH_API_KEY",
    restrictions = SecuredApiKeyRestrictions(
      filters = "user:user42 AND user:public",
    ),
  )
  ```

  ```php PHP theme={"system"}
  $response = $client->generateSecuredApiKey(
      'ALGOLIA_SEARCH_API_KEY',
      ['filters' => 'user:user42 AND user:public',
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.generate_secured_api_key(
      parent_api_key="ALGOLIA_SEARCH_API_KEY",
      restrictions={
          "filters": "user:user42 AND user:public",
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.generate_secured_api_key(
    "ALGOLIA_SEARCH_API_KEY",
    Algolia::Search::SecuredApiKeyRestrictions.new(filters: "user:user42 AND user:public")
  )
  ```

  ```scala Scala theme={"system"}
  val response = client.generateSecuredApiKey(
    parentApiKey = "ALGOLIA_SEARCH_API_KEY",
    restrictions = SecuredApiKeyRestrictions(
      filters = Some("user:user42 AND user:public")
    )
  )
  ```

  ```swift Swift theme={"system"}
  let response = try client.generateSecuredApiKey(
      parentApiKey: "ALGOLIA_SEARCH_API_KEY",
      restrictions: SecuredApiKeyRestrictions(filters: "user:user42 AND user:public")
  )
  ```
</CodeGroup>

With MCM, you still need to generate secured API keys, but all you have to do is provide the `userToken`. It automatically adds the right filters to authorize.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = client.GenerateSecuredApiKey(
    "ALGOLIA_SEARCH_API_KEY",
    new SecuredApiKeyRestrictions { UserToken = "user42" }
  );
  ```

  ```dart Dart theme={"system"}
  final response = client.generateSecuredApiKey(
    parentApiKey: "ALGOLIA_SEARCH_API_KEY",
    restrictions: SecuredApiKeyRestrictions(userToken: "user42"),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.GenerateSecuredApiKey(
  	"ALGOLIA_SEARCH_API_KEY",
  	search.NewEmptySecuredApiKeyRestrictions().SetUserToken("user42"))
  if err != nil {
  	// handle the eventual error
  	panic(err)
  }
  ```

  ```java Java theme={"system"}
  client.generateSecuredApiKey("ALGOLIA_SEARCH_API_KEY", new SecuredApiKeyRestrictions().setUserToken("user42"));
  ```

  ```js JavaScript theme={"system"}
  const response = client.generateSecuredApiKey({
    parentApiKey: "ALGOLIA_SEARCH_API_KEY",
    restrictions: { userToken: "user42" },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response = client.generateSecuredApiKey(
    parentApiKey = "ALGOLIA_SEARCH_API_KEY",
    restrictions = SecuredApiKeyRestrictions(
      userToken = "user42",
    ),
  )
  ```

  ```php PHP theme={"system"}
  $response = $client->generateSecuredApiKey(
      'ALGOLIA_SEARCH_API_KEY',
      ['userToken' => 'user42',
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.generate_secured_api_key(
      parent_api_key="ALGOLIA_SEARCH_API_KEY",
      restrictions={
          "userToken": "user42",
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.generate_secured_api_key(
    "ALGOLIA_SEARCH_API_KEY",
    Algolia::Search::SecuredApiKeyRestrictions.new(user_token: "user42")
  )
  ```

  ```scala Scala theme={"system"}
  val response = client.generateSecuredApiKey(
    parentApiKey = "ALGOLIA_SEARCH_API_KEY",
    restrictions = SecuredApiKeyRestrictions(
      userToken = Some("user42")
    )
  )
  ```

  ```swift Swift theme={"system"}
  let response = try client.generateSecuredApiKey(
      parentApiKey: "ALGOLIA_SEARCH_API_KEY",
      restrictions: SecuredApiKeyRestrictions(userToken: "user42")
  )
  ```
</CodeGroup>

<Note>
  With MCM, you always need to provide the `X-Algolia-User-ID` header, even when the query contains the `userToken` parameter.
  The header is required to route the requests efficiently.
</Note>

## Index configuration and API keys

When performing actions that have global impact on all clusters, such as using [`addApiKey`](/doc/libraries/sdk/v1/methods/add-api-key) or [`setSettings`](/doc/libraries/sdk/v1/methods/set-settings), you don't need to provide the `X-Algolia-User-ID` header.
