New to Kestra?
Use blueprints to kickstart your first workflows.
Monitor API rate-limit response headers, store quota measurements, and alert Slack before exhaustion.
id: api-rate-limit-observer
namespace: company.team
description: |
Make an authenticated API request, inspect rate-limit response headers, and
alert before the remaining quota reaches a configurable safety threshold.
triggers:
- id: hourly
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 * * * *"
disabled: true
inputs:
- id: url
type: STRING
description: API endpoint used for the quota observation request.
defaults: https://api.github.com/rate_limit
required: true
- id: warning_remaining
type: INT
description: Alert when the remaining quota is at or below this number.
defaults: 100
required: true
- id: header_name
type: STRING
description: Case-insensitive response header containing remaining quota.
defaults: x-ratelimit-remaining
required: true
tasks:
- id: observe_quota
type: io.kestra.plugin.scripts.python.Script
description: Query the endpoint and parse its remaining-quota response header.
taskRunner:
type: io.kestra.plugin.core.runner.Process
env:
URL: "{{ inputs.url }}"
WARNING: "{{ inputs.warning_remaining }}"
HEADER: "{{ inputs.header_name }}"
script: |
import json, os, urllib.request
url, header, warning = os.environ["URL"], os.environ["HEADER"].lower(), int(os.environ["WARNING"])
request = urllib.request.Request(url, headers={"User-Agent": "kestra-api-rate-limit-observer", "Accept": "application/json"})
with urllib.request.urlopen(request, timeout=20) as response:
headers = {key.lower(): value for key, value in response.headers.items()}
remaining_raw = headers.get(header)
limit_raw = headers.get("x-ratelimit-limit")
if remaining_raw is None:
raise ValueError(f"response did not contain quota header {header}")
remaining = int(remaining_raw)
report = {"url": url, "remaining": remaining, "limit": int(limit_raw) if limit_raw else None, "warning_threshold": warning, "alert": remaining <= warning}
print("::" + json.dumps({"outputs": {"report": report, "failed_count": int(report["alert"])}}) + "::")
- id: write_report
type: io.kestra.plugin.core.storage.Write
description: Store quota observations for trend analysis.
content: "{{ outputs.observe_quota.vars.report | toJson }}"
extension: ".json"
- id: alert_if_low
type: io.kestra.plugin.core.flow.If
description: Alert only when remaining quota is below the safety threshold.
condition: "{{ outputs.observe_quota.vars.failed_count > 0 }}"
then:
- id: alert_slack
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
messageText: "API quota warning for {{ inputs.url }}: {{
outputs.observe_quota.vars.report.remaining }} requests remain
(threshold {{ inputs.warning_remaining }}). Evidence: {{
outputs.write_report.uri }}"
outputs:
- id: report
type: URI
value: "{{ outputs.write_report.uri }}"
- id: remaining
type: INT
value: "{{ outputs.observe_quota.vars.report.remaining }}"
Avoid surprise 429 responses by polling an API endpoint and reading its
remaining-quota header. Configure url, header_name, and
warning_remaining. Authentication can be supplied through a custom
header in a small task edit using {{ secret('API_TOKEN') }}.
Prerequisites: outbound HTTPS access and SLACK_WEBHOOK_URL. The endpoint
must expose a numeric remaining-quota header. Run manually before enabling
the hourly schedule; report stores the observed limit and threshold.
Links: Python Script, Slack. Created by yashspidey.
