Skip to content

Export (CSV / JSON)

Export gives you a whole dataset — or the slice you filtered to — as one file: every licence, every operator, or every domain ↔ operator link we hold. The rows are the same records the rest of the API serves, and each one carries its own provenance, so a spreadsheet you hand to an auditor still says where every status came from and when we read it.

Export is part of Pro (and Business and Enterprise). On Starter the Export button and the endpoint are there, but they answer with what Pro includes, never with data — not even a sample. Pro isn’t open yet: email founder@igregulator.io to get access or to hear when it opens.

Signed in, both Explore views carry an export: csv · json control beside the sort:

  • Operators / Licenses (app.igregulator.io/) exports the licences the current filters match — search, jurisdiction, status, category, licence type, enforcement flags — every page of them, in the table’s sort.
  • Domains (app.igregulator.io/domains) exports the domain links of the domains the current filters match. A domain is exported with every link it has, as its table row shows it, so a brand licensed by two entities never comes out half-way.

The file has the same columns as the API export below.

Terminal window
curl -s -OJ "https://api.igregulator.io/v1/export/licences?format=csv&jurisdiction=UKGC,MGA" \
-H "Authorization: Bearer $IGREGULATOR_KEY"
# → igregulator-licences-2026-09-30.csv

GET /v1/export/{dataset} — a key is required (no anonymous exports).

DatasetOne row perJoins on
licenceslicence (licenses is accepted too)operator_slug
operatorslegal entityoperator_slug
domainsdomain ↔ operator linkoperator_slug, license_id
ParameterValues
formatcsv (default) or json — NDJSON, one object per line (ndjson works too).
jurisdictionComma-separated codes (UKGC,MGA), case-insensitive; repeating the parameter works too. licences: the licence’s jurisdiction. operators: holds a licence there. domains: the jurisdiction of the licence the link’s status comes from. An unknown code is a 400.
statusComma-separated licence statuses (active, suspended, revoked, expired, pending, unknown, surrendered, not_in_register). operators: holds a licence with that status (in the jurisdiction given, when both are). domains: the status of the licence the link answers to.

Any other parameter is a 400 rather than being silently ignored — a filter we dropped would hand you more rows than you asked for. as_of is not supported (400, details.reason: "as_of_not_supported"): an export is the current state. For a status on a past date, use ?as_of= on /v1/licenses/{id}, /v1/operators/{slug} or /v1/check (point-in-time).

HeaderMeaning
Content-Dispositionattachment; filename="igregulator-<dataset>-<YYYY-MM-DD>.<csv|ndjson>"
X-Dataset-Generated-AtWhen the rows were read (ISO-8601 UTC).
X-Export-Row-CountRows in the file (the CSV header / NDJSON _meta line not counted).
X-Export-Daily-Limit / -Used / -Remaining / -ResetYour export allowance today — see limits.

The body is streamed. The row count and the rows come from one database statement, so they describe the same moment; if the lines you received are fewer than X-Export-Row-Count, the download was cut off — fetch it again.

CSV — RFC 4180: UTF-8 without a byte-order mark, CRLF line ends, a header row, a field quoted when it contains a comma, quote or line break. A list (license_types, jurisdictions) is |-joined; an object (upstream_ids) is its JSON text; null is an empty cell. A text value that starts with =, +, - or @ gets a leading ' so a spreadsheet shows it instead of running it as a formula — a UKGC trading name really is @gamingfun. In Excel, open the file with Data → From Text/CSV so accented names come through as UTF-8.

JSON (format=json) — NDJSON. Line 1 is a metadata object; every other line is one row, keys in the column order below, values untouched (lists as arrays, nulls as null, no formula guard):

{"_meta":{"dataset":"licences","generated_at":"2026-09-30T18:00:00.000Z","row_count":6664,"filters":{"jurisdiction":null,"status":null},"source":"api","format":"ndjson","columns":["license_id","license_number","…"],"as_of":null,"note":"…"}}
{"license_id":"4db0141c-…","license_number":"039028-R-319297-013","license_number_kind":"regulator","jurisdiction_code":"UKGC","status":"active","status_source_url":"https://www.gamblingcommission.gov.uk/downloads/business-licence-data.zip","status_observed_at":"2026-09-30T03:10:12.000Z","…":"…"}

No row has a _meta key, so skip the line that has one:

Terminal window
tail -n +2 igregulator-licences-2026-09-30.ndjson | jq -r 'select(.status != "active") | [.license_number, .status, .status_source_url] | @tsv'
import pandas as pd
df = pd.read_json("igregulator-domains-2026-09-30.ndjson", lines=True).iloc[1:]

Every row carries the same truth the API serves — and no more:

  • A status comes with where and when we read it. status_source_url is the page that published THIS status (an enforcement register, a revoked-licences list, an advisory notice), which is often not the register the licence is listed in (source_url); status_observed_at is when we read it. Cite status_source_url.
  • Only active means licensed now. not_in_register means the register stopped listing the licence and published no reason — not_listed_since says since when and last_listed_at when we last saw it. It is not a revocation, and neither are surrendered or unknown. How we verify says which regulator page sets which status.
  • Kahnawake, Tobique and the Isle of Man publish no licence number. For KH, TGC and IOM the license_number is a reference we assigned (KH/IG/…, TGC/B2C/…, IOM/OGRA/…), and license_number_kind says so: igregulator_reference, not regulator. Never quote it as the regulator’s number.
  • A domain row is a link, with the link’s own status. domain_status is the domain’s status under THAT operator (delisted = the regulator removed it from that licensee); the same domain can be active on one row and delisted on another. The licence columns are the operator’s best licence — active first, then the most recently verified — the same one /v1/check reports.
  • upstream_status is the register’s own word, left out where it would contradict the status, exactly as on /v1/check.
ColumnMeaning
license_idOur licence id — what /v1/licenses/{id} takes.
license_numberThe licence number; for KH, TGC and IOM our reference.
license_number_kindregulator or igregulator_reference.
jurisdiction_codeUKGC, MGA, CW, KH, AN, TGC, IOM.
operator_slug, operator_nameThe licensee.
statusThe licence status.
status_qualifierprovisional_under_assessment for a Curaçao licence under the CGA’s final assessment; else empty.
upstream_statusThe register’s own word, where it doesn’t contradict status.
status_source_url, status_observed_atWhere and when this status was read.
last_listed_atWhen we last saw the licence listed.
not_listed_sinceWhen the register stopped listing it; empty while listed.
license_types, license_type_raw, license_categoryTypes as named, the raw string, and our cross-jurisdiction category.
issued_date, expiry_dateWhere the register publishes them.
last_verified_atWhen we last read the register for it.
source_urlThe register it is listed in.
ColumnMeaning
operator_id, operator_slug, operator_nameThe legal entity.
registered_nameThe name the register gives next to it (UKGC: often a trading name).
countryISO country code, where known.
upstream_idsIts id per register; for KH, TGC and IOM a key made from the name.
jurisdictionsWhere we hold a licence record for it, any status.
active_jurisdictionsWhere it holds an active licence.
licences_total, licences_activeLicence records; those active.
licences_last_verified_atWhen we last read a register about it.
domains_total, domains_activeDomain links; those whose own status is active (says nothing about the licence).
created_at, updated_atWhen we first recorded it; when its own record last changed.

There is no operator-level status: an operator licensed in two jurisdictions is never collapsed into one. The licences export has each licence.

ColumnMeaning
domainAs the register lists it.
operator_slug, operator_nameThe operator this link is to.
domain_statusThe link’s own status: active, delisted, …
domain_associationdirect (the licensee runs it) or white_label.
verification_urlThe regulator’s own page for this domain (Curaçao certificate, Tobique seal), where one exists.
standinglicensed (link and licence both active), licence_not_active, delisted, or other — derived from domain_status and license_status on the same row.
link_first_seen, link_last_verifiedWhen we first saw the link; when a source last confirmed it.
license_id, jurisdiction_code, license_number, license_number_kindThe licence the link’s status comes from.
license_status, status_qualifier, upstream_statusThat licence’s status, as in licences.
status_source_url, status_observed_atWhere and when that status was read.
license_last_verified_atWhen we last read that licence’s register.
PlanExports per UTC day
Starter, legacy trialNone — 402
Pro10
Business100
EnterpriseUnlimited
  • The allowance is per account, and the dashboard and the API share it.
  • An API export counts as one request against your monthly quota, whatever its size. A dashboard export doesn’t touch the API quota.
  • A refused request (400, 402, 429) uses neither. An export whose query failed before the first byte is given back.
  • Today’s counter resets at 00:00 UTC.

Below Pro — 402, with X-Upgrade-URL:

{
"error": "CSV/JSON export is a Pro feature",
"code": "payment_required",
"details": {
"reason": "export_requires_pro",
"plan_tier": "starter",
"required_plan": "pro",
"suggestion": "CSV/JSON export is part of Pro. Pro isn't open yet — email founder@igregulator.io to get access or to hear when it opens."
}
}

Over today’s allowance — 429, with Retry-After (seconds to 00:00 UTC):

{
"error": "Daily export limit reached",
"code": "rate_limited",
"details": {
"reason": "export_daily_limit_reached",
"current_usage": 10,
"limit": 10,
"reset_at": "2026-10-01T00:00:00.000Z",
"plan_tier": "pro",
"suggestion": "…"
}
}

400 invalid_query for a bad format, jurisdiction, status or an unknown parameter (details.field names it), 400 as_of_not_supported, and 404 dataset_not_found for a dataset other than the three. The full list is on error handling.

  • as_of — exports are the current state (see above).
  • Licence history — use /v1/licenses/{id}/history, or /v1/operators/{slug}/licenses?include_history=true.
  • Regulatory actions — /v1/operators/{slug}/regulatory-actions.
  • MCP — there is no export tool on the MCP server; a file download isn’t a tool call. Use the API.