---
title: "Errors and limits"
description: "Stable error codes to switch on, why 402 and 429 differ, what an error never contains, and the rate limits that exist today."
canonical: "https://gtable.app/docs/api/errors"
updated: "2026-10-05"
---

# Errors and limits

Every failure has the same shape, on both APIs and in MCP tool results:

```json
{ "error": { "code": "forbidden", "message": "…", "details": {} } }
```

Switch on `code`. The codes are stable across versions; the message is a sentence for a person
and may be reworded.

## Codes

| Code                | HTTP | Means                                                                                  |
| ------------------- | ---- | -------------------------------------------------------------------------------------- |
| `unauthenticated`   | 401  | No usable credential, or one from the other plane                                      |
| `forbidden`         | 403  | Your permissions or your credential's scopes do not cover this                         |
| `not_found`         | 404  | No such thing, or none you can see                                                     |
| `conflict`          | 409  | Something changed underneath you, or an idempotency key was reused for another request |
| `validation_failed` | 422  | The request is wrong; the message names the parameter or key                           |
| `rate_limited`      | 429  | Too many requests: wait and retry                                                      |
| `payment_required`  | 402  | The plan or the credit does not allow it: waiting will not help                        |
| `not_implemented`   | 501  | Not available on this deployment                                                       |
| `internal`          | 500  | Our fault. Retry with the same `Idempotency-Key`                                       |

## What an error never contains

The name of a field you cannot read. "You cannot read `salary`" tells you a field called
`salary` exists, which is exactly what the rule was hiding. Errors name the operation, never
the thing being protected, and a hidden field gets the same answer as a field that never
existed.

Likewise, something you cannot reach (missing, deleted, another organisation's) is the same
`404` with the same sentence, whichever it is.

## Saved, with warnings

Email, URL and phone fields keep the text you send. A suspect format still succeeds, and the
response carries a `warnings` array (`code: "suspect_format"`, with the record, the field and a
message) for the fields you can see. Do not retry a write because it has warnings.

Invalid numbers, impossible dates, unknown choices and values over a field's limit fail with
`validation_failed`. Send numbers or numeric strings, and ISO dates such as `2026-09-22`;
timestamps must carry a timezone. A failed create batch saves none of its rows.

## Why 402 and 429 differ

A rate limit is about speed and clears on its own. A spend limit is about money and does not.
Conflating them would tell someone who ran out of credit to wait, and they would wait forever.

## Rate limits today

- **Signing in and the OAuth endpoints** are limited per address per minute and answer `429`
  with `rate_limited` beyond that.
- **`/v1` and MCP**, on both planes, are limited per credential (per person for a browser
  session): 1,200 requests a minute and 300 per 10 seconds. A request with no credential is
  limited per address, 600 a minute. Beyond that the answer is `429` with `rate_limited` and a
  `RateLimit-Policy` header describing the limit. A well-behaved client backs off and retries;
  with an `Idempotency-Key`, a retried write is safe.
