Errors & Rate Limits

Error Response Format

All errors follow a consistent JSON structure:

json
{
  "success": false,
  "message": "Human-readable error description",
  "errors": {},
  "doc_url": null,
  "timestamp": "2026-05-03T10:30:00Z"
}

API controller errors carry this envelope — success is always false, message is human-readable, errors holds machine context (an error code plus per-endpoint details where applicable), and doc_url points to relevant documentation (null when none applies). The edge-level per-IP throttle uses the smaller envelope documented below because it rejects the request before the API controller runs.

Request Deadlines

Most requests return immediately. Domain resolve requests that refresh observation include[] data can wait up to 240 seconds. Configure the client timeout to allow at least 300 seconds for those Domain requests; this is an outer cutoff, not expected latency.

When a Domain site check reaches its wait budget, Domain resolve returns HTTP 200 with the data it can deliver. An unavailable optional Domain section adds no charge. Company paid sections use the exact Company page and do not use this Domain refresh mechanism.

HTTP Status Codes

Status Meaning When
200 Success Request completed
201 Created Resource created
202 Accepted Async operation started (exports)
400 Bad Request Invalid public parameters or filter values
401 Unauthorized Missing or invalid API key
402 Payment Required Feature needs a higher plan, account credits, or connected-app Spending limit
403 Forbidden Demo key used outside documented templates, or the authenticated account or connected app lacks the required scope
404 Not Found Domain or resource doesn't exist
409 Conflict A Company identifier names several Companies, a paid request cannot choose one of several held pages, a Company has no held page for a paid section, or the main company page of a domain is not known; each refusal charges zero credits
422 Unprocessable Entity Validation failed
429 Too Many Requests Credit quota exhausted, or 100 requests/minute/IP throttle exceeded
500 Internal Server Error Unexpected server error
503 Service Unavailable Public capability or dependency is temporarily unavailable

A 402 response from a connected app includes a recovery URL. Follow it to Account → AI integrations to inspect or increase that app's Spending limit.

Free Preview Limits (400)

GET /companies/search/preview is free, so it answers about a crowd and never about a paid value or a later page. Two codes say which limit you met, and both carry a next_action that repeats the same query on paid search:

errors.code When
preview_payload_filter payment_method, supplier_brand or platform was used, or q asked for related companies (e.g. "operators on Pragmatic Play") — see Company Filters
free_page_limit page was greater than 1; the free preview answers the first page only
json
{
  "success": false,
  "message": "Continue this search with paid rows.",
  "errors": {
    "code": "free_page_limit",
    "next_action": {
      "type": "paid_search",
      "method": "GET",
      "href": "/v1/companies/search?q=crypto&page=1&page_size=10&max_credits=10"
    }
  },
  "doc_url": "https://igamingfinder.com/docs/errors.md",
  "timestamp": "2026-05-03T10:30:00Z"
}

Neither response charges credits, and neither returns a count, filter breakdowns or rows.

Company Resolve Errors

GET /companies/resolve, GET /companies/firmographics, and GET /companies/people each take exactly one of company_ref, domain, page_url, or legal_entity. Company names are search text: use companies/search?q=<name> and then resolve with a returned identifier. Base resolve returns one Company or refuses. Each error below charges 0 credits.

Status errors.code When
400 missing_identifier No identifier was sent
400 unsupported_identifier name is not a Company identifier; next_action points to companies/search?q=<value>
400 one_identifier More than one identifier was sent. Supply exactly one nonempty identifier
400 invalid_domain domain is not a valid registrable domain
400 invalid_cursor after on GET /companies/people does not continue the request: it comes from another API key, company page or role, or the people of the page changed. Nothing is charged; start again without after
400 invalid_include Unsupported include[], or role without include[]=people on resolve
400 unsupported_enrichment_option domain_ref, max_age_days or allow_stale_on_error was sent to a Company paid section; a Domain ref and freshness controls do not select/refresh Company page reads
404 company_not_found No Company/page matches the identifier, including an unknown company_ref. Do not confuse this with related search's company_ref_not_found
409 ambiguous_company An identifier names multiple catalogue Companies: the known candidate_count is their count, and next_action lists them
409 ambiguous_company A paid section by company_ref or legal_entity selects a Company holding 2+ standing pages: candidate_count is the actual page count, and next_action is search?related=<company_ref>; provide an explicit page_url for the exact page, which is valid even for a Company holding other pages
409 ambiguous_company A paid include on resolve by domain: resolve names licensees and the brand owner, not a company page. Call people or firmographics with the domain instead
409 ambiguous_company People or firmographics by domain when the main company page of the domain is not known: candidate_count is the number of pages that name the domain, and next_action is search?related=<domain>; list the pages, then supply page_url
404 company_page_not_found People or firmographics by domain when no company page names the domain, the domain is a confirmed shared platform, or it is too broad; no credits are charged
409 company_page_required A catalogue Company has no held page for a paid section. Supply a user-known page_url directly; no empty related search is suggested
422 invalid_page_url page_url is not a company page URL (https, path /company/<slug>)
503 company_page_unavailable The company page, or the pages that name a domain, cannot be read now. Try again later

ambiguous_company names no Companies. Its candidate_count appears when the count is known (Companies for a catalogue collision, pages for a held-page collision or for a domain whose main page is not known). People and firmographics by domain use the main company page of the domain and never several pages. Explicit page_url names its page rather than relying on another held page or a domain ownership guess. If no page is held, company_page_required provides instructions, not a next_action to an empty related search.

json
{
  "success": false,
  "message": "This identifier names 2 companies. Nothing was charged; search to see them.",
  "errors": {
    "code": "ambiguous_company",
    "candidate_count": 2,
    "next_action": {
      "type": "search",
      "method": "GET",
      "href": "/v1/companies/search?related=example.com",
      "preview_href": "/v1/companies/search/preview?related=example.com"
    },
    "billing": { "returned_count": 0, "credits_charged": 0, "billing_reason": "no_payload_delivered" }
  },
  "doc_url": "https://igamingfinder.com/docs/errors.md",
  "timestamp": "2026-09-28T10:30:00Z"
}

For a collision, next_action.href is search?q=<value> for legal_entity, and search?related=<value> for company_ref, domain, and page_url. An unsupported name request also points to search?q=<value>. A single catalogue Company with several held pages instead uses search?related=<company_ref> and the page count. Open next_action.preview_href first to see count and price for free. company_page_required does not have that search action: provide the Company's exact page URL directly.

related on GET /companies/search and GET /companies/search/preview lists the organizations related to one identifier. Each error below charges 0 credits.

Status errors.code When
404 company_ref_not_found The company_ref is unknown
422 related_needs_identifier The value is a name. Search names with q
422 invalid_page_url The value starts with http:// or https:// but is not a company page URL
422 related_not_combinable Another search parameter was sent. related combines only with page, page_size, and max_credits
422 related_not_quotable related was sent to POST /companies/search/quote. Buy the rows with GET /companies/search?related=<value>
422 related_too_broad Too many company pages list this domain to list them
503 related_unavailable The related pages cannot be read now. Try again later

Authentication Errors (401)

json
{
  "success": false,
  "message": "Unauthorized",
  "errors": {},
  "doc_url": "https://igamingfinder.com/docs/authentication.md",
  "timestamp": "2026-05-03T10:30:00Z"
}

Check that your API key is valid and passed via X-API-Key or Authorization: Bearer header.

Plan Upgrade Required (402)

Returned when your plan doesn't include the requested feature (e.g. exports require Starter or higher). The machine context lives in errors.code / errors.details:

json
{
  "success": false,
  "message": "This feature requires the Starter plan or higher",
  "errors": {
    "code": "plan_upgrade_required",
    "details": {
      "feature": "exports",
      "current_plan": "free",
      "required_plan": "starter"
    }
  },
  "doc_url": "https://igamingfinder.com/pricing",
  "timestamp": "2026-05-03T10:30:00Z"
}
$ help --topic=tip
See pricing for what each plan includes. 402 means your plan does not include the feature. A quota 429 has errors.code: quota_exceeded. A throttle 429 has retry_after and rate-limit headers.

Quota Exceeded (429)

Returned when your credit balance is exhausted (credits are granted upfront and never expire — see pricing). Machine context lives in errors.code / errors.details:

json
{
  "success": false,
  "message": "Credit balance exhausted — top up or upgrade your plan",
  "errors": {
    "code": "quota_exceeded",
    "details": {
      "granted": 2500,
      "used": 2500,
      "reserved": 0
    }
  },
  "doc_url": "https://igamingfinder.com/docs/errors.md",
  "timestamp": "2026-05-03T10:30:00Z"
}
$ help --topic=tip
Check your remaining credits with GET /v1/account before a paid search, resolve, or export.

Per-IP Throttle (429)

Public /v1 requests are limited to 100 requests per minute per IP. This throttle is separate from credits and can apply to any plan.

json
{
  "error": "Rate limit exceeded",
  "message": "Too many requests. Limit: 100 per 60 seconds",
  "retry_after": 60
}

Every matching public /v1 response includes the draft-11 RateLimit-Policy and RateLimit fields. Draft-11 is work in progress, so clients should parse the named parameters rather than depend on field ordering.

http
RateLimit-Policy: "public-api";q=100;w=60
RateLimit: "public-api";r=99;t=42

q is the request limit, w is the fixed-window size in seconds, r is the remaining request count, and t is the number of seconds until the window resets. These fields describe the per-IP request throttle, not the credit balance.

Throttle responses include r=0 plus these recovery headers:

Header Meaning
Retry-After Seconds until the current fixed window resets
RateLimit-Policy Draft-11 named policy, request limit, and window
RateLimit Draft-11 remaining requests (0) and reset delay
X-RateLimit-Limit Request limit (older header)
X-RateLimit-Remaining Remaining requests (older header); 0 on the throttle response
X-RateLimit-Reset Unix time when the window resets (older header)

Handling Errors

Retry Strategy

For a quota 429 with errors.code: quota_exceeded, top up credits or upgrade the plan. For a throttle 429, wait for Retry-After and retry. Credits are a prepaid balance — granted never expires and does not reset monthly.

For 500 responses, retry with exponential backoff:

python
import time
import requests

def api_call_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        r = requests.get(url, headers=headers)
        if r.status_code == 500:
            time.sleep(2 ** attempt)
            continue
        return r
    raise Exception(f"Failed after {max_retries} retries")

Validation Errors (422)

Field-level errors include the field name and description:

json
{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "domain": ["can't be blank"]
  },
  "timestamp": "2026-05-03T10:30:00Z"
}

Self-Correction for AI Agents

Error responses include a doc_url field linking to relevant documentation:

json
{
  "success": false,
  "message": "Unauthorized",
  "doc_url": "https://igamingfinder.com/docs/authentication.md"
}
Error doc_url
401 Unauthorized /docs/authentication.md
400 Bad Request /docs/errors.md
429 Too Many Requests /docs/errors.md