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 classification
  • http_status: HTTP status that should drive retry or fallback behavior
  • message: operator-facing detail for logs and debugging
  • details: structured context for the error, an empty object when there is none
  • trace_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:

  • 400 for invalid parameters or malformed requests, including a query parameter the route does not accept
  • 401 for missing or invalid authentication
  • 403 for unauthorized access, and for anything your plan does not include
  • 404 when a requested resource cannot be found
  • 429 when 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-Key header nor a bearer token.

  • Name
    MISSING_API_KEY
    Type
    401
    Description

    The X-Api-Key header 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_metric empty, timeline_severity NONE. total_hops counts the versions traversed, so it reflects how many versions exist, not how many changes were found. Apple's FY2023 income statement returns total_hops: 2 with by_metric: {} — two versions that differ in no metric. Read by_metric and timeline_severity to decide whether anything changed, never total_hops.
  • The identity has no versions, but the company does. 200 with total_hops: 0 and 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 a 404.
  • The company has no statements at all. 404 EDGAR_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 later as_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. Another narrative_type or statement_type may 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 the X-Request-ID you sent.
  • Treat 400 responses as caller bugs to be fixed before retry.
  • Treat 429 responses 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.

Was this page helpful?