The API
Everything a program needs: create a monitor when a service goes live, read your incidents into your own reporting, ask whether something is up. The same account, the same plan limits, and a token you issue and revoke yourself.
A token, and what it can do
Make one under API tokens in your account. It is shown once, we keep only a fingerprint of it, and it belongs to you rather than to the organisation: it can do no more than you can, and it stops working if you leave or your role changes. Choose read, or read and write.
Send it as Authorization: Bearer n404_... on every call. The first one worth making is /api/v1/me, which answers which organisation the token acts for, which role it carries and which of the two scopes it has: a person with two accounts has two tokens and no other way to tell which one is in an environment variable.
What is in version 1
| Call | What it does |
|---|---|
| GET /api/v1/me | What this token is, and which organisation it acts for |
| GET /api/v1/monitors | Every monitor, oldest first |
| POST /api/v1/monitors | Create one, against the same rules the form obeys |
| GET /api/v1/monitors/{id} | One monitor |
| PATCH /api/v1/monitors/{id} | Change its name, address, interval or config, or pause it |
| DELETE /api/v1/monitors/{id} | Delete it, and its history with it |
| GET /api/v1/monitors/{id}/status | What it is doing now, and its uptime over a window |
| GET /api/v1/groups | Every group, with how many monitors are in it |
| POST /api/v1/groups | Create one; a name that exists returns the group that has it |
| GET /api/v1/groups/{id} | One group |
| PATCH /api/v1/groups/{id} | Rename it, or change its description |
| DELETE /api/v1/groups/{id} | Delete it. Its monitors stay, ungrouped |
| GET /api/v1/incidents | Outages, newest first, filtered by state, monitor or period |
| GET /api/v1/incidents/{id} | One incident, with the probes that saw and confirmed it |
| GET /api/v1/maintenance | Maintenance windows and what they cover |
Every id in a path or a body is the UUID, never a number. Writing needs a read and write token and a role that may write: the scope is what you handed to a program, the role is what you were allowed to hand over.
The document is OpenAPI 3.1. It describes every call, the shape of every body, and the bearer token, so a client can be generated from it rather than written: openapi-generator, oapi-codegen and the rest read it as it is. The operations are named for a person, so the generated methods are listMonitors and createMonitor.
Version 1 only ever grows: a field may be added, none is removed or retyped, and a second version would be a second path. So a generated client keeps working, and regenerating it is how you pick up what is new.
A client, if you want one
One file, Python 3.9 or newer, no dependencies. Download it, make it executable, and put your token in the environment. It does everything the API does, because every command is one call to the routes above.
curl -O https://nomore404.com/api/nomore404.py
chmod +x nomore404.py
export N404_TOKEN=n404_...
./nomore404.py monitors list
./nomore404.py monitors add --type https --target shop.example.com
./nomore404.py incidents --state open
The token comes from N404_TOKEN or from a nomore404.env file beside the script, never from a command line flag: an argument is visible in ps to every user on the machine and stays in your shell history.
For an assistant
nomore404_mcp.py serves the same API as tools over the Model Context Protocol, for asking what is down in words rather than in flags. Keep it beside nomore404.py, which it uses for the token and for paging. It reads and never writes: an assistant is a caller that can be talked into things, and a tool that deletes a monitor and its history is one prompt away from being used.
Both are a few hundred lines and worth reading before you run them. Neither is packaged or signed, and nothing stops you generating your own client from the document instead.
Failures, and long lists
One error shape
The status says the category. The body says which failure it was, with a code to branch on and a sentence to read. The sentence may be reworded in any release, so nothing should parse it.
{
"error": {
"code": "monitor_not_found",
"message": "Nothing here with that id."
}
}
Cursors, not offsets
A listing answers with items and next_cursor. Pass the cursor back to get the next page, and stop when it is null. Incidents arrive while you are reading them, and an offset would quietly show you one row twice and never show you the next.
GET /api/v1/incidents?limit=50
GET /api/v1/incidents?limit=50&cursor=...
How much you may ask for
One token may make a hundred and twenty calls a minute. Every answer carries RateLimit-Remaining and RateLimit-Reset, so a well-behaved client can pace itself instead of finding the limit by hitting it. Over the limit is a 429 with Retry-After.