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-keys-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 Keys 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: Keys
paths:
/api/v1/keys:
get:
operationId: listKeys
summary: List API keys
description: 'Returns all API keys for the authenticated account, ordered by creation
date (newest first). Key hashes and plaintexts are never exposed.'
tags:
- Keys
security:
- bearerAuth: []
responses:
'200':
description: List of API keys.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/APIKey'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
$ref: '#/components/responses/RateLimited'
post:
operationId: createKey
summary: Create API key
description: 'Creates a new API key. The plaintext key is returned **once** in the `key`
field — it cannot be retrieved again. Max 10 keys per account.'
tags:
- Keys
security:
- bearerAuth: []
requestBody:
content:
application/json:
schema:
type: object
properties:
label:
type: string
minLength: 3
maxLength: 100
description: 'Optional human-readable label for the key. Trimmed of
surrounding whitespace before persistence. Must be 3..100
characters after trimming, or omitted entirely.
'
example: production
responses:
'201':
description: Key created. The `key` field contains the plaintext (shown once).
content:
application/json:
schema:
$ref: '#/components/schemas/APIKeyCreated'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/AccountSuspended'
'409':
description: Account has reached the 10-key limit.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: key limit reached
'413':
$ref: '#/components/responses/RequestEntityTooLarge'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'429':
$ref: '#/components/responses/RateLimited'
'500':
description: "Two distinct failure shapes share this status, distinguishable\nby the `error` body:\n\n1. **Generic pre-persistence failure** — key material generation\n or an unclassified store error before any commit was\n attempted. Body: `{\"error\": \"internal error\"}`. No key was\n written; safe to retry.\n2. **Commit outcome unknown** (e.g. a connection drop\n mid-commit) — the message names the candidate key's ID and\n prefix; a key with that ID/prefix may or may not have been\n created. A `GET /api/v1/keys` that doesn't show it is not\n proof it was never created (a commit can become visible\n moments after a read on another connection), and this stays\n true even after waiting and listing again: negative reads\n remain inconclusive. Do not automatically create another\n key. If the candidate does appear and another usable key\n already exists, revoke the candidate directly; if it is the\n account's only key, authenticate via a dashboard session\n (not the withheld candidate) to create and verify a\n replacement first — `DELETE /api/v1/keys/{keyID}` refuses to\n remove an account's last key regardless of whether its\n plaintext was ever received.\n"
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
genericFailure:
summary: Generic pre-persistence failure (no key written)
value:
error: internal error
commitOutcomeUnknown:
summary: Commit outcome unknown (candidate may exist)
value:
error: 'key creation outcome unknown: a key with id 42 and prefix umk_live_ab12cd may or may not have been created. Listing keys and not seeing it does not prove it was not created, even after waiting and listing again — negative reads remain inconclusive. Do not automatically create another key. If it does appear and you already have another usable key, revoke the candidate directly. If it is your only key, deleting it will fail with 409 cannot delete last key: first sign in to the dashboard (GitHub OAuth session, not this key) and create a verified replacement key there, then revoke the candidate. Otherwise contact an operator to confirm the outcome before creating a replacement.'
/api/v1/keys/{keyID}:
delete:
operationId: deleteKey
summary: Delete API key
description: 'Deletes an API key. Deletion is rejected while the account has only
one `api_keys` row, but that guard counts raw rows, not usable
(non-revoked) keys — a key freshly rotated out and still in its
grace window still counts. Confirm a working replacement key before
revoking a candidate key.'
tags:
- Keys
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/keyID'
responses:
'204':
description: Key deleted.
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
description: Key not found or belongs to another account.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Cannot delete the last key on the account.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: cannot delete last key
'429':
$ref: '#/components/responses/RateLimited'
/api/v1/keys/{keyID}/rotate:
post:
operationId: rotateKey
summary: Rotate API key
description: 'Creates a new key and schedules the old key for revocation after a 5-minute
grace window. During the grace window both keys are valid. After the window,
the old key stops working. The rotated key counts toward the 10-key limit
during the grace period.'
tags:
- Keys
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/keyID'
requestBody:
content:
application/json:
schema:
type: object
properties:
label:
type: string
minLength: 3
maxLength: 100
description: 'Optional label for the new key. Trimmed of surrounding
whitespace before persistence. Must be 3..100 characters
after trimming, or omitted entirely.
'
responses:
'201':
description: New key created. The `key` field contains the plaintext (shown once).
content:
application/json:
schema:
$ref: '#/components/schemas/APIKeyCreated'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
description: Key not found, belongs to another account, or already revoked.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Account has reached the 10-key limit.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: key limit reached
'413':
$ref: '#/components/responses/RequestEntityTooLarge'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'429':
$ref: '#/components/responses/RateLimited'
'500':
description: "Two distinct failure shapes share this status, distinguishable\nby the `error` body:\n\n1. **Generic pre-persistence failure** — key material generation\n or an unclassified store error before any commit was\n attempted. Body: `{\"error\": \"internal error\"}`. No rotation\n was committed — the old key is untouched; safe to retry.\n2. **Commit outcome unknown** (e.g. a connection drop\n mid-commit) — the message names the old key's ID and the\n candidate replacement's ID/prefix — the old key may already\n be revoked (entering its 5-minute grace window) and the new\n key may or may not have been created. A `GET /api/v1/keys`\n that doesn't show the new key is not proof it was never\n created, even after waiting and listing again: negative\n reads remain inconclusive. Retrying this same rotation (same\n old key ID) is safe and cannot mint a second replacement —\n it either mints exactly one new key (the ambiguous attempt\n rolled back) or fails with 404 (the ambiguous attempt\n already landed, in which case the ID/prefix named in this\n message is the real replacement). Before revoking the\n candidate, confirm you have a working replacement key —\n deletion is only rejected while the account has a single\n `api_keys` row, and that count includes an old key still in\n its post-rotation grace window, so the guard does not always\n prevent ending up without a usable key.\n"
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
genericFailure:
summary: Generic pre-persistence failure (no rotation committed)
value:
error: internal error
commitOutcomeUnknown:
summary: Commit outcome unknown (candidate replacement may exist)
value:
error: 'key rotation outcome unknown: the old key (id 41) may already be revoked and entering its 5-minute grace window, and a new key with id 42 and prefix umk_live_ab12cd may or may not have been created. Listing keys and not seeing the new key does not prove it was not created, even after waiting and listing again — negative reads remain inconclusive. Retrying this same rotation (same old key ID) is safe and will not create a second replacement: it will either succeed with exactly one new key (the first attempt rolled back) or fail with 404 key not found (the first attempt already landed — in that case the key with id 42 and prefix umk_live_ab12cd above is your real replacement; if you still need its plaintext, rotate again using that key''s ID before the old key''s 5-minute grace window expires). Before revoking the new key, confirm you have a working replacement: the last-key guard only blocks deletion when the account has a single api_keys row, which still counts an old key inside its grace window, so it will not always stop you from ending up without a usable key.'
components:
schemas:
APIKey:
type: object
description: API key metadata (plaintext and hash are never exposed).
properties:
id:
type: integer
format: int64
account_id:
type: integer
format: int64
key_prefix:
type: string
description: 'Prefix for identification: `umk_live_` + first 8 hex chars.'
example: umk_live_a1b2c3d4
label:
type:
- string
- 'null'
description: 'User-assigned label. New labels (created on or after the #115
tightening) are trimmed to 3..100 characters. Legacy labels
persisted before that rule may fall outside this range or carry
untrimmed surrounding whitespace and are returned verbatim.
'
example: production
revoked_at:
type:
- string
- 'null'
format: date-time
description: If set, the key will stop working after this timestamp.
last_used_at:
type:
- string
- 'null'
format: date-time
last_used_ip:
type:
- string
- 'null'
description: IP address of the last request authenticated with this key.
created_at:
type: string
format: date-time
Error:
type: object
required:
- error
properties:
error:
type: string
example:
error: something went wrong
APIKeyCreated:
description: API key with the one-time plaintext included.
type: object
properties:
id:
type: integer
format: int64
account_id:
type: integer
format: int64
key_prefix:
type: string
example: umk_live_a1b2c3d4
label:
type:
- string
- 'null'
revoked_at:
type:
- string
- 'null'
format: date-time
created_at:
type: string
format: date-time
key:
type: string
description: 'Full plaintext API key. **Shown once** — store it securely.
'
example: umk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
parameters:
keyID:
name: keyID
in: path
required: true
schema:
type: integer
format: int64
description: API key ID.
responses:
RateLimited:
description: 'Per-account dispatcher rate limit exceeded (200 requests/minute).
The `Retry-After` header gives the integer seconds until at least
one token has refilled. The `X-RateLimit-*` trio reflects the
bucket whose rejection produced this response — usually the global
200/min cap; on `POST /api/v1/monitors` a 429 may instead reflect
the per-route 20/min creation limit.
'
headers:
Retry-After:
schema:
type: integer
description: Seconds until the next allowed request.
X-RateLimit-Limit:
$ref: '#/components/headers/XRateLimitLimit'
X-RateLimit-Remaining:
$ref: '#/components/headers/XRateLimitRemaining'
X-RateLimit-Reset:
$ref: '#/components/headers/XRateLimitReset'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: account rate limit exceeded; retry in 1s
RequestEntityTooLarge:
description: 'Request body exceeds the 64 KB hard limit. Per docs/SPEC.md
§Hard Limits the dispatcher rejects any payload larger than
65,536 bytes before it is decoded.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: request body exceeds 64 KB limit
Unauthorized:
description: Missing or invalid credentials.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: invalid credentials
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
UnsupportedMediaType:
description: 'The request `Content-Type` is not `application/json`. Write
operations (POST, PUT, PATCH) require that header; a missing or
non-JSON media type is rejected before the body is read.
The `charset` parameter is accepted and ignored, so
`application/json; charset=utf-8` is valid.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: unsupported media type, expected application/json
BadRequest:
description: Invalid request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
headers:
XRateLimitReset:
schema:
type: integer
description: 'Unix epoch seconds at which the bucket would be full again if no
further requests are made. Sub-second fragments are rounded up so
`Reset - now` is never less than the actual refill duration.
'
XRateLimitLimit:
schema:
type: integer
description: 'Bucket capacity for the limiter that produced this response. `200`
for the global per-account dispatcher cap; `20` on a 429 from the
per-route monitor-create limiter.
'
XRateLimitRemaining:
schema:
type: integer
description: 'Tokens left in the bucket after this request was charged
(floor of the post-charge token count). `0` on a 429 means the
request was rejected with the bucket empty.
'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'API key (`umk_live_*`) or session token from OAuth exchange.
'