> ## Documentation Index
> Fetch the complete documentation index at: https://klipy.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Why is my API request failing?

> Every Klipy API error returns { object, error: { code, message }, meta }. 401 is a bad key, 403 a missing scope, 429 a rate limit — read the headers.

Every failed Klipy v2 API request returns a JSON envelope: `{ object: "error", error: { code, message, details? }, meta }`. A `401` means your API key is missing, invalid, or expired. A `403` means the key lacks a required scope. A `429` means you're rate limited — read `Retry-After` and the `X-RateLimit-*` headers before retrying.

## What you'll see

Any failed Klipy API v2 request comes back with an HTTP status code in the 4xx or 5xx range and a JSON body shaped as `{ object: "error", error: { code, message, details?, context? }, meta: { request_id, api_version, timestamp } }`. The `error.code` field tells you the failure type (for example `RATE_LIMIT_EXCEEDED`), `error.message` is a human-readable description, and `meta.request_id` is a unique identifier for that specific request — worth saving if you need to report the issue.

For validation failures, `error.details` is an array of field-level issues describing exactly which fields were wrong and why. Not every error includes `details` — it's only present when there's something field-specific to point to.

A `429` response also carries three extra response headers on top of the error body: `Retry-After` (seconds until you can retry), `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`.

This envelope shape is consistent across every failure the Klipy API returns, from a single bad field to a full outage, so client code can parse one shape everywhere rather than branching on status code alone. An optional `error.context` field carries extra debugging context — arbitrary strings, numbers, or booleans — when there's something more specific to add beyond the message, though most errors omit it entirely.

<Frame caption="Screenshot coming soon: Example JSON error envelope (401/403/429) shown in an API client">
  <div style={{padding:'2.5rem 1rem',textAlign:'center',border:'1px dashed currentColor',borderRadius:'12px',opacity:0.55}}>Screenshot coming soon</div>
</Frame>

## Why it happens

**`401` — bad or missing API key.** The `X-Klipy-Api-Key` header (or `Authorization: Bearer <key>`) is missing from the request, or the key itself is invalid, expired, or has been revoked. Create and manage keys under **Settings → API** in the Klipy app.

**`403` — missing scope.** The key is valid but wasn't granted the scope the endpoint requires. Klipy v2 keys are issued with an explicit set of scopes at creation (`resource:action`, for example `contacts:read` or `deals:write`) — there's no all-access scope. A `403` can also happen with a **user-bound** key (scoped to one user's own records) used against data outside that user's ownership; only an **organization-wide** key can read or write any record in the org.

**`429` — rate limited.** Klipy enforces two separate, fixed-window rate limits: **200 requests per minute per API key**, and **1000 requests per minute per organization** (shared across every key in that organization). Exceeding either limit returns `429` with `Retry-After` set to the number of seconds until the window resets.

## What to do now

<Steps>
  <Step title="Read the error code and request ID">
    Every error response includes `error.code` and `meta.request_id`. Note both — the code tells you the failure category, and the request ID is what Klipy support needs if you report the issue.
  </Step>

  <Step title="For a 401, check your key in Settings → API">
    Confirm the key hasn't been revoked or expired, and that you're sending it correctly — either the `X-Klipy-Api-Key` header or `Authorization: Bearer <key>`. v2 keys use the `klipy_live_<random>` format; a v1 key (`klipy_pk_<id>`) won't authenticate against `/api/v2/*`.
  </Step>

  <Step title="For a 403, check the key's scopes and access level">
    Open the key's settings and confirm it was granted the scope the failing endpoint requires (each endpoint's required scope follows the `resource:action` pattern). If the key is user-bound and you need org-wide access, issue a new organization-wide key instead.

    <Frame caption="Screenshot coming soon: Settings → API, a key's scopes shown for troubleshooting">
      <div style={{padding:'2.5rem 1rem',textAlign:'center',border:'1px dashed currentColor',borderRadius:'12px',opacity:0.55}}>Screenshot coming soon</div>
    </Frame>
  </Step>

  <Step title="For a 429, read Retry-After and back off">
    Wait the number of seconds in the `Retry-After` header before retrying. Check `X-RateLimit-Remaining` and `X-RateLimit-Reset` to see how much of your current window is left and when it resets — remember the per-key limit (200/min) and per-organization limit (1000/min) are separate, so a busy organization with multiple keys can still trip the org-wide limit even if any single key looks fine.
  </Step>

  <Step title="For a validation error, read error.details">
    When `error.details` is present, it's an array of field-level issues — fix the specific fields it names rather than guessing at the request body.
  </Step>
</Steps>

## When to contact us

Contact us if a request keeps returning `401` or `403` after you've double-checked the key is active, unrevoked, and has the correct scope for that endpoint, or if you're consistently hitting `429` while your own request volume is clearly well under both the 200-per-minute-per-key and 1000-per-minute-per-organization limits. Also reach out if a response doesn't match the documented envelope shape at all, or if `error.code` doesn't correspond to anything described here.

When you contact us, include the `meta.request_id` from the failing response, the exact endpoint and HTTP method you called, whether the key is organization-wide or user-bound, and roughly when the request happened. The request ID alone lets Klipy trace that specific call server-side, so it's the single most useful thing to include — everything else just speeds up the investigation.

This is worth reporting rather than working around, since a genuine mismatch between your key's configuration and the errors you're seeing usually means something is wrong on Klipy's side — a scope that isn't being recognized correctly, or a rate-limit counter that isn't resetting on schedule — not something you can fix by adjusting your own request pattern.

<AccordionGroup>
  <Accordion title="What does every Klipy API error response look like?">
    A JSON body shaped `{ object: "error", error: { code, message, details?, context? }, meta: { request_id, api_version, timestamp } }`, alongside a 4xx or 5xx HTTP status. `details` only appears for validation failures.
  </Accordion>

  <Accordion title="What's the difference between a 401 and a 403?">
    A 401 means the API key itself is missing, invalid, expired, or revoked — Klipy doesn't recognize it. A 403 means Klipy recognizes the key but it lacks the specific scope (or access level) the endpoint requires.
  </Accordion>

  <Accordion title="How long do I need to wait after a 429?">
    Read the `Retry-After` header on the 429 response — it's the number of seconds until you can safely retry. Don't hardcode a wait time; it reflects the actual reset for whichever limit you hit.
  </Accordion>

  <Accordion title="Are rate limits per API key or per organization?">
    Both, and they apply independently: 200 requests per minute per individual API key, and 1000 requests per minute across the whole organization (all keys combined). Hitting either one returns a 429.
  </Accordion>

  <Accordion title="What should I include when reporting an API error to Klipy?">
    The `meta.request_id` from the failing response, the `error.code`, the endpoint and HTTP method you called, and roughly when it happened. The request ID is what lets Klipy trace the exact call.
  </Accordion>
</AccordionGroup>
