Skip to content

Per-jurisdiction register freshness

GET
/v1/health/coverage

Per jurisdiction, how long ago we last wrote a licence record from a read of that regulator’s register (the newest last_verified_at among its licences), so the freshness claim can be checked without a key. It dates the latest register read, not every record: one licence’s own last_verified_at can be older.

Freshness SLA — fresh while age_hours is under the jurisdiction’s SLA: 24 h for UKGC, AN, TGC, IOM, ON, SE, ES, BE, DE, CO, UA; 48 h for MGA, CW, KH (24 h where the register is one download or one page, 48 h where reading it takes many requests). Each jurisdiction reports its own sla_hours. status is healthy when every jurisdiction that has been read meets its SLA, degraded when any is stale. A stale register is not an error and not a statement about any licence: it means a change the regulator published since last_successful_scrape is not in our data yet.

No key needed. Without one: 10 requests / IP / hour. A valid key sent with it is honoured (your plan’s limits, counted against your quota).

Each jurisdiction also carries a domain_coverage object: the share of consumer-facing operators (those whose licence type normally comes with a published website) that have at least one domain in our data. See coverage methodology for the per-jurisdiction filter and how to read it.

Freshness per jurisdiction.

Media type application/json
object
status
required
string
Allowed values: healthy degraded
overall_freshness_sla
required

The SLA rule as one English sentence. For display, not for parsing.

string
jurisdictions
required

One entry per covered jurisdiction, keyed by code (UKGC, MGA, CW, KH, AN, TGC, IOM, ON, SE, ES, BE, DE, CO, UA).

object
key
additional properties
object
last_successful_scrape
required

The newest last_verified_at among the jurisdiction’s licences. null when we hold no licence for it yet.

string | null format: date-time
age_hours
required

Hours since then, rounded to the nearest hour.

integer | null
fresh
required

age_hours < sla_hours. null when never read.

boolean | null
sla_hours
required
integer
Allowed values: 24 48
record_count
required

Licence records we hold for the jurisdiction, in every status.

integer
domain_coverage
required

Domain coverage among operators whose licence type normally comes with a published website. See https://igregulator.io/docs/coverage-methodology/

object
operators_with_domain
required
integer
normative_operators
required
integer
coverage_pct
required

Whole percent; null when normative_operators is 0.

integer | null
reason

Only when the jurisdiction is not fresh: last_successful_scrape_older_than_24h / …_48h, or no_licenses_recorded when it was never read.

string
timestamp
required

When this answer was computed.

string format: date-time

Example

{
"status": "degraded",
"overall_freshness_sla": "24 hours for direct-download sources (UKGC, SE, BE, UA) and HTML registers read in one pass (AN, TGC, IOM, ON, ES, DE, CO), 48 hours for heavier scraped sources (MGA, CW, KH).",
"jurisdictions": {
"AN": {
"last_successful_scrape": "2026-10-07T04:00:17.625Z",
"age_hours": 21,
"fresh": true,
"sla_hours": 24,
"record_count": 1592,
"domain_coverage": {
"operators_with_domain": 1397,
"normative_operators": 1414,
"coverage_pct": 99
}
},
"TGC": {
"last_successful_scrape": "2026-10-05T04:15:07.803Z",
"age_hours": 69,
"fresh": false,
"sla_hours": 24,
"record_count": 206,
"domain_coverage": {
"operators_with_domain": 29,
"normative_operators": 181,
"coverage_pct": 16
},
"reason": "last_successful_scrape_older_than_24h"
}
},
"timestamp": "2026-10-08T00:56:25.204Z"
}
X-RateLimit-Limit
integer

Without a key: 10 — requests per IP per clock hour on THIS endpoint (each keyless endpoint has its own counter). With a valid key these three X-RateLimit-* headers describe your monthly quota instead, and the X-Monthly-Quota-* headers are sent too — see any keyed endpoint.

X-RateLimit-Remaining
integer

Requests left this hour, this one counted. A refused request (400) counts.

X-RateLimit-Reset
integer

Unix epoch seconds of the top of the next UTC hour, when the counter starts again.

X-RateLimit-Policy
string

tier=public;limit=10;window=hour without a key; tier=authenticated with one.

RateLimit-Policy
string

The same limit in the IETF draft form, "default";q=10;w=3600. Not sent on a keyed call.

X-Upgrade-URL
string format: uri

Always https://igregulator.io/pricing.

The API key is missing, malformed, unknown or revoked. On an endpoint that works without a key this is only ever sent when a key WAS sent and is not valid — drop the header, or fix the key.

Media type application/json

The body of every non-2xx response.

object
error
required

Human-readable summary. The wording can change; do not branch on it.

string
code
required

The error class. Mostly one per HTTP status, with two exceptions to know: quota_exceeded is a 429 for the monthly request quota and a 403 for a plan cap (watchlist size, webhook endpoints). A 409 carries conflict (it carried invalid_query before 1.24.0). Branch on the HTTP status and details.reason. forbidden is only ever sent to a signed-in MCP connection, never to an API key.

string
Allowed values: invalid_query invalid_slug invalid_license_id invalid_jurisdiction_code invalid_pagination auth_required auth_invalid auth_revoked payment_required forbidden not_found method_not_allowed conflict quota_exceeded rate_limited server_error
details
required

Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.

object
reason
required

Machine-readable refinement of the top-level code — branch on this. Values: invalid_input, missing_required_parameter, conflicting_parameters, not_a_valid_hostname, hostname_too_long, invalid_pagination, invalid_cursor, since_exceeds_retention_window, invalid_event_type, invalid_jurisdiction, as_of_not_supported, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, plan_inactive, plan_ended, export_requires_pro, geo_checks_require_business, watchlist_quota_exceeded, webhook_quota_exceeded, operator_not_found, license_not_found, jurisdiction_not_found, webhook_not_found, dataset_not_found, route_not_found, method_not_allowed, watchlist_duplicate, no_active_secret, quota_exceeded, monthly_request_limit_reached, per_second_limit_exceeded, watchlist_events_poll_limit, export_daily_limit_reached, geo_check_quota_exceeded, internal_error. A rejected webhook URL carries the SSRF check’s reason (invalid_url, invalid_scheme, empty_host, blocked_hostname, unresolvable_host, private_ip_blocked). New values can be added.

string
field

Present only when the error maps to a specific request input (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

A hint a person or an agent can act on. Present on every error today.

string
limit

On a limit error (429, and the 403 plan caps): the ceiling that was reached.

integer
current_usage

On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.

integer
reset_at

When the counter starts again — on the keyless per-IP 429, the monthly quota, the watchlist-poll limit and the export daily limit. Not on the per-second limit (retry after one second).

string format: date-time
plan_tier

The key’s plan, on plan-scoped limit errors and on export_requires_pro.

string
required_plan

On export_requires_pro: the lowest plan that includes the feature.

string
window

On watchlist_events_poll_limit: always hour.

string
key
additional properties
any

Examples

{
"error": "Authorization header required",
"code": "auth_required",
"details": {
"reason": "api_key_missing",
"suggestion": "Attach an API key: `Authorization: Bearer <key>`. No key yet? Start a 14-day free trial of Starter (card required) at https://app.igregulator.io/signup; an existing account creates keys at https://app.igregulator.io/api-keys."
}
}

Only when a key was sent and its account has no plan or a canceled one (plan_inactive), or its plan’s end date has passed (plan_ended). The same request without the Authorization header is answered (within the per-IP limit).

Media type application/json

The body of every non-2xx response.

object
error
required

Human-readable summary. The wording can change; do not branch on it.

string
code
required

The error class. Mostly one per HTTP status, with two exceptions to know: quota_exceeded is a 429 for the monthly request quota and a 403 for a plan cap (watchlist size, webhook endpoints). A 409 carries conflict (it carried invalid_query before 1.24.0). Branch on the HTTP status and details.reason. forbidden is only ever sent to a signed-in MCP connection, never to an API key.

string
Allowed values: invalid_query invalid_slug invalid_license_id invalid_jurisdiction_code invalid_pagination auth_required auth_invalid auth_revoked payment_required forbidden not_found method_not_allowed conflict quota_exceeded rate_limited server_error
details
required

Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.

object
reason
required

Machine-readable refinement of the top-level code — branch on this. Values: invalid_input, missing_required_parameter, conflicting_parameters, not_a_valid_hostname, hostname_too_long, invalid_pagination, invalid_cursor, since_exceeds_retention_window, invalid_event_type, invalid_jurisdiction, as_of_not_supported, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, plan_inactive, plan_ended, export_requires_pro, geo_checks_require_business, watchlist_quota_exceeded, webhook_quota_exceeded, operator_not_found, license_not_found, jurisdiction_not_found, webhook_not_found, dataset_not_found, route_not_found, method_not_allowed, watchlist_duplicate, no_active_secret, quota_exceeded, monthly_request_limit_reached, per_second_limit_exceeded, watchlist_events_poll_limit, export_daily_limit_reached, geo_check_quota_exceeded, internal_error. A rejected webhook URL carries the SSRF check’s reason (invalid_url, invalid_scheme, empty_host, blocked_hostname, unresolvable_host, private_ip_blocked). New values can be added.

string
field

Present only when the error maps to a specific request input (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

A hint a person or an agent can act on. Present on every error today.

string
limit

On a limit error (429, and the 403 plan caps): the ceiling that was reached.

integer
current_usage

On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.

integer
reset_at

When the counter starts again — on the keyless per-IP 429, the monthly quota, the watchlist-poll limit and the export daily limit. Not on the per-second limit (retry after one second).

string format: date-time
plan_tier

The key’s plan, on plan-scoped limit errors and on export_requires_pro.

string
required_plan

On export_requires_pro: the lowest plan that includes the feature.

string
window

On watchlist_events_poll_limit: always hour.

string
key
additional properties
any

Examples

{
"error": "Active subscription required",
"code": "payment_required",
"details": {
"reason": "plan_inactive",
"suggestion": "Current plan_tier: canceled — this account has no active plan. Renew or choose a plan at https://app.igregulator.io/billing (by card, or by invoice for three months or more; Starter starts with a 14-day free trial, card required), or email founder@igregulator.io."
}
}

Without a key: the per-IP limit of this endpoint, 10 requests per hour (rate_limited / quota_exceeded) — every keyless endpoint has its own counter, a refused request (400) counts, and an API key lifts it — Starter (10,000 requests / month) starts with a 14-day free trial, card required, at https://app.igregulator.io/signup. With a key: the limits of the key — see the key_* examples.

Media type application/json

The body of every non-2xx response.

object
error
required

Human-readable summary. The wording can change; do not branch on it.

string
code
required

The error class. Mostly one per HTTP status, with two exceptions to know: quota_exceeded is a 429 for the monthly request quota and a 403 for a plan cap (watchlist size, webhook endpoints). A 409 carries conflict (it carried invalid_query before 1.24.0). Branch on the HTTP status and details.reason. forbidden is only ever sent to a signed-in MCP connection, never to an API key.

string
Allowed values: invalid_query invalid_slug invalid_license_id invalid_jurisdiction_code invalid_pagination auth_required auth_invalid auth_revoked payment_required forbidden not_found method_not_allowed conflict quota_exceeded rate_limited server_error
details
required

Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.

object
reason
required

Machine-readable refinement of the top-level code — branch on this. Values: invalid_input, missing_required_parameter, conflicting_parameters, not_a_valid_hostname, hostname_too_long, invalid_pagination, invalid_cursor, since_exceeds_retention_window, invalid_event_type, invalid_jurisdiction, as_of_not_supported, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, plan_inactive, plan_ended, export_requires_pro, geo_checks_require_business, watchlist_quota_exceeded, webhook_quota_exceeded, operator_not_found, license_not_found, jurisdiction_not_found, webhook_not_found, dataset_not_found, route_not_found, method_not_allowed, watchlist_duplicate, no_active_secret, quota_exceeded, monthly_request_limit_reached, per_second_limit_exceeded, watchlist_events_poll_limit, export_daily_limit_reached, geo_check_quota_exceeded, internal_error. A rejected webhook URL carries the SSRF check’s reason (invalid_url, invalid_scheme, empty_host, blocked_hostname, unresolvable_host, private_ip_blocked). New values can be added.

string
field

Present only when the error maps to a specific request input (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

A hint a person or an agent can act on. Present on every error today.

string
limit

On a limit error (429, and the 403 plan caps): the ceiling that was reached.

integer
current_usage

On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.

integer
reset_at

When the counter starts again — on the keyless per-IP 429, the monthly quota, the watchlist-poll limit and the export daily limit. Not on the per-second limit (retry after one second).

string format: date-time
plan_tier

The key’s plan, on plan-scoped limit errors and on export_requires_pro.

string
required_plan

On export_requires_pro: the lowest plan that includes the feature.

string
window

On watchlist_events_poll_limit: always hour.

string
key
additional properties
any

Examples

{
"error": "Public rate limit reached (10/hour/IP).",
"code": "rate_limited",
"details": {
"reason": "quota_exceeded",
"suggestion": "Start a 14-day free trial of Starter (card required) at https://app.igregulator.io/signup for an API key — 10,000 requests/month, no per-IP cap — and send it as \"Authorization: Bearer <key>\". Or wait until reset_at, the top of the next hour (UTC; also the X-RateLimit-Reset header).",
"limit": 10,
"reset_at": "2026-10-08T01:00:00.000Z"
}
}
X-RateLimit-Limit
integer

Without a key: 10 — requests per IP per clock hour on THIS endpoint (each keyless endpoint has its own counter). With a valid key these three X-RateLimit-* headers describe your monthly quota instead, and the X-Monthly-Quota-* headers are sent too — see any keyed endpoint.

X-RateLimit-Remaining
integer

Requests left this hour, this one counted. A refused request (400) counts.

X-RateLimit-Reset
integer

Unix epoch seconds of the top of the next UTC hour, when the counter starts again.

X-RateLimit-Policy
string

tier=public;limit=10;window=hour without a key; tier=authenticated with one.

RateLimit-Policy
string

The same limit in the IETF draft form, "default";q=10;w=3600. Not sent on a keyed call.

X-Upgrade-URL
string format: uri

Always https://igregulator.io/pricing.

Unexpected server error. Safe to retry.

Media type application/json

The body of every non-2xx response.

object
error
required

Human-readable summary. The wording can change; do not branch on it.

string
code
required

The error class. Mostly one per HTTP status, with two exceptions to know: quota_exceeded is a 429 for the monthly request quota and a 403 for a plan cap (watchlist size, webhook endpoints). A 409 carries conflict (it carried invalid_query before 1.24.0). Branch on the HTTP status and details.reason. forbidden is only ever sent to a signed-in MCP connection, never to an API key.

string
Allowed values: invalid_query invalid_slug invalid_license_id invalid_jurisdiction_code invalid_pagination auth_required auth_invalid auth_revoked payment_required forbidden not_found method_not_allowed conflict quota_exceeded rate_limited server_error
details
required

Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.

object
reason
required

Machine-readable refinement of the top-level code — branch on this. Values: invalid_input, missing_required_parameter, conflicting_parameters, not_a_valid_hostname, hostname_too_long, invalid_pagination, invalid_cursor, since_exceeds_retention_window, invalid_event_type, invalid_jurisdiction, as_of_not_supported, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, plan_inactive, plan_ended, export_requires_pro, geo_checks_require_business, watchlist_quota_exceeded, webhook_quota_exceeded, operator_not_found, license_not_found, jurisdiction_not_found, webhook_not_found, dataset_not_found, route_not_found, method_not_allowed, watchlist_duplicate, no_active_secret, quota_exceeded, monthly_request_limit_reached, per_second_limit_exceeded, watchlist_events_poll_limit, export_daily_limit_reached, geo_check_quota_exceeded, internal_error. A rejected webhook URL carries the SSRF check’s reason (invalid_url, invalid_scheme, empty_host, blocked_hostname, unresolvable_host, private_ip_blocked). New values can be added.

string
field

Present only when the error maps to a specific request input (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

A hint a person or an agent can act on. Present on every error today.

string
limit

On a limit error (429, and the 403 plan caps): the ceiling that was reached.

integer
current_usage

On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.

integer
reset_at

When the counter starts again — on the keyless per-IP 429, the monthly quota, the watchlist-poll limit and the export daily limit. Not on the per-second limit (retry after one second).

string format: date-time
plan_tier

The key’s plan, on plan-scoped limit errors and on export_requires_pro.

string
required_plan

On export_requires_pro: the lowest plan that includes the feature.

string
window

On watchlist_events_poll_limit: always hour.

string
key
additional properties
any

Example

{
"error": "Internal server error",
"code": "server_error",
"details": {
"reason": "internal_error",
"suggestion": "Retry with exponential backoff (1s / 2s / 4s). If the error persists past 4 seconds, surface failure state to the user instead of looping."
}
}