Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/uptimemonitoring-com:uptimemonitoring-com-events-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: UptimeMonitoring.com Events API
version: 0.1.0
description: 'API-first uptime monitoring for developers, deploy pipelines, and AI agents.
## Authentication
All `/api/v1` endpoints require a Bearer token — either an API key (`umk_live_*`)
or a session token obtained via GitHub OAuth. The `/mcp` endpoint additionally
accepts OAuth 2.1 JWT access tokens (`eyJ...`) issued via the self-hosted
authorization server documented under the `OAuth` tag.
```
Authorization: Bearer umk_live_abc123...
```
## Rate limits
200 requests/minute per account. Exceeding the limit returns `429` with a
`Retry-After` header.
Responses that pass through the per-account rate-limiter carry the
`X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`
headers (decimal integers; `Reset` is a Unix epoch second at which
the bucket would be full again). In practice this is every
authenticated response under `/api/v1/*` whose request reached the
handler — earlier failures from the auth middleware (e.g. 401 missing
credentials, 403 suspended account) return before the limiter runs
and do NOT carry the trio. Routes mounted outside `/api/v1`
(notably `GET /api/v1/events/stream`, which has its own SSE
middleware stack) are not subject to this limiter and likewise do
not emit these headers. Successful responses on
`POST /api/v1/monitors` reflect the **global** 200/min bucket; the
per-route 20/min creation limit only surfaces its own headers when
it 429s. The trio is documented once here rather than referenced
into every individual response — see `components.headers` for the
schemas.
One exception: once a request presenting an API key enters an
authenticated route group through a registered route or mounted
subtree, while that key remains in the bounded pre-auth cache
(entries expire after 5 minutes, but may be evicted sooner once the
cache is at capacity), the global bucket is charged BEFORE
authentication and authorization run (issue #47) — so for that
cached key only, a `401` (key revoked while still cached), `403`,
`500`, or a `404` for an unmatched path inside a mounted subtree
also carries the `X-RateLimit-*` trio and spends one token. `429`
still comes first whenever the bucket is already empty. An uncached
key (first seen, expired, or evicted early at capacity), a session
token, an OAuth JWT, and any path outside every authenticated group
are all unaffected.
## Browser push notifications
Subscriptions registered via `POST /api/v1/push/subscribe` receive
fire-and-forget Web Push notifications on monitor state transitions
(down/up/flapping); per-monitor 5-minute throttle. No public API change
in this release — dispatch is internal to `internal/push.Dispatcher`.'
contact:
name: UptimeMonitoring.com
url: https://uptimemonitoring.com
license:
name: Proprietary
identifier: LicenseRef-Proprietary
servers:
- url: https://api.uptimemonitoring.com
description: Production
tags:
- name: Events
paths:
/api/v1/events/ticket:
post:
operationId: issueSSETicket
summary: Issue a single-use SSE connection ticket
description: 'Mints a short-lived ticket that the dashboard exchanges for an SSE
connection at `GET /api/v1/events/stream`. Requires Bearer auth.
Ticket TTL is 30 seconds; tickets are single-use.
Rate limited to 20 ticket mints per minute per credential (issue
#339 — an API key is one credential shared by every caller who
presents it; a session/OAuth-JWT caller is bucketed per user, not
per account).'
tags:
- Events
security:
- bearerAuth: []
responses:
'200':
description: Ticket issued.
content:
application/json:
schema:
type: object
required:
- ticket
- expires_in
properties:
ticket:
type: string
description: Opaque single-use token, base64url encoded.
expires_in:
type: integer
description: Seconds until the ticket expires.
example: 30
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: 'Per-credential ticket-mint rate limit exceeded (20
requests/minute, fixed window, keyed the same way as the
connection cap above — not per account). Distinct from the
dispatcher-wide 200/min limit (`components.responses.RateLimited`)
— this limiter only counts ticket mints. The `Retry-After`
header gives the integer seconds until the oldest mint slides
out of the window.
'
headers:
Retry-After:
schema:
type: integer
description: Seconds until the next ticket mint is allowed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: ticket rate limit exceeded
/api/v1/events/stream:
get:
operationId: subscribeSSEStream
summary: Subscribe to the live event stream (SSE)
description: 'Consumes a ticket from `POST /api/v1/events/ticket` and upgrades the
HTTP response to an SSE stream (`text/event-stream`). Each frame is a
JSON envelope with `type`, `monitor_ref`, `occurred_at`, and a
type-specific `data` payload — `monitor_ref` is `""` for an
account-level event (issue #412 replaced the old nullable numeric
`monitor_id` with this always-present string). See
`docs/SSE_LIVE_UPDATES_SPEC.md` for the four event types and their
payloads. Heartbeat comments
(`: heartbeat`) are emitted every 25 seconds.
**Credential re-checked at open AND periodically while connected
(issue #339).** A ticket''s bound credential — the account, the
minting user, and the API key if one was presented — is re-verified
against durable storage both when the ticket is redeemed and, once
the stream is open, every ~30 seconds thereafter. Removing the
minting user from the account, revoking the API key past its grace
window, or suspending the account closes an already-open stream
within roughly 45 seconds on a healthy connection (a 30s recheck
interval + 5s check timeout detects the revocation and cancels the
stream in ~35s, plus up to a further 10s write deadline for an
in-flight write/flush to unblock and the stream to actually close)
— not instantly, and not a hard worst case: the write deadline is
armed only for the duration of a write+flush and cleared between
frames, so it bounds the handler''s own blocking, not a stalled
HTTP/2 connection writer. A transient failure (e.g. a database
hiccup) never closes an open stream on its own; it only delays how
soon a genuine revocation takes effect.'
tags:
- Events
security: []
parameters:
- name: ticket
in: query
required: true
schema:
type: string
description: Single-use ticket from `POST /api/v1/events/ticket`.
responses:
'200':
description: SSE stream opened.
content:
text/event-stream:
schema:
type: string
description: Server-Sent Events stream.
'401':
description: 'Missing, invalid, expired, or already-consumed ticket — or a
ticket whose bound API key has been revoked. A ticket refused
here for a revoked key is also consumed (burned): the same
ticket value cannot be retried even within its remaining TTL.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: 'The ticket''s bound user is no longer a member of the account,
or the account is suspended. Also consumes (burns) the
ticket — it cannot be retried.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: 'Max concurrent SSE connections per credential reached (10 per
credential — an API key, or a user for session/OAuth-JWT
auth). This is a connection-cap rejection, not a rate-window
rejection, so `Retry-After` is intentionally **not** set:
retry depends on a peer disconnecting (no server-side clock
event), and a guessed value would mislead clients into tight
retry loops. Each API key has its own independent 10-slot
budget shared across every caller who presents it; each
session/OAuth-JWT user has their own independent 10-slot
budget on the account, separate from every other user on that
account. **This budget is shared with MCP GET `/mcp`
streams** — REST event streams and MCP streams draw from the
same per-credential counter, so a credential with zero open
MCP streams can still hit this 429 if its REST streams (or
vice versa) hold every slot. This 429''s body text is shared
byte-for-byte with the MCP refusal. This route is mounted
outside the `/api/v1/*` Bearer block, so the dispatcher-wide
200/min limiter (`components.responses.RateLimited`) does not
apply here — the connection cap is the only 429 source. A
429 here does NOT consume the ticket — retry with the same
one once a slot frees.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v1/admin/events/ticket:
post:
operationId: issueAdminSSETicket
summary: Issue a single-use admin aggregate SSE ticket
description: 'Mints a short-lived ticket scoped to the back-office aggregate
SSE stream at `GET /api/v1/admin/events/stream` (the dot-grid
live feed of every monitor across every account). Requires
Bearer auth AND admin role per `auth.RequireAdmin`. Tickets
minted here are stored in a separate ticket store from the
per-account `/api/v1/events/ticket`; an admin ticket presented
to `/api/v1/events/stream` returns 401 and vice versa.
Ticket TTL is 30 seconds, single-use. Per-admin mint rate is 10
per minute (fixed window) — half the per-account rate, since
admins reconnect rarely. See `docs/SSE_LIVE_UPDATES_SPEC.md` §15.'
tags:
- Events
security:
- bearerAuth: []
responses:
'200':
description: Admin ticket issued.
content:
application/json:
schema:
type: object
required:
- ticket
- expires_in
properties:
ticket:
type: string
description: Opaque single-use admin token, base64url encoded.
expires_in:
type: integer
description: Seconds until the ticket expires.
example: 30
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
description: 'Per-admin ticket-mint rate limit exceeded (10 requests/minute,
fixed window). The `Retry-After` header gives the integer
seconds until the oldest mint slides out of the window.
'
headers:
Retry-After:
schema:
type: integer
description: Seconds until the next ticket mint is allowed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: ticket rate limit exceeded
'503':
description: Admin events surface not configured on this dispatcher.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v1/admin/events/stream:
get:
operationId: subscribeAdminSSEStream
summary: Subscribe to the admin aggregate live event stream (SSE)
description: 'Consumes a ticket from `POST /api/v1/admin/events/ticket` and
upgrades the response to an SSE stream that delivers every
event published for every account. Used by the back-office
dot-grid dashboard at `app.uptimemonitoring.com/ops` (SPEC
§Back-Office Dashboard).
Each frame carries the same envelope as
`/api/v1/events/stream` (see `docs/SSE_LIVE_UPDATES_SPEC.md`
§6 + §7); `account_handle` identifies the originating account
(issue #412 — replaced the old numeric `account_id`).
Heartbeat comments (`: heartbeat`) are emitted every 25 s.
Admin membership is re-checked at stream-open time against
the durable `admins` table — a row deleted in the 0-30s
ticket-redeem window cannot keep a long-lived connection
alive. **Also re-checked periodically while connected (issue
#339):** once open, the same account/membership/key check plus
the admin-lookup check both re-run every ~30 seconds. Removing
the minting admin''s account membership, revoking their API key,
suspending the account, or deleting their `admins` row closes
an already-open admin stream within roughly 45 seconds on a
healthy connection (detected within ~35 seconds by the recheck,
plus up to a further 10s write deadline for an in-flight
write/flush to unblock and the stream to actually close) — not
instantly, not a hard worst case (the write deadline is armed
only for the duration of a write+flush and cleared between
frames, so a stalled HTTP/2 connection writer is not bounded by
it), and never due to a merely transient error (e.g. a database
hiccup), which only delays how soon a genuine revocation takes
effect.'
tags:
- Events
security: []
parameters:
- name: ticket
in: query
required: true
schema:
type: string
description: Single-use admin ticket from `POST /api/v1/admin/events/ticket`.
responses:
'200':
description: Admin SSE stream opened.
content:
text/event-stream:
schema:
type: string
description: Server-Sent Events stream, multiplexed across all accounts.
'401':
description: 'Missing/invalid/expired admin ticket; OR admin membership
was revoked between mint and redeem; OR admin status
(the `admins` row) was revoked; OR a per-account ticket was
presented to this admin route. A ticket refused for any of
the DEFINITE reasons above (not "missing/invalid/expired")
is also consumed (burned) — it cannot be retried.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: 'Max concurrent admin SSE connections reached (5 per
admin). Connection-cap rejection, not a rate-window
rejection — `Retry-After` is intentionally not set. Does
NOT consume the ticket — retry with the same one once a
slot frees.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Admin events surface not configured on this dispatcher.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
type: object
required:
- error
properties:
error:
type: string
example:
error: something went wrong
responses:
AccountSuspended:
description: 'Account is currently suspended. On `POST /api/v1/monitors`, and on
a `PUT /api/v1/monitors/{monitorID}` that changes `url` or `type`
(issue #335), this can also be returned by the per-account
port-scan heuristic when the triggering request crossed the
port-fan threshold inside the sliding window (10 minutes by
default; `SCAN_WINDOW`). A host-fan trip does NOT suspend the
account — see the 429 response instead.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: account suspended; contact support
Unauthorized:
description: Missing or invalid credentials.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: invalid credentials
Forbidden:
description: 'Authenticated but the caller is not permitted on this route.
Body is the opaque `{"error":"forbidden"}` envelope used for
every admin-gated 403 across the API; the same shape is used
whether the caller is missing the admin role or the route
otherwise denies them, so clients cannot distinguish the two.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: forbidden
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'API key (`umk_live_*`) or session token from OAuth exchange.
'