# API errors

> What 400, 401, 403, 404, 422 and 429 from the Nvivio API mean, with the code of each error and how to fix it, from a missing key to the hourly limit.

Every error from the [REST API](https://nvivio.com/docs/api) has an `error` (what is wrong), a `fix` (what to do) and a stable `code` to compare on, in your language ([Errors](https://nvivio.com/docs/api#errors)). Compare on `code`, never on the text. These are the ones people run into most.

## 401: who are you?

| Code | Cause | Fix |
|---|---|---|
| `auth.not_logged_in` | No key was sent. | Send `Authorization: Bearer nv_…`, with the word `Bearer` and a space. |
| `auth.invalid_key` | The key is wrong, incomplete or revoked. | Copy the whole key; you only saw it once when you made it. Lost it? Make a new one in **API and AI assistants** (`/app/api`) and revoke the old one. |
| `auth.session_expired` | A dashboard session expired, or the token sent is not an API key at all (keys start with `nv_`). | Log in again, or send the whole key. |
| `share.invalid` | A [shared link](https://nvivio.com/docs/sharing) that does not work: unknown, expired, revoked, or more than the plan allows now. | Ask whoever gave you the link for a new one. |
| `share.password` | A shared link with a password, without the value from `POST /share/unlock`. | Give the password, then send `Share <token>.<unlock>`. |

## 403: not allowed

| Code | Cause | Fix |
|---|---|---|
| `api.plan` | Your organisation is on Free, which has no API of its own. | Take Starter or above ([The API](https://nvivio.com/docs/plans#the-api)), or connect an [AI assistant](https://nvivio.com/docs/mcp), which reads through the MCP server on every plan. |
| `key.mcp_only` | A key for the MCP server asked for something its tools never ask. | Use it in the [MCP server](https://nvivio.com/docs/mcp) only. For the REST API, make a key of its own on Starter or above ([Authentication](https://nvivio.com/docs/api#authentication)). |
| `key.wordpress_only` | A key for WordPress asked for something the plugin never shows. | Use it in the [WordPress plugin](https://nvivio.com/docs/wordpress) only. For the REST API, make a key of its own on Starter or above ([Authentication](https://nvivio.com/docs/api#authentication)). |
| `auth.key_read_only` | A key with the scope `read` tried to change something. | Make a key with the scope `admin` for changes ([Authentication](https://nvivio.com/docs/api#authentication)). |
| `auth.read_only` | Your role on this site only reads. | Ask an owner or admin to change your role ([Roles](https://nvivio.com/docs/team#roles)). |
| `auth.not_admin` | Only owners and admins can do this. | Ask an owner or admin, of the organisation or of the site. |

Managing a team (inviting, changing roles) is never possible with a key, only as a logged-in person, so a leaked key cannot hand out access.

## 404: not found

`site.not_found` means the site does not exist **or** your key or login has no access to it: Nvivio does not say which, so nobody learns that a site exists ([The site in a path](https://nvivio.com/docs/api#the-site-in-a-path)).

- Check the site in the path: its code (`NV-9Y5FVJQ`) or its domain without `https://`, such as `example.com`.
- A key belongs to one organisation. A site of another organisation needs a key made there.
- Through a [shared link](https://nvivio.com/docs/sharing), `share.not_shown` means the link does not show this: it reads only its own site's numbers, never single visits, labels or settings.
- `GET /v1/sites` lists every site your key can read.

`request.no_endpoint` means the method and path together do not exist, for example a typo in the path or `POST` where only `GET` exists: compare with the [endpoints](https://nvivio.com/docs/api#endpoints).

## 400 and 422: the request is not right

Through a [shared link](https://nvivio.com/docs/sharing), `share.filter` refuses a filter that could single out a visitor (a city, a device model, "is not") and `share.too_few` a view with fewer than 5 visitors. The `fix` says what is missing or wrong, often with an example. Common ones: `400` for a period that does not exist (`period.unknown`, see [Periods](https://nvivio.com/docs/api#periods)) or a filter on a goal or part the site does not have (`goal.unknown`, `part.unknown`), and `422` for a value Nvivio cannot accept, such as a shared domain like `github.io` as a site (`site.domain_shared`).

A filter in the wrong form or with an unknown key is no error: it is left out. When a filter seems to change nothing, check it against the [filter syntax](https://nvivio.com/docs/filters-and-periods#filter-syntax).

## 429: too many requests

| Code | Cause | Fix |
|---|---|---|
| `api.limit` | Your organisation used its requests for this hour: all its API keys together, while keys of the MCP server and for WordPress count apart ([Limits](https://nvivio.com/docs/api#limits)). | Wait for the next hour: the `Retry-After` header says how many seconds. The `fix` names the plan that allows more. |
| `demo.too_many` | Too many requests to the live demo at once, without a key. | Wait a minute and try again. |
| `share.too_many` | Too many requests or passwords through a [shared link](https://nvivio.com/docs/sharing) at once. | Wait a minute and try again. |

Every answer to a request with a key carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`, so your code can slow down before it hits the limit. The dashboard itself is never counted. Need many numbers at once? One request to `GET /v1/sites/periods` returns totals for up to 24 sites, and a breakdown returns up to 500 rows.

## Errors from the script's endpoint

The endpoint that receives your visitors' events, `nviv.io/e`, has errors of its own, such as `403` for an address that is not one of the site's domains. See [No data arrives](https://nvivio.com/docs/no-data#2-does-the-browser-send-anything).
