---
title: "Rotate API key"
method: POST
path: "/api/v1/keys/{keyID}/rotate"
tags: ["Keys"]
---

# Rotate API key

`POST /api/v1/keys/{keyID}/rotate`

## Request

- Base URL: `https://api.uptimemonitoring.com`
- URL: `https://api.uptimemonitoring.com/api/v1/keys/{keyID}/rotate`
- Auth: HTTP bearer

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.

## Path parameters

- `keyID` integer, required

## Request body

- object
  - `label` string: Optional label for the new key. Trimmed of surrounding whitespace before persistence. Must be 3..100 characters after trimming, or omitted entirely.

## Response `201`

New key created. The `key` field contains the plaintext (shown once).

- APIKeyCreated: API key with the one-time plaintext included.
  - `id` integer
  - `account_id` integer
  - `key_prefix` string
  - `label` string, nullable
  - `revoked_at` string, date-time, nullable
  - `created_at` string, date-time
  - `key` string: Full plaintext API key. **Shown once** — store it securely.

## Other responses

- `400`: Invalid request.
- `401`: Missing or invalid credentials.
- `403`: 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.
- `404`: Key not found, belongs to another account, or already revoked.
- `409`: Account has reached the 10-key limit.
- `413`: 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.
- `415`: 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.
- `429`: 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.
- `500`: Two distinct failure shapes share this status, distinguishable by the `error` body: 1. **Generic pre-persistence failure** — key material generation or an unclassified store error before any commit was attempted. Body: `{"error": "internal error"}`. No rotation was committed — the old key is untouched; safe to retry. 2. **Commit outcome unknown** (e.g. a connection drop mid-commit) — the message names the old key's ID and the candidate replacement's ID/prefix — the old key may already be revoked (entering its 5-minute grace window) and the new key may or may not have been created. A `GET /api/v1/keys` that doesn't show the new key is not proof it was never created, even after waiting and listing again: negative reads remain inconclusive. Retrying this same rotation (same old key ID) is safe and cannot mint a second replacement — it either mints exactly one new key (the ambiguous attempt rolled back) or fails with 404 (the ambiguous attempt already landed, in which case the ID/prefix named in this message is the real replacement). Before revoking the candidate, confirm you have a working replacement key — deletion is only rejected while the account has a single `api_keys` row, and that count includes an old key still in its post-rotation grace window, so the guard does not always prevent ending up without a usable key.

---

[API](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api.md) · [All operations](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/llms.txt) · [OpenAPI document](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/revisions/ddcb2a8bc70f?raw)
