# Gotchas > What trips up API clients: session-only routes, error texts, rate limits and draft visibility. ## Session-only routes An API key cannot call these routes. They answer `403` with a plain-text body and no JSON: ``` HTTP/1.1 403 Forbidden This action needs a real session. ``` - `/v1/api-key/*`, `/v1/system/*`, `POST /v1/auth/verify`, `DELETE /v1/auth/register`. - Every activity route under `/v1/activity`. - Writes on sites (`POST`, `PATCH`, `DELETE` under `/v1/site`, so a key cannot create a site) and on authors (`POST`, `PATCH`, `DELETE` under `/v1/author`). - `PATCH /v1/settings`. Variant routes under `/v1/media/{id}/variants` answer `401` to a key instead. The sign-in routes (login, logout, register, password reset) ignore a key and give no `403`. When a task needs one of these routes, stop and tell the user to do it in the app. Do not try to log in with a password. ## Error texts are plain text Errors are not JSON. The body is one sentence; branch on the status code, and show the sentence to the user. | Status | Typical text | | --- | --- | | `400` | `The request cannot be processed due to a mismatch in the format of the body.` | | `401` | `You must be logged in to access this resource.` | | `403` | `This action needs a real session.`, `Only the owner can do this.`, `Only owners and admins can do this.`, `You must verify your account to access this resource.` | | `404` | `Article not found`, `Image not found` | | `429` | `Too many requests. Please try again later.` | An invalid, unknown, expired or revoked key answers `401`, even on public routes. It is never treated as anonymous. ## Rate limit: 429 Each key may make 120 requests per minute. The `429` response carries a `Retry-After` header holding the seconds to wait: ``` HTTP/1.1 429 Too Many Requests Retry-After: 46 Too many requests. Please try again later. ``` Sleep for `Retry-After` seconds, then retry once. Do not retry in a tight loop. Prefer one list call with `size=100` over many single reads. ## Draft versus online - Anonymous callers and unverified users only see online articles. A draft answers `404` with `Article not found`, the same as an id that does not exist. - A verified session or a key sees drafts as well. In lists, `counts.draft` is `0` for anonymous callers and `status=draft` answers an empty page. - `POST /v1/article/` always creates a draft. Publishing is a separate call, `PATCH /v1/article/{id}/publish`. - Sitemaps list online articles only. ## Writes - A title cannot be changed after creation. Create a new article instead. - `PATCH /v1/article/{id}` accepts `body`, `site_id`, `author_ids` and the SEO fields only. `{}` and `null` values answer `400`, except `cover_image_id` and `social_image_id`, where `null` removes the image. - Request bodies are HTML even though responses default to markdown. Send `
` and `