Errors

The API uses conventional HTTP status codes and always returns errors as JSON. A 2xx status means success; 4xx means the request needs changing. One 3xx is used — 300 asks you to choose between equally-valid matches (see Disambiguation).

At a glance

Status Meaning When
300 Multiple Choices An identifier matches more than one time zone (a bare abbreviation code, or a convert from/to token).
401 Unauthorized The token is missing, malformed, or revoked.
403 Forbidden The account's email address isn't verified.
404 Not Found No resource matches the identifier in the URL.
422 Unprocessable Entity A query parameter failed validation.
429 Too Many Requests You exceeded the rate limit.

Error shape

Most errors return a single message:

{
    "message": "Unauthenticated."
}

Validation errors (422) add an errors object keyed by parameter, with an array of messages per field — show these to developers, not end users:

{
    "message": "The offset must look like -04:00 or bare minutes like -240.",
    "errors": {
        "offset": [
            "The offset must look like -04:00 or bare minutes like -240."
        ]
    }
}

Disambiguation (300)

GET /v1/abbreviations/{code} — and GET /v1/convert for its from/to tokens — return 300 Multiple Choices when an identifier matches more than one named time zone (many share a code — AST, CST, …) or when a country has more than one timezone. Unlike the message-only error shape it returns a candidates list:

{
    "message": "The abbreviation \"AST\" matches 9 time zones. Re-request by slug, or add ?offset= or ?continent= to disambiguate.",
    "candidates": [
        {
            "code": "AST",
            "name": "Atlantic Standard Time",
            "slug": "atlantic-standard-time",
            "continent": "Americas",
            "current": { "utc_offset": "-04:00", "is_dst": false },
            "links": {
                "self": "https://api.timezone.io/v1/abbreviations/atlantic-standard-time"
            }
        },
        {
            "code": "AST",
            "name": "Arabian Standard Time",
            "slug": "arabian-standard-time",
            "continent": "Asia",
            "current": { "utc_offset": "+03:00", "is_dst": false },
            "links": {
                "self": "https://api.timezone.io/v1/abbreviations/arabian-standard-time"
            }
        }
    ]
}

(Remaining candidates trimmed.) Recover by re-requesting with the unique slug from the candidate you meant, or narrow with ?offset= / ?continent=. For convert's country matches, re-request with one of the candidate IANA identifiers.

Treat 300 as a prompt, not a failure: surface the candidates to your user the way a search box surfaces suggestions.

Common cases

  • 401 Unauthenticated. — Check the Authorization: Bearer <token> header. See Authentication.
  • 403 — Verify your email address, then retry. Body: { "message": "Your email address is not verified." }.
  • 404 — The identifier doesn't exist. Double-check spelling and casing (America/New_York, not America/NewYork), and remember to encode + as %2B.
  • 422 — Read errors for the exact parameter and rule that failed.
  • 429 — Back off and retry using the Retry-After header — see rate limits for a worked retry example.