Status-change history for a licence
Every event we recorded for one licence, most recent first — the whole history in one response (not paginated). It starts when WE first recorded the licence (change_type: created), not when it was issued: there is nothing here from before our observation window. Each event carries the regulator page it was read from, what wrote it, and — for events since 2026-09-17 — the SHA-256 of the payload behind it. An event with corrected_at set was later withdrawn: it is kept for the audit trail, never deleted, and must not be quoted as something that happened. _meta.freshness_range spans the oldest and newest event.
Authorizations
Section titled “ Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “ Path Parameters ”The licence’s iGregulator UUID: match.license_id on /v1/check, or id in an operator’s licenses. Not the licence number.
Example
d9832e56-10bb-458b-82af-ac7762f7a502Responses
Section titled “ Responses ”History entries, most-recent first.
object
The licence number as the regulator’s register prints it — except for KH (Kahnawake), TGC (Tobique), IOM (Isle of Man), ON (Ontario), SE (Sweden), ES (Spain), BE (Belgium), DE (Germany) and UA (Ukraine), whose registers publish none. For those nine it is an iGregulator reference we assign: KH/IG/<slug> or KH/CSPA/<slug>, TGC/B2C/<slug> or TGC/B2B/<slug>, IOM/OGRA/<company key>, ON/IGO/<iGaming Ontario directory id>, SE/ONLINE/<holder key> or SE/BETTING/<holder key>, ES/APUESTAS/<DGOJ profile slug>, ES/CONCURSOS/<…> or ES/OTROS-JUEGOS/<…> (one per general licence), BE/APLUS/<Gaming Commission dossier id>, BE/BPLUS/<…> or BE/F1PLUS/<…> (one per licence class and dossier), DE/SLOTS/<holder key>, DE/POKER/<…>, DE/SPORTS/<…> or DE/HORSES/<…> (one per GGL game type), UA/CASINO/<company code>/<decision date>/<decision number>, UA/POKER/<…> or UA/BETTING/<…> (one per PlayCity issuing decision). The reference is stable and /v1/check?license_number= accepts it, but no regulator issued it: do not present it to a user as the licence number.
Every event, most recent first; [] for a licence with none.
object
When we recorded the event — the read that found it, not the date the regulator made the decision.
The status before this event (the LicenseStatus values). null on the first event of a licence (change_type: created).
The status this event set (the LicenseStatus values).
created — the first time we recorded the licence (the start of our observation window, not its issue date). status_change — the status moved. data_update — a field other than the status changed. revoked — how some events label a move to revoked instead of status_change. renewed — in the vocabulary, written by no loader today. Read new_status for what the licence became, never change_type alone, and skip any event with corrected_at set (it was withdrawn).
The regulator page this event was read from.
What wrote the event: scraper:<name>:<run id> for a register read, seal-verify:<source> for the certificate re-verifier, correction:… / backfill:… for our own corrections.
What we observed, in words — e.g. “No longer listed in the public register since 2026-08-04.”
SHA-256 of the exact payload this change was read from, stored on our side. null for events recorded before 2026-09-17 and for runs whose snapshot could not be filed. Ask support for that payload by hash if you need to audit a claim — a hash you can check against your own copy of the regulator’s page is the point.
When that payload was fetched from the regulator.
Set when a later review found this event asserted something we had not read from the regulator. The event is kept for the audit trail; treat it as withdrawn and read correction_note.
Aggregate provenance envelope for list responses. Describes the freshness window of the returned rows.
object
The oldest and newest timestamp among the returned rows — search: when each operator’s licences were last verified; history: the events’ changed_at. Both null when no row was returned.
object
On /v1/licenses/{license_id}/history: the number of distinct source_url values among the events. On /v1/operators/search: always 1 — a placeholder, not a count; do not read it.
Example
{ "freshness_range": { "oldest": "2026-04-18T03:00:00Z", "newest": "2026-04-19T03:45:00Z" }, "total_sources": 1}Headers
Section titled “ Headers ”Your plan’s monthly request quota (not the per-second limit). Not sent on a plan with no monthly quota (Business, Enterprise).
What is left of the monthly quota, this request counted. Not sent on a plan with no monthly quota.
Unix epoch seconds of the start of the next UTC month. Not sent on a plan with no monthly quota.
tier=unlimited on a plan with no monthly quota; otherwise not sent.
Always https://igregulator.io/pricing.
The monthly request quota of your plan, or unlimited.
Requests counted this UTC month, this one included (a request that ends in 4xx / 5xx is not counted). Not sent when the limit is unlimited.
What is left of the monthly quota. Not sent when unlimited.
When the monthly counter starts again: the first of next month, 00:00 UTC, ISO-8601. Not sent when unlimited.
80% of monthly limit used — sent from 80% of the quota on, absent before.
license_id is not a UUID.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
Example
{ "error": "license_id must be a UUID", "code": "invalid_license_id", "details": { "reason": "invalid_input", "field": "license_id", "suggestion": "license_id is the `id` UUID returned by /v1/operators/:slug/licenses — not the human-readable licence_number." }}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.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
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." }}{ "error": "Authorization header must be \"Bearer <key>\"", "code": "auth_invalid", "details": { "reason": "malformed_header", "suggestion": "Header format: `Authorization: Bearer <your-api-key>`." }}{ "error": "API key not recognized", "code": "auth_invalid", "details": { "reason": "api_key_invalid", "suggestion": "Double-check the key. Create a new one at https://app.igregulator.io/api-keys if the previous one was deleted." }}{ "error": "API key has been revoked", "code": "auth_revoked", "details": { "reason": "api_key_revoked", "suggestion": "Generate a new API key at https://app.igregulator.io/api-keys. Revoked keys cannot be restored." }}The key’s plan does not cover this request: the account has no plan or a canceled one (plan_inactive), or its plan’s end date has passed (plan_ended — keys, watchlist and webhook endpoints are kept, and no webhook is sent until the plan is renewed). A trial account is served as Starter. Paid plans are not on sale yet: email founder@igregulator.io and we will sort it out.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
Examples
{ "error": "Active subscription required", "code": "payment_required", "details": { "reason": "plan_inactive", "suggestion": "Current plan_tier: none — 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." }}{ "error": "Plan ended", "code": "payment_required", "details": { "reason": "plan_ended", "suggestion": "This account's business plan ended on 2027-01-22 (UTC). Keys and settings are kept; email founder@igregulator.io to renew." }}No licence has that id.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
Example
{ "error": "License not found", "code": "not_found", "details": { "reason": "license_not_found", "field": "license_id", "suggestion": "No licence with that UUID. Fetch the operator first via /v1/operators/:slug and use the `id` field from the returned licences array." }}A limit of the key was reached. quota_exceeded / monthly_request_limit_reached: the plan’s monthly request quota — wait for reset_at. rate_limited / per_second_limit_exceeded: more requests in one second than the plan allows — retry after Retry-After (1 s). A request refused here is not counted against the monthly quota.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
Examples
{ "error": "Monthly quota exceeded", "code": "quota_exceeded", "details": { "reason": "monthly_request_limit_reached", "current_usage": 10000, "limit": 10000, "reset_at": "2026-11-01T00:00:00.000Z", "plan_tier": "starter", "suggestion": "Wait until the monthly reset (reset_at). Higher limits: Pro and Business, by card or by invoice (three months or more) — https://app.igregulator.io/billing, or email founder@igregulator.io." }}{ "error": "Per-second rate limit reached", "code": "rate_limited", "details": { "reason": "per_second_limit_exceeded", "current_usage": 6, "limit": 5, "plan_tier": "starter", "suggestion": "Your plan allows 5 req/s. Back off for a second and retry. Higher limits: Pro and Business, by card or by invoice (three months or more) — https://app.igregulator.io/billing, or email founder@igregulator.io." }}Headers
Section titled “ Headers ”On the per-second limit (1). Not on the monthly quota — read details.reset_at.
On the per-second limit only: the requests per second your plan allows.
On the per-second limit only: 0.
On the per-second limit only: Unix epoch seconds of the next second.
On the monthly quota only: the quota of your plan.
On the monthly quota only: requests counted this month.
On the monthly quota only: 0.
On the monthly quota only: when the counter starts again (ISO-8601).
Unexpected server error. Safe to retry.
The body of every non-2xx response.
object
Human-readable summary. The wording can change; do not branch on it.
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.
Always present. reason is always set; field and suggestion when they apply; limit errors add the numbers below.
object
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.
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).
A hint a person or an agent can act on. Present on every error today.
On a limit error (429, and the 403 plan caps): the ceiling that was reached.
On a keyed limit error: how much of limit is used. Not on the keyless per-IP 429 or the watchlist-poll 429.
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).
The key’s plan, on plan-scoped limit errors and on export_requires_pro.
On export_requires_pro: the lowest plan that includes the feature.
On watchlist_events_poll_limit: always hour.
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." }}