Errors
Arche returns machine-readable JSON errors so failed requests can be retried, routed, or escalated programmatically.
Error envelope
Representative error response:
{
"error": {
"code": "VALIDATION_ERROR",
"http_status": 400,
"message": "from must be <= to",
"details": {},
"trace_id": "req-123"
}
}
Field meanings:
code: stable machine-readable classificationhttp_status: HTTP status that should drive retry or fallback behaviormessage: operator-facing detail for logs and debuggingdetails: structured context for the error, an empty object when there is nonetrace_id: Arche-generated request identifier to include when escalating an issue
Every error carries all five keys. details and trace_id are present even
when empty or null, so one parser handles every error body.
If you sent an X-Request-ID header, Arche echoes it back on the response, so a
failed call has two identifiers: yours and ours. See
Request IDs for how to set them and which to
quote when escalating.
Status codes
The public schema documents route-level error statuses including:
400for invalid parameters or malformed requests, including a query parameter the route does not accept401for missing or invalid authentication403for unauthorized access, and for anything your plan does not include404when a requested resource cannot be found429when the request budget is exceeded
Authentication errors
A 401 carries one of four codes, and each points to a different fix:
- Name
MISSING_CREDENTIALS- Type
- 401
- Description
The request carried neither an
X-Api-Keyheader nor a bearer token.
- Name
MISSING_API_KEY- Type
- 401
- Description
The
X-Api-Keyheader was sent but empty.
- Name
INVALID_API_KEY- Type
- 401
- Description
The key is not one Arche issued. Every Arche key starts with
ak_, so a value without that prefix, such as a key from another service, returns this code. So does a well-formed key that does not exist.
- Name
API_KEY_REVOKED- Type
- 401
- Description
The key existed but has been revoked. Create a new key in the developer portal.
None of these clear on retry. See Authentication.
Plan limits return 403
A capability your plan does not include returns 403 with the code
FEATURE_NOT_AVAILABLE, not an empty result. The details name what is
required and what you hold:
{
"error": {
"code": "FEATURE_NOT_AVAILABLE",
"http_status": 403,
"message": "This feature requires the growth plan or higher.",
"details": { "required_tier": "growth", "current_tier": "developer" },
"trace_id": "…"
}
}
An endpoint your plan does not include, such as bulk export below Scale, returns this code.
On the Free plan, a request for a company beyond the 25 you have chosen returns
403 with a different code, COVERAGE_SELECTION_FULL, because releasing a slot
fixes it as well as upgrading does. See
Choosing companies on Free.
Treat it as a configuration outcome, not a transient failure. Retrying will not
change it, and an empty-result fallback will silently hide it. Branch on
details.required_tier if you need to surface an upgrade path. See
Rate limits for what each plan includes.
Absent is not failed
A request that finds nothing is answered, not failed. On the restatement timeline, three outcomes are easy to confuse:
- The identity has versions and no metric changed.
200,by_metricempty,timeline_severityNONE.total_hopscounts the versions traversed, so it reflects how many versions exist, not how many changes were found. Apple's FY2023 income statement returnstotal_hops: 2withby_metric: {}— two versions that differ in no metric. Readby_metricandtimeline_severityto decide whether anything changed, nevertotal_hops. - The identity has no versions, but the company does.
200withtotal_hops: 0and everything else empty. Asking Apple for fiscal year 1995 answers this way. A period the filer never reported is ordinary history, not a gap, and it is not a404. - The company has no statements at all.
404EDGAR_STATEMENTS_NOT_FOUND.
So a 404 on this route means "no such filer in the corpus", and an empty 200
means "nothing to report for that period". Branching on 404 to detect missing
history will read an unreported period as an unrestated one.
/v1/edgar/statements/restatements/ledger draws the line differently: it needs
two filings to build a ledger and returns 404 EDGAR_STATEMENTS_NOT_FOUND when
the period has fewer, where the timeline returns an empty 200.
An unrecognized statement_type is a caller bug and returns 400 VALIDATION_ERROR, not an empty result.
Neither absent case is a server fault, and neither should be retried. Earlier releases
reported the first case as 502, which made ordinary reporting history look
like an upstream outage.
Why a narrative is missing
GET /v1/ai/companies/{company_id}/narratives answers 404 with one of three
codes. Each one tells you what to do next:
NARRATIVE_AS_OF_UNAVAILABLE: Arche has no figures for the company as of that date. Try a lateras_of.NARRATIVE_INSUFFICIENT_HISTORY: the company has fewer than two comparable periods. A quarter is compared only with an earlier quarter, and a year with an earlier year.NARRATIVE_METRICS_UNAVAILABLE: none of the metrics the narrative cites is reported in both periods. Anothernarrative_typeorstatement_typemay still have one.
None of the three is a fault, and retrying will not change the answer.
Troubleshooting
- Log the request path, query parameters, response status,
trace_id, and theX-Request-IDyou sent. - Treat
400responses as caller bugs to be fixed before retry. - Treat
429responses as backoff signals rather than permanent failures. - Preserve the original request inputs when debugging point-in-time discrepancies.
- Follow Request IDs when you need to escalate.
Do not parse only the HTTP status. Use the structured error body so different failure classes can be handled explicitly.