Skip to content
FeedCentral

Errors

Every non-2xx response from /api/v1/* uses the same stable envelope:

{
  "error": {
    "code": "SCOPE_DENIED",
    "message": "A human-readable description."
  }
}

Branch on error.code, not on the message text — the message is for humans, the code is for your code.

Codes

Code HTTP status Meaning
MALFORMED_REQUEST 400 The request itself is invalid (bad cursor, bad limit, unknown category key, etc.) — checked before authentication/entitlement.
UNAUTHENTICATED 401 The credential is missing, invalid, revoked, or expired.
NOT_FOUND 404 The resource doesn't exist. Also returned for a resource that exists but belongs to a different Customer — these two cases are deliberately indistinguishable to you, by design.
CONFLICT 409 The request is well-formed, but the current state doesn't allow it (e.g. the Site already has an active subscription).
CAPABILITY_NOT_ENTITLED 403 Authenticated and scoped, but your Site's Plan doesn't include this capability at all.
QUOTA_EXHAUSTED 403 The capability is included, but this period's quota is used up.
SCOPE_DENIED 403 The credential is valid, but doesn't hold the scope this endpoint requires.
WRONG_AUDIENCE 403 The credential is valid, but is issued for a different product than what you're calling (e.g. a WordPress Connector credential used against a Public API–only capability, or vice versa). Issue a credential for the correct product instead.
RATE_LIMITED 429 The security rate limit was exceeded. A Retry-After header may be present. See Rate Limits.
INTERNAL_ERROR 500 (occasionally 503, e.g. when a payment provider is temporarily unavailable) Something went wrong on our side. Safe to retry after a short delay.

UNAUTHORIZED exists in the underlying error-code vocabulary but is not currently returned by any live endpoint — treat it as reserved, not something you'll encounter today.

Why 401/403/404 are split the way they are

  • 401 always means "we don't know who you are" (bad/missing/revoked credential).
  • 403 always means "we know who you are, but no" — and is further split into why not (missing scope vs. Plan doesn't include it vs. quota used up) so your integration can react differently (a scope problem needs a new Credential; a quota problem might just need to wait).
  • 404 deliberately never distinguishes "doesn't exist" from "exists but isn't yours" — this is a tenant-isolation safeguard, not a bug. Don't build logic that assumes you can tell them apart.

What's safe to log

error.message is safe to log — it never contains a stack trace, SQL, a connection string, a credential, an internal provider error, or a filesystem path, by construction.