Alerting¶
The Alerting API lets you manage Kibana alerting rules programmatically: create rules, control their lifecycle (enable, disable, mute, snooze), rotate API keys, and schedule backfill runs. kibana-py exposes it as client.alerting, organized into:
client.alerting.rule— rule CRUD and lifecycle operationsclient.alerting.backfill— backfill (catch-up) run schedulingclient.alerting.health()andclient.alerting.rule_types()— framework-level helpers
Creating Rules¶
A rule needs a name, a consumer (the feature that owns it), a rule_type_id, a check schedule, and rule-type-specific params:
from kibana import Kibana
client = Kibana("http://localhost:5601", api_key="your_api_key")
rule = client.alerting.rule.create(
name="ES query rule",
consumer="alerts",
rule_type_id=".es-query",
schedule={"interval": "1m"},
params={
"searchType": "esQuery",
"size": 100,
"timeWindowSize": 5,
"timeWindowUnit": "m",
"threshold": [10],
"thresholdComparator": ">",
"index": ["logs-*"],
"timeField": "@timestamp",
"esQuery": '{"query": {"match_all": {}}}',
},
tags=["ops"],
)
rule_id = rule.body["id"]
print(f"Created rule: {rule_id}")
Useful optional arguments:
id— a caller-chosen rule ID (otherwise Kibana generates one)actions— a list of actions to run when the rule fires (each references a connector)enabled— whether the rule starts enabled (defaultTrue)notify_when/throttle— notification cadence (throttleis deprecated in favor of per-actionfrequency)alert_delay— e.g.{"active": 3}to require consecutive matching runs before alertingflapping— e.g.{"look_back_window": 10, "status_change_threshold": 3}
Attaching Actions¶
rule = client.alerting.rule.create(
name="CPU alert with notification",
consumer="alerts",
rule_type_id=".index-threshold",
schedule={"interval": "1m"},
params={
"index": ["metrics-*"],
"timeField": "@timestamp",
"aggType": "avg",
"aggField": "system.cpu.total.pct",
"groupBy": "all",
"timeWindowSize": 5,
"timeWindowUnit": "m",
"thresholdComparator": ">",
"threshold": [0.9],
},
actions=[
{
"id": "my-connector-id", # An existing connector
"group": "threshold met",
"params": {"message": "CPU above 90%"},
"frequency": {"notify_when": "onActionGroupChange", "summary": False},
}
],
)
Reading and Searching Rules¶
# Get a rule by ID
rule = client.alerting.rule.get(id=rule_id)
print(rule.body["name"], rule.body["enabled"])
# Search rules
found = client.alerting.rule.find(search="cpu", per_page=10)
for r in found.body["data"]:
print(f" {r['id']}: {r['name']} (enabled={r['enabled']})")
# List available rule types (returns a JSON array)
types = client.alerting.rule_types()
print([t["id"] for t in types.body])
Note
On live Kibana 9.4.3, passing sort_order to find() without sort_field returns HTTP 406 — pass both together. client.alerting.rule_types() returns a bare JSON array, so iterate types.body directly.
Updating Rules¶
update() replaces the rule’s user-editable attributes (name, schedule, params, actions, tags, …). The consumer and rule_type_id cannot be changed:
client.alerting.rule.update(
id=rule_id,
name="ES query rule (tightened)",
schedule={"interval": "30s"},
params={
"searchType": "esQuery",
"size": 100,
"timeWindowSize": 5,
"timeWindowUnit": "m",
"threshold": [5],
"thresholdComparator": ">",
"index": ["logs-*"],
"timeField": "@timestamp",
"esQuery": '{"query": {"match_all": {}}}',
},
)
Rule Lifecycle¶
Enable and Disable¶
client.alerting.rule.disable(id=rule_id)
# Optionally untrack active alerts when disabling
client.alerting.rule.disable(id=rule_id, untrack=True)
client.alerting.rule.enable(id=rule_id)
Mute and Unmute¶
Muting suppresses a rule’s notifications while the rule keeps running:
# Mute all alerts of a rule
client.alerting.rule.mute_all(id=rule_id)
client.alerting.rule.unmute_all(id=rule_id)
# Mute a single alert (instance) of a rule
client.alerting.rule.mute_alert(rule_id=rule_id, alert_id="server-1")
client.alerting.rule.unmute_alert(rule_id=rule_id, alert_id="server-1")
Note
By default the live server validates that the alert instance exists and returns 404 for an alert that has never fired. To pre-emptively mute an alert that has not fired yet, pass validate_alerts_existence=False:
client.alerting.rule.mute_alert(
rule_id=rule_id, alert_id="server-1", validate_alerts_existence=False
)
Snoozing¶
Snoozing suppresses notifications for a scheduled period — useful for planned maintenance (see also Maintenance Windows for space-wide suppression):
# Snooze for one hour starting at a given time
snoozed = client.alerting.rule.snooze(
id=rule_id,
schedule={
"custom": {
"duration": "1h",
"start": "2026-07-04T00:00:00.000Z",
}
},
)
# The response contains the schedule ID needed to remove the snooze
schedule_id = snoozed.body["schedule"]["id"]
client.alerting.rule.unsnooze(rule_id=rule_id, schedule_id=schedule_id)
Rotating the API Key¶
Rules run with an embedded API key. Rotate it after credential changes:
client.alerting.rule.update_api_key(id=rule_id)
Deleting Rules¶
client.alerting.rule.delete(id=rule_id)
Backfills¶
Backfills run a rule against a historical time range — for example after an outage, or when a rule was created late and you want it evaluated over past data. Backfills are supported by detection-style rule types.
# Schedule a backfill over a past window (ISO 8601 timestamps)
result = client.alerting.backfill.schedule(
backfills=[
{
"rule_id": rule_id,
"ranges": [
{
"start": "2026-07-01T00:00:00.000Z",
"end": "2026-07-01T12:00:00.000Z",
}
],
"run_actions": False,
}
]
)
# Find scheduled backfills
backfills = client.alerting.backfill.find(rule_ids=rule_id)
for b in backfills.body["data"]:
print(b["id"], b["status"])
# Get / delete a backfill by ID
backfill = client.alerting.backfill.get(id="backfill-id")
client.alerting.backfill.delete(id="backfill-id")
Note
Two live-server behaviors to be aware of on Kibana 9.4.3:
Scheduling a backfill for an unsupported rule type (e.g.
.es-query) returns HTTP 200 with a per-item error object ("Backfill not supported...") rather than an error status — always inspect the response body.Backfill ranges older than roughly 90 days are rejected.
Framework Health¶
health = client.alerting.health()
print(health.body["is_sufficiently_secure"])
print(health.body["alerting_framework_health"]["execution_health"]["status"])
Space-Scoped Rules¶
Alerting rules are space-scoped. Every method accepts space_id, or use a space-scoped client:
rule = client.alerting.rule.create(
name="Marketing alert",
consumer="alerts",
rule_type_id=".es-query",
schedule={"interval": "5m"},
params={...},
space_id="marketing",
)
# Or scope the client
marketing = client.space("marketing")
found = marketing.alerting.rule.find(search="Marketing*")
Complete Example: Rule Lifecycle¶
from kibana import Kibana
from kibana.exceptions import NotFoundError
with Kibana("http://localhost:5601", api_key="your_api_key") as client:
rule = client.alerting.rule.create(
name="kbnpy-demo-rule",
consumer="alerts",
rule_type_id=".es-query",
schedule={"interval": "1m"},
params={
"searchType": "esQuery",
"size": 100,
"timeWindowSize": 5,
"timeWindowUnit": "m",
"threshold": [10],
"thresholdComparator": ">",
"index": ["logs-*"],
"timeField": "@timestamp",
"esQuery": '{"query": {"match_all": {}}}',
},
enabled=True,
)
rule_id = rule.body["id"]
try:
# Snooze it for planned maintenance
snoozed = client.alerting.rule.snooze(
id=rule_id,
schedule={"custom": {"duration": "1h",
"start": "2026-07-04T00:00:00.000Z"}},
)
client.alerting.rule.unsnooze(
rule_id=rule_id, schedule_id=snoozed.body["schedule"]["id"]
)
# Disable, then re-enable
client.alerting.rule.disable(id=rule_id)
client.alerting.rule.enable(id=rule_id)
finally:
try:
client.alerting.rule.delete(id=rule_id)
except NotFoundError:
pass
Next Steps¶
Set up Connectors for rule actions
Use Maintenance Windows to suppress notifications across many rules
See Error Handling for exception patterns
Browse
examples/alerting_management.pyin the repository for a runnable end-to-end script