API reference menu
Getting started
OverviewAuthentication and rate limitExamplesSending
Send a notificationPriority and repeatLinks, images and buttonsSourcesScheduled sendsWebhook templatesReceipts and replies
Receipts and callbacksRepliesTeams
Teams, roles and endpointsChannels and escalationWebhooks and emailAlert email and timelineTeam formsPlans, audit log and transferCompany sign‑in and SCIMThe web consoleAccount
Identity and moving phonesDevices, keys and historyThe Upgrade PassCompatibility
ntfy's publish APIProwl's APIAppendix
Endpoint indexErrors and status codesLimitsPriorities and fieldsGlossaryAuthentication and rate limit
A script only ever needs an API key.
Two kinds of credential#
Scripts use an API key and the app uses an account key. Send one where the other belongs and the error names the mistake, rather than a bare 401.
| Credential | Prefix | What it can do | Where it comes from |
|---|---|---|---|
API key |
nlk_… |
Send notifications. Nothing else. |
Settings › Connect an App in the app |
Account key |
nla_… |
Everything: read history, manage keys and devices, delete the account |
Minted on first launch, kept in the user's iCloud. Scripts never need it. |
Scripts only call POST /v1/notify; the account endpoints
are here for the iOS app's own use.
Sending the key#
Either header works:
X-Nowlark-Key: nlk_…
Authorization: Bearer nlk_…
GET /v1/key/check#
Prove a key is live without spending one of its hourly calls.
GET /v1/key/check
X-Nowlark-Key: nlk_…
{"ok": true, "label": "Home server", "remaining": 1000, "reset_at": 1789665600.0}
401 if the key is unknown or revoked. Safe to call at the top of every run.
Rate limit#
1000 notifications per hour, per key, on a sliding window kept on the key itself, so it
survives a server restart. Exceeding it returns 429 with {"error": "rate_limited"}.
The limit is per key, not per account, which is the reason to give each scanner its own key: one runaway loop cannot silence the others, and rotating one key does not touch the rest.