Uptime Monitoring API: Create and Manage Monitors From Code
An uptime monitoring API lets your own code create, change, pause and read monitors, so monitoring follows your sites instead of a checklist. The common patterns are creating a monitor when a site or customer is added, opening a maintenance window from CI during a deploy, reading status into your own dashboard with a read-only key, and syncing monitors from a list. Keep keys on the server, look a monitor up before creating it, and back off when you get HTTP 429.
An uptime monitoring API lets your code manage monitors the same way it manages servers, DNS records and deploys. Instead of someone remembering to add a monitor for each new site, your signup flow creates one. Instead of a flood of false alerts during every release, your CI pipeline opens a maintenance window first. The four patterns that cover most needs are: create a monitor when a site or customer is created, silence checks during deploys, read status into your own dashboard, and sync monitors from a list you already maintain.
This guide walks through each one with curl, Node and Python examples against a real REST API, plus the details that keep automation from turning into a mess: where keys live, how to avoid duplicates, and what to do when you hit a rate limit.
Why automate monitoring at all
Clicking through a dashboard works for five sites. It stops working when:
- You run many sites. Agencies, hosting providers and SaaS platforms with per-customer domains add and remove sites every week. A manual step gets skipped, and the first sign of an unmonitored site going down is a customer email.
- You deploy often. Every deploy that restarts a service can trip a check. Either people learn to ignore those alerts, which is how alert fatigue starts, or someone has to remember to pause monitoring by hand.
- You onboard customers. If a customer's site, API or status page is part of what you sell, its monitor should exist the moment the customer does, with the right alert contacts attached.
- You want monitoring data elsewhere. An internal dashboard, a weekly report or a support tool is more useful when it shows live status and uptime next to everything else.
The basics of the API
The examples below use the PingRobot REST API, which is available on every plan. The shape is typical of monitoring APIs, so the patterns carry over to other tools.
- Base URL:
https://pingrobot.io/api/v1 - Authentication: an API key sent as
Authorization: Bearer <key>. Create keys in the app under Settings, API keys. A key is shown only once. - JSON in and out, with
Content-Type: application/jsonon every request that has a body. - Times are Unix seconds in UTC.
- Rate limit: 120 requests a minute per key. Over that, you get
429withRetry-After: 60.
Check that a key works with the account route, which returns your plan, its limits and current usage:
curl https://pingrobot.io/api/v1/account \
-H "Authorization: Bearer $PINGROBOT_KEY"
If you would rather not write requests by hand, the OpenAPI 3.1 document at https://pingrobot.io/api/v1/openapi.json describes every route. Import it into Postman, Insomnia or Bruno, or feed it to an OpenAPI generator to get a typed client in your language. Fetching it does not need a key.
Pattern 1: create a monitor when a site is created
When your app provisions a site or a customer adds a domain, create the monitor in the same flow:
curl https://pingrobot.io/api/v1/monitors \
-H "Authorization: Bearer $PINGROBOT_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "http", "name": "acme-shop: storefront", "url": "https://shop.acme.example" }'
A successful call returns 201 and { "monitor": { ... } }. Save monitor.id alongside the site in your own database. You will need it to pause, change or delete the monitor later, and it is the simplest way to stay idempotent.
The same call creates other monitor types. An API health check with a JSON rule (see API monitoring with JSON assertions for what to assert):
{
"type": "http",
"name": "acme-shop: API health",
"url": "https://api.acme.example/health",
"jsonRules": [{ "path": "$.status", "op": "equals", "value": "ok" }],
"alertAfterMin": 2
}
Or a heartbeat for a customer's nightly job. The response includes a heartbeatUrl that the job calls when it finishes, as described in cron job monitoring with heartbeats:
{ "type": "heartbeat", "name": "acme-shop: nightly export", "intervalMin": 1440 }
If you leave out contactIds, a new monitor uses every confirmed alert contact on the account. For per-customer routing, create the contact first with POST /contacts and pass its id in contactIds.
Look up before you create
Retries, double-clicked buttons and re-run jobs all create duplicates if the code blindly POSTs. The API has no idempotency key, so the check is yours to make. Prefer the stored id. If you do not have one, list monitors and match on URL or a unique name:
// Node 18+
const API = "https://pingrobot.io/api/v1";
const headers = { Authorization: `Bearer ${process.env.PINGROBOT_KEY}`, "Content-Type": "application/json" };
async function ensureMonitor({ name, url }) {
const { monitors } = await (await fetch(`${API}/monitors`, { headers })).json();
const existing = monitors.find((m) => m.url === url || m.name === name);
if (existing) return existing;
const res = await fetch(`${API}/monitors`, { method: "POST", headers, body: JSON.stringify({ type: "http", name, url }) });
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).error}`);
return (await res.json()).monitor;
}
Plan limits apply through the API too. When an account is at its monitor limit, the API returns 403 with an error message and "upgrade": true, so surface that message instead of retrying.
Pattern 2: silence checks during a deploy
A deploy that restarts the app for 30 seconds can fail a check and wake someone up. You have two options.
Pause a monitor with PATCH /monitors/{id} and { "active": false }, then send { "active": true } afterwards. This is fine for a single monitor, but if the deploy script dies between the two calls, the monitor stays paused until somebody notices.
Open a one-off maintenance window instead. While a window is active, the monitors it covers are not checked and send no alerts, and the window ends on its own:
NOW=$(date +%s)
curl https://pingrobot.io/api/v1/maintenance \
-H "Authorization: Bearer $PINGROBOT_KEY" \
-H "Content-Type: application/json" \
-d "{ \"name\": \"Deploy $GIT_SHA\", \"repeat\": \"once\", \"startAt\": $NOW, \"endAt\": $((NOW + 900)), \"monitorIds\": [12, 13] }"
Delete the window with DELETE /maintenance/{id} when the deploy finishes, so checks resume immediately. A full deploy-script version, with cleanup on failure, is in scheduled maintenance without false alerts.
Pattern 3: read status into your own dashboard
GET /monitors returns every monitor with its status (pending, up, down or paused), statusSince, lastReason and 30-day uptime. GET /monitors/{id} adds uptime stats, a response-time chart and recent incidents, with ?range=7d, 30d, 90d or 1y for longer history within your plan.
Use a read-only key for this. It can only make GET requests, so a leaked dashboard key cannot delete monitors or change alert contacts. A short Python script that a cron job or backend endpoint can run:
import os, requests
r = requests.get(
"https://pingrobot.io/api/v1/monitors",
headers={"Authorization": f"Bearer {os.environ['PINGROBOT_READ_KEY']}"},
timeout=10,
)
r.raise_for_status()
for m in r.json()["monitors"]:
if m["status"] == "down":
print(f"{m['name']}: down since {m['statusSince']} ({m['lastReason']})")
Two rules for dashboards. First, call the API from your server and cache the result for a minute or so; do not let every page view trigger a request, or a busy dashboard will run into the rate limit. Second, never put the key in frontend code. Keys are meant for servers, and anything in the browser is public.
Pattern 4: sync monitors from a list
If you already keep a list of sites, in a config file, a CMS or a customer table, make it the source of truth and reconcile monitors against it:
- Fetch your list and
GET /monitors. - For each site with no matching monitor,
POST /monitors. - For each monitor whose settings differ,
PATCH /monitors/{id}with only the fields that changed. Fields you leave out stay as they are. - For monitors no longer in the list, decide deliberately.
DELETE /monitors/{id}removes the monitor and its history; pausing with{ "active": false }keeps the history if you might need it for a report.
Run the sync on a schedule or after changes to the list, and log what it did. A sync that silently deletes monitors because the list came back empty is worse than no sync: guard against an empty or suspiciously short list before deleting anything.
Handle errors and the rate limit
Errors come back as JSON with a readable message:
| Status | What to do |
|---|---|
| 400 | Fix the request; the message names the field. |
| 401 | The key is missing, wrong or revoked. Do not retry. |
| 403 | The key is read-only, or the feature or limit is not on your plan. |
| 404 | The route or item does not exist on this account. |
| 429 | Wait Retry-After seconds, then retry. |
A small wrapper handles 429 once for every call:
async function call(path, init = {}) {
for (let attempt = 0; attempt < 3; attempt++) {
const res = await fetch(`${API}${path}`, { ...init, headers });
if (res.status !== 429) return res;
const wait = Number(res.headers.get("Retry-After") ?? 60);
await new Promise((r) => setTimeout(r, wait * 1000));
}
throw new Error("Rate limited");
}
For bulk jobs, such as importing 300 customer sites, pace requests well under 120 a minute rather than relying on retries. The API also adds fields over time, so ignore fields you do not recognize instead of failing on them.
Keep keys safe
- Store keys in environment variables or your CI's secret store, not in the repository.
- Give each integration its own key, so you can revoke one without breaking the others. Revoking takes effect at once.
- Use read-only keys wherever nothing needs to change: dashboards, reports, status widgets.
- A key cannot reach billing, account settings or other keys, but a read-and-write key can still delete monitors. Treat it like a deploy credential.
Start monitoring free — 10 monitors, no credit card.
Frequently asked questions
Should I pause monitors or use a maintenance window during a deploy?
A maintenance window is usually safer. It covers several monitors in one call and ends on its own at the time you set, so a deploy script that crashes halfway does not leave monitoring switched off. Pausing is fine for a single monitor you will resume yourself.
Can I call a monitoring API from JavaScript in the browser?
You should not. Anything shipped to the browser can be read by visitors, including an API key. Call the API from your server, a serverless function or a CI job, and send your frontend only the data it needs.
How do I avoid creating duplicate monitors when a script runs twice?
Store the monitor id your code got back when it created the monitor, and check for it before creating another. If you have no stored id, list the existing monitors and match on the URL or a unique name first.
What should a client do when it receives HTTP 429?
Stop sending requests, wait for the number of seconds in the Retry-After header, then retry. For background jobs, spread work out over time rather than firing every request at once.