Skip to main content
There are two steps to use tenant tokens with an official SDK: generating the tenant token, and making a search request using that token.

Generate a tenant token with an official SDK

First, import the SDK. Then create a set of search rules:
{
  "patient_medical_records": {
    "filter": "user_id = 1"
  }
}
Search rules must be an object where each key corresponds to an index in your instance. You may configure any number of filters for each index. Next, find your default search API key. Query the get API keys endpoint and inspect the uid field to obtain your API key’s UID:
curl \
  -X GET 'MEILISEARCH_URL/keys' \
  -H 'Authorization: Bearer MASTER_KEY'
const client = new MeiliSearch({ host: 'MEILISEARCH_URL', apiKey: 'masterKey' })
client.getKeys()
client = Client('MEILISEARCH_URL', 'masterKey')
client.get_keys()
$client = new Client('MEILISEARCH_URL', 'masterKey');
$client->getKeys();
Client client = new Client(new Config("MEILISEARCH_URL", "masterKey"));
client.getKeys();
client = MeiliSearch::Client.new('MEILISEARCH_URL', 'masterKey')
client.keys
client := meilisearch.New("MEILISEARCH_URL", meilisearch.WithAPIKey("masterKey"))
client.GetKeys(nil);
MeilisearchClient client = new MeilisearchClient("MEILISEARCH_URL", "masterKey");
var keys = await client.GetKeysAsync();
let client = Client::new("MEILISEARCH_URL", Some("MASTER_KEY")); let keys = client .get_keys() .await .unwrap();
client = try MeiliSearch(host: "MEILISEARCH_URL", apiKey: "masterKey")
client.getKeys { result in
    switch result {
    case .success(let keys):
        print(keys)
    case .failure(let error):
        print(error)
    }
}
var client = MeiliSearchClient('MEILISEARCH_URL', 'masterKey');
await client.getKeys();
For maximum security, you should also define an expiry date for tenant tokens. Finally, send this data to your chosen SDK’s tenant token generator:
import { generateTenantToken } from 'meilisearch/token'

const searchRules = {
  patient_medical_records: {
    filter: 'user_id = 1'
  }
}
const apiKey = 'B5KdX2MY2jV6EXfUs6scSfmC...'
const apiKeyUid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76'
const expiresAt = new Date('2025-12-20') // optional

const token = await generateTenantToken({ apiKey, apiKeyUid, searchRules, expiresAt })
uid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76';
api_key = 'B5KdX2MY2jV6EXfUs6scSfmC...'
expires_at = datetime(2025, 12, 20)
search_rules = {
  'patient_medical_records': {
    'filter': 'user_id = 1'
  }
}
token = client.generate_tenant_token(api_key_uid=uid, search_rules=search_rules, api_key=api_key, expires_at=expires_at)
$apiKeyUid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76';
$searchRules = (object) [
  'patient_medical_records' => (object) [
    'filter' => 'user_id = 1',
  ]
];
$options = [
    'apiKey' => 'B5KdX2MY2jV6EXfUs6scSfmC...',
    'expiresAt' => new DateTime('2025-12-20'),
];

$token = $client->generateTenantToken($apiKeyUid, $searchRules, $options);
Map<String, Object> filters = new HashMap<String, Object>();
filters.put("filter", "user_id = 1");
Map<String, Object> searchRules = new HashMap<String, Object>();
searchRules.put("patient_medical_records", filters);

Date expiresAt = new SimpleDateFormat("yyyy-MM-dd").parse("2025-12-20");
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
TenantTokenOptions options = new TenantTokenOptions();
options.setApiKey("B5KdX2MY2jV6EXfUs6scSfmC...");
options.setExpiresAt(expiresAt);

String token = client.generateTenantToken("85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76", searchRules, options);
uid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76'
api_key = 'B5KdX2MY2jV6EXfUs6scSfmC...'
expires_at = Time.new(2025, 12, 20).utc
search_rules = {
  'patient_medical_records' => {
    'filter' => 'user_id = 1'
  }
}

token = client.generate_tenant_token(uid, search_rules, api_key: api_key, expires_at: expires_at)
searchRules := map[string]interface{}{
  "patient_medical_records": map[string]string{
    "filter": "user_id = 1",
  },
}
options := &meilisearch.TenantTokenOptions{
  APIKey: "B5KdX2MY2jV6EXfUs6scSfmC...",
  ExpiresAt: time.Date(2025, time.December, 20, 0, 0, 0, 0, time.UTC),
}

token, err := client.GenerateTenantToken(searchRules, options);
var apiKey = "B5KdX2MY2jV6EXfUs6scSfmC...";
var expiresAt = new DateTime(2025, 12, 20);
var searchRules = new TenantTokenRules(new Dictionary<string, object> {
  { "patient_medical_records", new Dictionary<string, object> { { "filter", "user_id = 1" } } }
});

token = client.GenerateTenantToken(
  searchRules,
  apiKey: apiKey // optional,
  expiresAt: expiresAt // optional
);
let api_key = "B5KdX2MY2jV6EXfUs6scSfmC...";
let api_key_uid = "6062abda-a5aa-4414-ac91-ecd7944c0f8d";
let expires_at = time::macros::datetime!(2025 - 12 - 20 00:00:00 UTC);
let search_rules = json!({ "patient_medical_records": { "filter": "user_id = 1" } });

let token = client
  .generate_tenant_token(api_key_uid, search_rules, api_key, expires_at)
  .unwrap();
let apiKey = "B5KdX2MY2jV6EXfUs6scSfmC..."
let expiresAt = Date.distantFuture
let searchRules = SearchRulesGroup(SearchRules("patient_medical_records", filter: "user_id = 1"))

client.generateTenantToken(
  searchRules,
  apiKey: apiKey, // optional
  expiresAt: expiresAt // optional
) { (result: Result<String, Error>) in
  switch result {
  case .success(let token):
    print(token)
  case .failure(let error):
    print(error)
  }
}
final uid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76';
final apiKey = 'B5KdX2MY2jV6EXfUs6scSfmC...';
final expiresAt = DateTime.utc(2025, 12, 20);
final searchRules = {
  'patient_medical_records': {
    'filter': 'user_id = 1'
  }
};

final token = client.generateTenantToken(
  uid,
  searchRules,
  apiKey: apiKey, // optional
  expiresAt: expiresAt // optional
);
The SDK will return a valid tenant token.

Make a search request using a tenant token

After creating a token, you must send it your application’s front end. Exactly how to do that depends on your specific setup. Once the tenant token is available, use it to authenticate search requests as if it were an API key:
const frontEndClient = new MeiliSearch({ host: 'MEILISEARCH_URL', apiKey: token })
frontEndClient.index('patient_medical_records').search('blood test')
front_end_client = Client('MEILISEARCH_URL', token)
front_end_client.index('patient_medical_records').search('blood test')
$frontEndClient = new Client('MEILISEARCH_URL', $token);
$frontEndClient->index('patient_medical_records')->search('blood test');
Client frontEndClient = new Client(new Config("MEILISEARCH_URL", token));
frontEndClient.index("patient_medical_records").search("blood test");
front_end_client = MeiliSearch::Client.new('MEILISEARCH_URL', token)

front_end_client.index('patient_medical_records').search('blood test')
client := meilisearch.New("MEILISEARCH_URL", meilisearch.WithAPIKey("masterKey"))
client.Index("patient_medical_records").Search("blood test", &meilisearch.SearchRequest{});
frontEndClient = new MeilisearchClient("MEILISEARCH_URL", token);
var searchResult = await frontEndClient.Index("patient_medical_records").SearchAsync<Patient>("blood test");
let front_end_client = Client::new("MEILISEARCH_URL", Some(token));
let results: SearchResults<Patient> = front_end_client
  .index("patient_medical_records")
  .search()
  .with_query("blood test")
  .execute()
  .await
  .unwrap();
let frontEndClient = MeiliSearch(host: "MEILISEARCH_URL", apiKey: token)

client.index("patient_medical_records")
  .search(parameters) { (result: Result<SearchResult<Record>, Swift.Error>) in
    switch result {
    case .success(let searchResult):
      print(searchResult)
    case .failure(let error):
      print(error)
    }
}
final frontEndClient = MeiliSearchClient('MEILISEARCH_URL', token);
await frontEndClient.index('patient_medical_records').search('blood test');
Applications may use tenant tokens and API keys interchangeably when searching. For example, the same application might use a default search API key for queries on public indexes and a tenant token for logged-in users searching on private data.

Next steps

Generate a token with a third-party library

Create tenant tokens using JWT libraries instead of Meilisearch SDKs.

Generate a token without any library

Build tenant tokens manually by assembling the JWT header, payload, and signature.

Tenant token payload reference

Learn about all available fields in the tenant token payload.

Manage API keys

Create, update, and delete API keys for your Meilisearch instance.