Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.keplerinsights.us/llms.txt

Use this file to discover all available pages before exploring further.

Every error response has the same shape:
{
  "error":   "stable_string_code",
  "message": "Human-readable hint",
  "reason":  "Optional extra context (rate-limit endpoints)"
}
Branch on error, not on message. The error string is stable across versions; message may be tightened or expanded.

4xx — caller errors

StatuserrorCauseRecover
400body.domain requiredPOST body had no domain field.Add it.
400async_requires_growthwait=false on Free/Starter.Upgrade tier, or omit wait (sync path is on every tier).
400sandbox: only [...] supportedLive domain with a test key.Use a ki_test_ key for sandbox, ki_live_ key for real.
400test domains require a ki_test_ keyTest domain with a live key.Same — match domain to key prefix.
400invalid_cursorHistory pagination cursor malformed.Drop the cursor (start over) or use the next_cursor we returned.
400window must be one of ['7d','30d','90d']Bad ?window= value on /v1/movers.Use a supported value.
401unauthorizedMissing or invalid X-API-Key.Check the header is set and the key is active. Allow up to 5 min for revocations to propagate.
403free_tier_sandbox_onlyLive key on Free tier attempted a real-domain score. The Free trial is sandbox-only.Use a ki_test_ key against the 4 canned test domains, or upgrade to Starter.
403(varies)Job belongs to a different caller.Job IDs aren’t shareable across keys; check you’re polling with the same account that started the job.
404no score available for this domainGET /v1/score/{domain} against a never-scored domain.Trigger one with POST /v1/score.
404no_historySame shape, history endpoint.Trigger an initial score.
404job not foundUnknown or expired (>30d) job ID.Re-run POST /v1/score?wait=false.
429cold_budget_exhausted (reason: monthly_cap)Account exhausted its tier’s monthly cold-call budget. v1.0 is flat-only — no overage.Upgrade to the next tier, or wait for the next monthly reset (Retry-After header gives seconds remaining). v1.5 will add opt-in metered overage.
429rate limited (reason: per_key_cold_1h)This key hit 200 cold calls in 1 hour.Wait — Retry-After header gives seconds until reset.
429rate limited (reason: account_cold_24h)Account hit 2× tier monthly cold cap inside 24h.Wait, or email support if legitimate spike.

5xx — server / pipeline errors

StatuserrorCauseRecover
500internal errorUnhandled exception.Retry once. If persistent, email support with requestId from headers.
500sandbox_invariant_violatedA sandbox key reached a live-path codepath. Should never fire — alerts us when it does.Retry; email support so we can investigate the regression.
502scoring failedCold pipeline ran but the engine couldn’t produce a score (insufficient data, fetcher exhaustion). Response includes execution_arn for support correlation.Try a different domain, or wait a few hours and retry — fetcher credit exhaustion clears on the daily rollover.
504scoring timeoutCold pipeline exceeded the 60s sync budget. The run is still going server-side.Retry GET /v1/score/{domain} in ~30s — the result will land in the cache when the SFN execution completes. Or use the async path (wait=false, Growth+).

How to handle 504s gracefully

The pattern that works:
import time, requests

def score(domain, key):
    r = requests.post("https://api.keplerinsights.us/v1/score",
                      headers={"X-API-Key": key},
                      json={"domain": domain})
    if r.status_code == 200:
        return r.json()
    if r.status_code == 504:
        # Background run is in flight — poll the cached endpoint
        for _ in range(20):
            time.sleep(5)
            r2 = requests.get(f"https://api.keplerinsights.us/v1/score/{domain}",
                              headers={"X-API-Key": key})
            if r2.status_code == 200:
                return r2.json()
        raise TimeoutError("cold scoring exceeded 2-minute wall time")
    r.raise_for_status()
If you’re on Growth+, prefer async scoring — same pattern but server-side, with explicit job IDs.

What we never do

  • Surface fetcher errors directly. A Crustdata timeout becomes a data_warnings entry in GET /v1/company/{domain}/confidence, not a 5xx. The score is still produced.
  • Return partial data on 200. A 200 response always has the full schema. Missing data appears as neutral defaults (signal score ≈ 50) flagged in confidence.
  • Auto-retry your call on a 4xx. If you sent bad input, retrying doesn’t help.