ELK Stack — API Keys
API Keys
API keys provide a lightweight, token-based authentication mechanism for programmatic access to Elasticsearch. Unlike user credentials, API keys can be scoped to specific privileges, given an expiration time, and revoked independently — making them ideal for CI/CD pipelines, microservices, and automated ingestion agents.
Creating an API Key
curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "ingestion-pipeline-key"
}'
Response:
{
"id": "VuaCfGcBCdbkQm-e5aOx",
"name": "ingestion-pipeline-key",
"api_key": "ui2lp2axTNmsyakw9tvNnw",
"encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
}
The encoded value is a Base64 encoding of id:api_key — use it directly in the Authorization header.
Using an API Key
With the Authorization header (preferred):
curl -k -H "Authorization: ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==" \
https://localhost:9200/_cluster/health
With the id and api_key encoded manually:
# Encode id:api_key in Base64
echo -n "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw" | base64
API Key with Role Descriptors
You can scope an API key to specific privileges that are a subset of the creating user’s permissions:
curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "log-reader-key",
"role_descriptors": {
"log_reader": {
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-*", "filebeat-*"],
"privileges": ["read", "view_index_metadata"]
}
]
}
}
}'
This key can only read from logs-* and filebeat-* indices — even though it was created by the elastic superuser.
API Key with Multiple Roles
curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "dashboard-service-key",
"role_descriptors": {
"read_logs": {
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"]
}
]
},
"read_metrics": {
"indices": [
{
"names": ["metrics-*"],
"privileges": ["read"]
}
]
}
}
}'
API Key Expiration
Set an expiration to automatically invalidate keys:
curl -k -u elastic:password -X POST "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "temp-migration-key",
"expiration": "7d",
"role_descriptors": {
"migrator": {
"indices": [
{
"names": ["old-index-*", "new-index-*"],
"privileges": ["read", "write", "create_index"]
}
]
}
}
}'
Supported time units: d (days), h (hours), m (minutes), s (seconds).
Retrieving API Key Information
Get a specific key by ID:
curl -k -u elastic:password "https://localhost:9200/_security/api_key?id=VuaCfGcBCdbkQm-e5aOx"
Get all keys for a user:
curl -k -u elastic:password "https://localhost:9200/_security/api_key?username=elastic"
Get all keys in the cluster:
curl -k -u elastic:password "https://localhost:9200/_security/api_key"
Response:
{
"api_keys": [
{
"id": "VuaCfGcBCdbkQm-e5aOx",
"name": "ingestion-pipeline-key",
"creation": 1743500000000,
"expiration": null,
"invalidated": false,
"username": "elastic",
"realm": "reserved",
"metadata": {}
}
]
}
Invalidating API Keys
Invalidate by ID:
curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"ids": ["VuaCfGcBCdbkQm-e5aOx"]
}'
Invalidate by name:
curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "ingestion-pipeline-key"
}'
Invalidate all keys for a user:
curl -k -u elastic:password -X DELETE "https://localhost:9200/_security/api_key" \
-H "Content-Type: application/json" -d'
{
"username": "elastic"
}'
Cross-Cluster API Keys
For cross-cluster search and replication, create API keys that grant access from a remote cluster:
curl -k -u elastic:password -X POST "https://localhost:9200/_security/cross_cluster/api_key" \
-H "Content-Type: application/json" -d'
{
"name": "remote-cluster-key",
"access": {
"search": [
{
"names": ["logs-*", "metrics-*"]
}
],
"replication": [
{
"names": ["critical-data-*"]
}
]
}
}'
Using API Keys in Beats
Replace username/password with an API key in Filebeat:
# /etc/filebeat/filebeat.yml
output.elasticsearch:
hosts: ["https://es-node-1:9200"]
api_key: "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw"
ssl:
enabled: true
certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
The api_key format is id:api_key (not Base64 encoded — Beats handles the encoding).
Using API Keys in Logstash
# /etc/logstash/conf.d/output.conf
output {
elasticsearch {
hosts => ["https://es-node-1:9200"]
api_key => "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw"
ssl_enabled => true
ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"]
}
}
Service Tokens for Kibana and Fleet
Service tokens are special tokens designed for Kibana and Fleet Server. They are stored in the Elasticsearch keystore and do not expire.
Create a service token for Kibana:
/usr/share/elasticsearch/bin/elasticsearch-service-tokens create \
elastic/kibana kibana-token-1
Output:
SERVICE_TOKEN elastic/kibana/kibana-token-1 = AAEAAWVsYXN0aWMva2liYW5hL2tpYmFuYS10b2tlbi0xOk...
Configure Kibana to use the service token:
# /etc/kibana/kibana.yml
elasticsearch.serviceAccountToken: "AAEAAWVsYXN0aWMva2liYW5hL2tpYmFuYS10b2tlbi0xOk..."
Create a service token for Fleet:
/usr/share/elasticsearch/bin/elasticsearch-service-tokens create \
elastic/fleet-server fleet-token-1
API Key Management in Kibana
Kibana provides a UI for managing API keys:
- Navigate to Stack Management → Security → API Keys
- View all active keys with owner, creation date, and expiration
- Create new keys with the Create API key button
- Invalidate keys by selecting them and clicking Invalidate
Tip: Kibana only shows keys the current user is authorized to manage. Users with the
manage_api_keyprivilege can see all keys; others see only their own.
Quick Reference
| Task | API / Command |
|---|---|
| Create API key | POST /_security/api_key |
| Use API key | Authorization: ApiKey <encoded> header |
| Scope API key | "role_descriptors": { ... } in create request |
| Set expiration | "expiration": "7d" in create request |
| Get key info | GET /_security/api_key?id=<id> |
| Invalidate key | DELETE /_security/api_key with id/name/username |
| Cross-cluster key | POST /_security/cross_cluster/api_key |
| Beats API key | api_key: "id:api_key" in output.elasticsearch |
| Logstash API key | api_key => "id:api_key" in elasticsearch output |
| Kibana service token | elasticsearch-service-tokens create elastic/kibana <name> |
Next: LDAP and SAML — integrate external identity providers.