# Geo-checks

URL: https://igregulator.io/docs/geo-checks/
Markdown: https://igregulator.io/docs/geo-checks.md

> Does a casino site open in a country where its licence does not reach? Request a check of a domain in up to ten markets; read from home internet connections there, rendered in a browser in the country, every 'reachable' confirmed on its screenshot. Business and Enterprise.

A licence from Curaçao or Anjouan does not let a site take players from the UK,
Sweden or the Netherlands. A geo-check tells you whether a site **opens** in a
country: what home internet connections there were served, on the date shown.

It is an **observation about a site on a date — never a licence status**, and never
a finding that anyone broke a rule. A site that opens on its front page may still
refuse players at registration or at the identity check.

**Plans:**

| Plan | In the dashboard (domain page) | Through the API |
| --- | --- | --- |
| Starter | The block is shown, results are not | — |
| Pro | Results and the screenshot | — |
| Business | Results, screenshot, and requesting checks — 10 domains per UTC month, each in as many of the ten countries as you like | All three endpoints |
| Enterprise | As Business, no monthly cap (bulk checks) | All three endpoints |

Every geo-check endpoint — reading results included — answers Starter and Pro with
`402`: `"error": "This endpoint is available on the Business and Enterprise plans"`,
`code: payment_required`, `details.reason: geo_checks_require_business`,
`details.available_on: ["business", "enterprise"]`.

## Countries

`GB`, `SE`, `NL`, `DE`, `ES`, `IT`, `FR`, `BR`, `CA`, `AU` — markets where an
offshore licence carries the most risk, in that order.

**`CA` is Ontario**, not Canada: probes in Toronto, and a browser only through a
connection that exits in Ontario. Its results carry `register` — whether iGaming
Ontario's list has the domain on an active licence — beside the result, never instead
of it. The United States is not checked: gambling there is licensed state by state,
and a country-wide answer would say nothing.

## How a check works

1. **Probes in the country.** Home-ISP connections in each country (through
   [Globalping](https://globalping.io)'s volunteer network) request the home page,
   following redirects: two for a check you request, one for our own background scan.
2. **The page's own code.** Many sites decide in the browser: their script asks an
   IP-geolocation service where the visitor is and shows a "not available in your
   location" window. When the page prints the countries it serves or blocks, we read
   that list — from the full page, not the probes' first 10 KB.
3. **A browser in the country.** The page is rendered in a real browser through a
   connection that exits in that country — one of our proxies, its exit verified for
   that very session, which then fetches the page and takes the screenshot: one IP.

**`reachable` needs step 3.** A screenshot from anywhere else is not what a visitor
there saw: a site can work in one country and block another in the browser. Without
a browser in the country, a page the probes were served is `served_unverified`
("Reachable, not verified"), with `reason: "… HTML only, no in-country render"`; `in_country_render` says which a check
had. A render from our own server (Germany) still finds a parked domain, and a page
that asks a geolocation service where its visitor is — which makes the other
countries `unknown`, never `reachable`.

**Every `reachable` is held back until a person has looked at the screenshot** from
the country — it is the result most easily wrong (a parked domain, a gateway page, a
block shown as an image). Until then the requester sees `in_review`, and nobody else
sees anything.

**We also check on our own.** Domains on Curaçao, Anjouan, Kahnawake and Tobique
licences are scanned in the background, about monthly — so a result on a domain does
not mean anyone asked for it. Your requests always go first.

## Results

| `result` | Meaning |
| --- | --- |
| `reachable` | Served in the country, and a browser **in that country** showed a working gambling site — confirmed by a person on its screenshot. |
| `served_unverified` | "Reachable, not verified": home connections in the country were served the page, but no browser in the country rendered it, so a block the page shows only in the browser could be missed. Never answers a later request from the cache. |
| `refused` | "Refused (HTTP 403)": every probe in the country got HTTP 403 — and so did a browser in the country, when we had one there (a browser that shows the site makes it `reachable`). What the server answered there — not a statement that the site geo-blocks the country: a bot wall answers 403 too. |
| `geo_blocked` | The site said it does not serve that country: HTTP 451, a block page, or its in-browser check's country list. |
| `waf_blocked` | A firewall refused the request — not a statement about the country. |
| `parked` | No gambling site at the domain (parked, for sale, empty). |
| `gateway` | Set by the reviewer: a page that only sends visitors on to another domain. `reason` names it when we can. |
| `not_casino` | Set by the reviewer: not a gambling site. |
| `unknown` | We could not tell. `reason` says why — e.g. `Cloudflare captcha` (a bot check that stops any automated reader, from any connection), probes that disagree, or no response. |

`reviewed: true` means a person set or confirmed the result. A check older than 30
days is `stale: true` — request a new one.

## Request a check

```http
POST /v1/geo-checks
Authorization: Bearer <your-api-key>
Content-Type: application/json

{ "domain": "example-casino.com", "countries": ["GB", "SE", "NL"] }
```

Leave out `countries` for all ten. One request is **one** against the monthly
allowance however many countries it covers.

**A recent result answers at once, free.** A definite result (anything but
`unknown` or `served_unverified`) of a check someone requested for the same domain
and country in the last 7 days answers that country immediately — our own background
scans never do; `from_cache` lists them with each result's date. A request every country
of which is answered so is `done` at once and **not counted** against the allowance —
even when the allowance is used up. A request with any new country counts as one.

The answer is `202`:

```json
{
  "request_id": "0b9c2a6e-1f3d-4c1a-9f0e-7d1c2b3a4e5f",
  "domain": "example-casino.com",
  "countries": ["GB", "SE", "NL"],
  "status": "queued",
  "requested_at": "2026-11-03T09:12:00.000Z",
  "from_cache": [{ "country": "GB", "checked_at": "2026-11-01T14:02:00.000Z" }],
  "expected_within": "two business days",
  "monthly_usage": { "used": 3, "limit": 10 }
}
```

`expected_within` is `null` and `status` is `done` when every country was answered
from the cache. `monthly_usage` is `null` on a plan with no cap (Enterprise).

Over the allowance: `429 quota_exceeded` with `details.reason: geo_check_quota_exceeded`
(and `current_usage`, `limit`).

## Follow your requests

`GET /v1/geo-check-requests` — your last 50 requests, newest first, each with its
countries and their status: `queued`, `running`, `in_review`, `done` (with `check`)
or `failed`, and `from_cache` when a recent check answered it. Only you see your
requests.

## Read the checks for a domain

`GET /v1/geo-checks?domain=example-casino.com` — the latest check per country that
can be shown (reviewed, or one that needed no review). The latest definite result
wins — `refused` is one, so a site that starts answering 403 in a country no longer
shows its older `reachable`. A later `served_unverified` or `unknown` does not replace
an earlier definite result: that one stays, with its own `checked_at`, until it goes
`stale`. Signed in, the same results appear on the domain's page under
"Availability by country" — for Pro, Business and Enterprise; requesting a check
there, as through this API, is Business and Enterprise:

```json
{
  "domain": "latamwin.com",
  "checks": [
    {
      "country": "GB",
      "result": "geo_blocked",
      "reason": "in-browser check serves only CL, PE",
      "checked_at": "2026-11-03T10:40:00.000Z",
      "reviewed": false,
      "stale": false,
      "in_country_render": false
    },
    {
      "country": "CA",
      "result": "geo_blocked",
      "reason": "in-browser check serves only CL, PE",
      "checked_at": "2026-11-03T10:41:00.000Z",
      "reviewed": false,
      "stale": false,
      "in_country_render": false,
      "register": { "jurisdiction": "ON", "name": "iGaming Ontario's list", "listed": false }
    }
  ],
  "note": "An observation of what the site served in each country on the date shown — not a licence status. …"
}
```

Who requested a check is never shown: a list of what a supplier checks is a list of
its partners.

## In the dashboard

**On a domain's page** (signed in): an "Availability by country" block — a row per
country with the result, the date of the check, how it was read (home-ISP probes, a
browser in the country), for Ontario whether iGaming Ontario lists the domain, and
a **screenshot** link on each country row that has one (the page as a browser
was served it for that country, so two countries can show different pages). Business and
Enterprise also get **Request a new check** there: the domain in all ten countries,
one against the monthly allowance. The page's **History** also lists each time a
country's result moved between Reachable, Blocked and Refused, dated by the check that
saw it (a `served_unverified` or `unknown` read is not a change). Neither is ever on
the public page: signed out, there is no block and no geo line in the History.

**Explore → [Domains](https://app.igregulator.io/domains)** (Pro and up): an
"availability" filter — the domains whose shown result in one market, or in any, is
the one you pick (Reachable, Refused, Blocked…), the same result each domain's page
shows. Starter sees a pointer to Pro instead.

**[Geo-checks](https://app.igregulator.io/geo-checks)** in the app: the request form
(all ten countries ticked by default), this month's usage, and a card per domain
with a row per country.
