Errors & Rate Limits
Error Response Format
All errors follow a consistent JSON structure:
{
"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 |
{
"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.
{
"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 Search Errors
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)
{
"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:
{
"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=tipSee pricing for what each plan includes.402means your plan does not include the feature. A quota429haserrors.code: quota_exceeded. A throttle429hasretry_afterand 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:
{
"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=tipCheck your remaining credits withGET /v1/accountbefore 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.
{
"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.
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:
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:
{
"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:
{
"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 |