Skip to Content

Errors

Every error response from the Regulatory Snapshot API is a structured problem-details envelope, per RFC 9457 . That means: a stable type URI you can branch on, a machine-readable code, and a human-readable detail, plus a request_id you can quote when something goes wrong.

Every response — error or success — carries an X-Request-Id header. Always include it when filing a bug.

This page walks through the shape, the status-code map, the common codes you’ll see in the wild, and the recovery pattern for each.

The shape

HTTP/1.1 422 Unprocessable Entity Content-Type: application/problem+json; charset=utf-8 X-Request-Id: req_lxr5sm9k2pq8aaa…
{ "type": "https://api.regsn.app/problems/validation", "title": "Validation error", "status": 422, "detail": "horizon must be one of 3, 6, 12, 18, 24, 36", "instance": "/v1/scans", "code": "validation_error", "request_id": "req_lxr5sm9k2pq8aaa…", "errors": [ { "path": "horizon", "code": "invalid", "message": "horizon must be one of 3, 6, 12, 18, 24, 36" } ] }
FieldTypeAlways present?Notes
typeURI stringyesStable identifier for the class of problem. Branch on this if you want, but code is shorter.
titlestringyesShort, human-readable summary.
statusintegeryesThe HTTP status. Mirrors the response line — useful when your HTTP client buries the status.
detailstringyesSpecific, actionable message.
instancestringyesThe request path.
codestringyesMachine-readable error code. This is what you switch on.
request_idstringyesSame value as the X-Request-Id header. Include in bug reports.
errorsarraysometimesPer-field validation errors on 422s.

[!TIP] Prefer code over status when branching. 404 not_found and 404 translation_not_available are both 404, but only one of them suggests the caller try a different language.

Status code map

StatusType slugDefault codeWhen it happens
400invalid_requestinvalid_requestMalformed request (JSON parse error, etc.)
401authauthentication_required / authentication_invalid / key_revokedSee Authentication.
402budget_exhaustedbudget_exhaustedAccount budget pool empty. Top up.
403forbiddenpermission_deniedReserved.
404not_foundnot_found / translation_not_availableResource doesn’t exist or you don’t own it.
409conflictidempotency_key_in_use / idempotency_in_progressSee Idempotency.
422validationvalidation_error / invalid_idempotency_key / key_limit_exceeded / haiku_blocked_on_verifierField-level problems; see errors[].
429rate_limitedrate_limit_exceededSee Rate limits.
500internalinternal_errorServer bug. We get a Sentry alert; you get the request id.
503transienttransient_errorDependency down, retry in a moment.

type is always https://api.regsn.app/problems/<slug>. The slug is stable; you can branch on type === 'https://api.regsn.app/problems/budget_exhausted' in code if you prefer URIs over codes.

Common errors and how to recover

401 authentication_required

You didn’t send an Authorization header. Add Authorization: Bearer regsn_live_….

401 authentication_invalid

The header was malformed, or the key isn’t recognised. Check for typos, trimmed whitespace, or accidentally including the prefix Bearer twice. If the key looks right, it may have been revoked from the dashboard.

401 key_revoked

The key matched a row but revoked_at is set. Create a new key in the dashboard .

402 budget_exhausted

Your account’s budget pool is empty. Top up in the dashboard; the limit lifts immediately on the next request. Reads (GETs) continue to work; only billable operations (POST /v1/scans, POST /v1/exports) fail.

[!WARNING] Don’t auto-retry 402s in a tight loop — you’ll hit the rate limiter and still not get charged. Surface the failure, alert your operations team, then retry once the budget is topped up.

404 not_found

The resource (scan_id, snapshot_id, export_job_id, key id) doesn’t exist, or it belongs to a different account. The API does not distinguish — same response either way — to avoid leaking the existence of other accounts’ resources.

404 translation_not_available

You asked for /executive-narrative?language=fr on a snapshot that doesn’t have a French translation. Either fall back to en, or re-run the scan with translation enabled.

409 idempotency_key_in_use

You reused an Idempotency-Key with a different request body. Generate a new key. See Idempotency.

409 idempotency_in_progress

You retried with an Idempotency-Key whose first request is still running. Wait a few seconds and retry. See Idempotency.

422 validation_error

One or more fields in your request body failed validation. Walk the errors[] array — each entry has a path (e.g. horizon), a code (e.g. invalid), and a message.

{ "code": "validation_error", "detail": "horizon must be one of 3, 6, 12, 18, 24, 36", "errors": [ { "path": "horizon", "code": "invalid", "message": "horizon must be one of 3, 6, 12, 18, 24, 36" }, { "path": "areas", "code": "required", "message": "areas is required and must be a non-empty array" } ] }

422 key_limit_exceeded

You already have 10 active API keys. Revoke one before creating another.

422 haiku_blocked_on_verifier

model: "haiku" isn’t supported on the verifier-aware engines (v4 with realist/auditor enabled, v4.5-alpha, v4.5-beta). Pick sonnet or opus.

429 rate_limit_exceeded

Hit a per-key rate-limit bucket. Wait Retry-After seconds and retry. See Rate limits.

500 internal_error

A server bug. The team gets a Sentry notification keyed to your request_id. Quote the request_id when you file a support ticket — it lets us walk the trace end-to-end.

503 transient_error

A dependency is briefly unavailable (database, engine, etc.). Retry with exponential backoff. Both SDKs do this automatically (3 attempts).

Branching in code

Python

from regsn import RegSn, RegSnError client = RegSn() try: client.scans.create(jurisdictions=["UK"], areas=["AML"], horizon=12) except RegSnError as e: if e.code == "budget_exhausted": notify_finance(e.request_id) elif e.code == "rate_limit_exceeded": # SDK already retried 3 times; defer to the queue. enqueue_for_later() else: raise

JavaScript

import { RegSn, RegSnError } from '@regsn/api'; const client = new RegSn(); try { await client.scans.create({ jurisdictions: ['UK'], areas: ['AML'], horizon: 12 }); } catch (err) { if (err instanceof RegSnError && err.code === 'budget_exhausted') { notifyFinance(err.requestId); } else { throw err; } }

A note on 5xxs

A 5xx response always writes a Sentry event on our side, with the request id, the endpoint, the API key id, and the user id attached. By the time you’ve decided to file a bug, we’ve usually already seen it — but quoting your request_id is what lets us correlate your report to our trace.

See also