{
  "openapi": "3.0.1",
  "info": {
    "title": "DVSA Operator Licence Scraper - UK HGV & PSV Fleet Data",
    "description": "Scrape the UK Traffic Commissioners' operator-licence register: all 69,127 GB goods and PSV O-licences with authorised vehicles and trailers, operating centres, transport managers, directors and Companies House numbers. Every row carries the age of the export it came from.",
    "version": "0.1",
    "x-build-id": "R05uugTqxXcietDul"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~dvsa-operator-licence-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-dvsa-operator-licence-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/scrapersdelight~dvsa-operator-licence-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-dvsa-operator-licence-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/scrapersdelight~dvsa-operator-licence-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-dvsa-operator-licence-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "regions": {
            "title": "Traffic areas",
            "type": "array",
            "description": "Which of the eight Traffic Commissioner areas to read. Leave empty for all of Great Britain (69,127 licences, 81 MB, measured 2026-09-08). Licence counts in that export: East of England 13,144 · North East 10,326 · West of England 9,855 · North West 9,398 · West Midlands 8,088 · London & the South East 7,929 · Scotland 5,643 · Wales 4,744. When you leave this empty the areas are read smallest file first, so a run with a row cap downloads the least it can; when you list areas yourself, YOUR order is used. Northern Ireland is NOT in this export — it is a separate register (opendatani.gov.uk), so this Actor is Great Britain, not the whole UK.",
            "items": {
              "type": "string",
              "enum": [
                "East of England",
                "North East of England",
                "North West of England",
                "Scotland",
                "London and the South East of England",
                "Wales",
                "West Midlands",
                "West of England"
              ]
            },
            "default": []
          },
          "outputLevel": {
            "title": "One row per…",
            "enum": [
              "licence",
              "operating-centre"
            ],
            "type": "string",
            "description": "\"Licence\" gives one row per operator licence with the operating centres nested in an array (69,127 rows GB-wide). \"Operating centre\" flattens that: one row per authorised operating centre, carrying all the licence fields plus that centre's address and postcode (97,348 rows GB-wide — 13,759 licences have more than one centre and the largest has 23). Operating-centre mode genuinely delivers, and therefore bills, more rows than licence mode: 97,348 against 69,127 for the whole of GB.",
            "default": "licence"
          },
          "mode": {
            "title": "Mode",
            "enum": [
              "full",
              "monitor"
            ],
            "type": "string",
            "description": "“Full export” delivers the register. “Monitor” downloads the same files, diffs them against what the last run saw and delivers ONLY the licences that are new, changed or gone — with `changedFields` naming exactly which fields moved. Monitor state lives in a named key-value store so it survives between scheduled runs. The first monitor run is a baseline: everything reads as new, and the second run onwards is the real signal. MONITOR MODE REQUIRES “Max licences” = 0, and refuses to run otherwise — before downloading or charging anything. A capped monitor run can never advance its baseline (it cannot tell “unchanged” from “not reached”), so it would report the same licences as new and bill for them again on every run. “Max licences” DEFAULTS to 200, so it has to be set to 0 explicitly. Use the run's maxTotalChargeUsd to bound what a monitor run can spend. Measured on the 07 Aug -> 07 Sep 2026 exports: 418 new, 8,989 changed, 464 removed out of 69,127 licences.",
            "default": "full"
          },
          "maxLicences": {
            "title": "Max licences",
            "minimum": 0,
            "maximum": 5000000,
            "type": "integer",
            "description": "Stop after this many LICENCES (not rows — in operating-centre mode one licence can produce up to 23 rows; the GB average is 1.41). 0 means no cap: the entire selected area. With no filter and no ranking the run stops downloading the moment the cap is met, and only fetches the byte range of each CSV it actually needs, so a 50-row demo pulls about 150 KB instead of 4.5 MB. With a ranking set, the cap picks the TOP N across every licence read — see “Rank by”. In Monitor mode this must be 0: a capped monitor run cannot advance its baseline, so the run is refused up front rather than billing you for the same licences on every run.",
            "default": 200
          },
          "licenceCategories": {
            "title": "Goods or PSV",
            "type": "array",
            "description": "Goods = HGV/freight O-licences (64,039 GB-wide, 603,051 authorised vehicles). PSV = bus and coach operator licences (5,088 GB-wide, 87,432 vehicles). Empty means both.",
            "items": {
              "type": "string",
              "enum": [
                "goods",
                "psv"
              ],
              "enumTitles": [
                "Goods (HGV) — 64,039",
                "PSV (bus & coach) — 5,088"
              ]
            },
            "default": []
          },
          "licenceTypes": {
            "title": "Licence type",
            "type": "array",
            "description": "Restricted 31,967 · Standard National 25,741 · Standard International 11,419 (measured GB-wide 2026-09-08). Standard International is the export/haulage segment; Restricted operators carry only their own goods.",
            "items": {
              "type": "string",
              "enum": [
                "Restricted",
                "Standard National",
                "Standard International"
              ]
            },
            "default": []
          },
          "licenceStatuses": {
            "title": "Licence status",
            "type": "array",
            "description": "The export contains granted licences only. Statuses seen GB-wide 2026-09-08: valid 68,588, curtailed 522, suspended 17. Curtailed and suspended operators are the compliance-signal segment — a fleet under a Traffic Commissioner sanction.",
            "items": {
              "type": "string",
              "enum": [
                "valid",
                "curtailed",
                "suspended",
                "revoked",
                "surrendered",
                "terminated",
                "withdrawn"
              ],
              "enumTitles": [
                "Valid (68,588)",
                "Curtailed (522)",
                "Suspended (17)",
                "Revoked",
                "Surrendered",
                "Terminated",
                "Withdrawn"
              ]
            },
            "default": []
          },
          "operatorTypes": {
            "title": "Operator legal form",
            "type": "array",
            "description": "Limited Company 53,790 · Sole Trader 10,537 · Partnership 4,183 · Other (public authority, charity, trust, university) 478 · LLP 139. Sole traders and partnerships are the segment where the correspondence address is most often a home address.",
            "items": {
              "type": "string",
              "enum": [
                "Limited Company",
                "Sole Trader",
                "Partnership",
                "Limited Liability Partnership",
                "Other (e.g. public authority, charity, trust, university)"
              ]
            },
            "default": []
          },
          "minVehiclesAuthorised": {
            "title": "Min authorised vehicles",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "The fleet-size field the whole dataset is priced on. GB distribution measured 2026-09-08: 1 vehicle 14,440 licences · 2-9 40,694 · 10-49 11,816 · 50-99 1,356 · 100+ 820. Median 3, largest 2,853 (Wincanton Holdings Limited)."
          },
          "maxVehiclesAuthorised": {
            "title": "Max authorised vehicles",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Upper bound on authorised vehicles. Use with the minimum to carve a band — e.g. 10 to 49 is 11,816 licences GB-wide, the classic mid-market fleet segment."
          },
          "minTrailersAuthorised": {
            "title": "Min authorised trailers",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Authorised trailers, 423,603 GB-wide. Filled on 96.44% of licences (the source leaves it blank on the rest, and blank does not pass a minimum)."
          },
          "maxTrailersAuthorised": {
            "title": "Max authorised trailers",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Upper bound on authorised trailers. Pair with the minimum to isolate, for example, tipper and curtainsider fleets that pull a handful of trailers rather than a full artic fleet."
          },
          "minVehiclesSpecified": {
            "title": "Min vehicles specified",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Vehicles actually specified on the licence (446,930 GB-wide) as against authorised (690,483). The gap is unused authority."
          },
          "maxVehiclesSpecified": {
            "title": "Max vehicles specified",
            "minimum": 0,
            "maximum": 10000,
            "type": "integer",
            "description": "Upper bound on vehicles actually specified on the licence."
          },
          "minVehicleUtilisation": {
            "title": "Min fleet utilisation (0-1)",
            "type": "string",
            "description": "vehiclesSpecified / vehiclesAuthorised. 1.0 means the operator has filled every slot it is licensed for — a buying signal for anyone selling vehicles, leasing or extra authority. Leave empty to ignore."
          },
          "maxVehicleUtilisation": {
            "title": "Max fleet utilisation (0-1)",
            "type": "string",
            "description": "Set this low (e.g. 0.5) to find operators sitting on authority they are not using."
          },
          "minOperatingCentres": {
            "title": "Min operating centres",
            "minimum": 0,
            "maximum": 50,
            "type": "integer",
            "description": "13,759 GB licences (19.9%) have more than one authorised operating centre; the largest has 23. Set 2 to get only multi-site operators."
          },
          "maxOperatingCentres": {
            "title": "Max operating centres",
            "minimum": 0,
            "maximum": 50,
            "type": "integer",
            "description": "Upper bound on authorised operating centres. Set 1 to keep only single-site operators (55,368 licences GB-wide)."
          },
          "minTransportManagers": {
            "title": "Min transport managers",
            "minimum": 0,
            "maximum": 50,
            "type": "integer",
            "description": "36,553 licences (52.9%) name at least one transport manager, 45,955 named posts in total. Restricted licences legally need none, which is why the other half is blank rather than missing."
          },
          "operatorNameContains": {
            "title": "Operator name contains",
            "type": "string",
            "description": "Case- and accent-insensitive match on the operator's registered name. Use the match mode below to switch between contains / starts with / exact."
          },
          "operatorNameMatchMode": {
            "title": "Operator name match mode",
            "enum": [
              "contains",
              "startsWith",
              "exact"
            ],
            "type": "string",
            "description": "How \"Operator name contains\" is matched. Exact is useful for reconciling a list of company names you already hold; starts with is useful for brand families (\"STOBART\", \"EDDIE STOBART\").",
            "default": "contains"
          },
          "transportManagerNameContains": {
            "title": "Transport manager name contains",
            "type": "string",
            "description": "Matches any of the licence's named transport managers. Ignored when \"Include people's names\" is off, because there would be nothing to match against."
          },
          "directorNameContains": {
            "title": "Director / partner name contains",
            "type": "string",
            "description": "Matches any named director or partner on the licence. 129,674 distinct director/partner entries GB-wide (119,010 Directors, 10,664 Partners)."
          },
          "postcodePrefixes": {
            "title": "Postcode prefixes",
            "type": "array",
            "description": "Outward-code prefixes, e.g. [\"CF\", \"SA1\", \"M17\"]. Matched against the correspondence postcode and/or the operating-centre postcodes depending on the setting below. 99.98% of correspondence addresses and 99.68% of operating centres yield a parseable postcode.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "postcodeMatchField": {
            "title": "Match postcodes against",
            "enum": [
              "any",
              "correspondence",
              "operatingCentre"
            ],
            "type": "string",
            "description": "\"Operating centre\" is the one to use for territory targeting — it is where the vehicles actually are, which is often not where the paperwork goes.",
            "default": "any"
          },
          "licenceNumbers": {
            "title": "Specific licence numbers",
            "type": "array",
            "description": "Look up exact licences, e.g. [\"OG0093659\", \"OG0094759\"]. Format is O (goods) or P (PSV), then the traffic-area letter, then 7 digits. Note the whole regional file still has to be read to find them — the export has no per-licence endpoint.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "companyRegNumbers": {
            "title": "Specific Companies House numbers",
            "type": "array",
            "description": "Exact Companies House numbers, e.g. [\"01234567\", \"SC221866\"]. 78.57% of licences carry one (50,171 eight-digit, 3,583 SC, 179 NI, 130 OC, 10 SO).",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "hasTransportManager": {
            "title": "Has a named transport manager",
            "enum": [
              "any",
              "yes",
              "no"
            ],
            "type": "string",
            "description": "A named transport manager is legally required on Standard licences and not on Restricted ones, so this splits the register almost exactly in half: 36,553 licences name at least one, 32,574 name none.",
            "default": "any"
          },
          "hasCompanyRegNumber": {
            "title": "Has a Companies House number",
            "enum": [
              "any",
              "yes",
              "no"
            ],
            "type": "string",
            "description": "\"Yes\" is the segment you can join to Companies House for accounts, SIC codes and officers; every such row also carries a ready-made companiesHouseUrl.",
            "default": "any"
          },
          "continuationDueInNextDays": {
            "title": "Continuation due within N days",
            "minimum": 0,
            "maximum": 4000,
            "type": "integer",
            "description": "An O-licence is continued every five years and the operator pays a fee to do it — this is the renewal trigger. Measured on the 07 Sep 2026 export: 3,715 licences fall due during 2026, 13,024 in 2027, 13,366 in 2028. Set 365 for \"due in the next year\"."
          },
          "continuationDueAfter": {
            "title": "Continuation due on or after",
            "type": "string",
            "description": "ISO date, e.g. 2027-01-01."
          },
          "continuationDueBefore": {
            "title": "Continuation due on or before",
            "type": "string",
            "description": "ISO date, e.g. 2027-12-31. Combine with the one above for an exact renewal window."
          },
          "sortBy": {
            "title": "Rank by",
            "enum": [
              "none",
              "vehiclesAuthorised",
              "trailersAuthorised",
              "vehiclesSpecified",
              "vehicleUtilisation",
              "operatingCentreCount",
              "transportManagerCount",
              "continuationDate",
              "operatorName",
              "licenceNumber"
            ],
            "type": "string",
            "description": "Ranking is applied across EVERY licence read before the cap is taken, so \"Rank by authorised vehicles, max 1,000\" really is the 1,000 biggest fleets in Great Britain — not the biggest of the first 1,000 rows. That combination is the fastest way to reproduce (and beat) a paid top-fleets list, and it uses flat memory. Leave on \"Source order\" for a plain export; source order also lets a capped run stop downloading early, which ranking cannot. NOTE: ranking is the one operation that has to buffer — a sort cannot write its first row until it has seen every matching licence — so ranking the WHOLE register (maxLicences 0) needs about 1 GB of run memory. Measured: 432 MB peak for all 69,127 licences. If the run does not have enough, it says so before downloading anything rather than failing part-way through a paid run.",
            "default": "none"
          },
          "sortOrder": {
            "title": "Order",
            "enum": [
              "desc",
              "asc"
            ],
            "type": "string",
            "description": "Direction for the ranking above. Descending on authorised vehicles gives the biggest fleets first; ascending on continuation date gives the soonest renewals first.",
            "default": "desc"
          },
          "changeTypes": {
            "title": "Report which changes",
            "type": "array",
            "description": "Monitor mode only. “Removed” means the licence is no longer in the export at all — surrendered, revoked or expired. A removed licence no longer carries the fields your filters test, so it is judged against the row THE LAST RUN SAW: it is delivered only when the licence it replaces would itself have matched your filters. Filter to PSV only and removed goods licences are dropped, never delivered and never billed. Those rows carry `fieldsAvailable` and `removalFilterCheck`, which says exactly how the row was judged. Removals are suppressed (with a warning) for any traffic area that was not read in full, because a partial read cannot tell “gone” from “not reached”. ONE THING TO KNOW BEFORE YOU TICK THE LAST BOX: “Unchanged” delivers every licence that did NOT move, and each one is billed at the licence-change-detected price of $0.006 — five times the $0.0012 the same row costs in mode “Full export”. Ticking it for the whole of Great Britain is 69,127 rows for about $414.76 on a run where nothing changed. Tick it only when you deliberately want the full register carrying change flags.",
            "items": {
              "type": "string",
              "enum": [
                "new",
                "changed",
                "removed",
                "unchanged"
              ],
              "enumTitles": [
                "New licences",
                "Changed licences",
                "Removed licences",
                "Unchanged (everything else)"
              ]
            },
            "default": [
              "new",
              "changed",
              "removed"
            ]
          },
          "watchedFields": {
            "title": "Fields that count as a change",
            "type": "array",
            "description": "A licence is reported as \"changed\" only when one of these moved. Measured over the 07 Aug -> 07 Sep 2026 exports (8,989 changed licences GB-wide): vehiclesSpecified 5,198 · operatingCentres 2,384 · transportManagers 901 · directorsOrPartners 817 · continuationDate 746 · vehiclesAuthorised 426 · correspondenceAddress 401 · trailersAuthorised 256 · operatorName 74 · licenceStatus 47 · licenceType 38. Narrow this list to cut the noise — watching only vehiclesAuthorised and licenceStatus, for example, turns 8,989 rows into a few hundred. Changing this list between runs never corrupts the comparison: the stored fingerprints always cover every field.",
            "items": {
              "type": "string",
              "enum": [
                "operatorName",
                "operatorType",
                "companyRegNumber",
                "licenceType",
                "licenceStatus",
                "correspondenceAddress",
                "vehiclesAuthorised",
                "trailersAuthorised",
                "vehiclesSpecified",
                "operatingCentres",
                "transportManagers",
                "directorsOrPartners",
                "continuationDate"
              ]
            },
            "default": [
              "operatorName",
              "operatorType",
              "companyRegNumber",
              "licenceType",
              "licenceStatus",
              "correspondenceAddress",
              "vehiclesAuthorised",
              "trailersAuthorised",
              "vehiclesSpecified",
              "operatingCentres",
              "transportManagers",
              "directorsOrPartners",
              "continuationDate"
            ]
          },
          "monitorStateKey": {
            "title": "Monitor state key",
            "type": "string",
            "description": "Names the saved baseline, so two schedules watching different scopes never overwrite each other's state. Use one key per watch — e.g. \"gb-all\" and \"big-fleets-only\".",
            "default": "gb-all"
          },
          "removalsWithUnmatchableFilters": {
            "title": "Removals your filters cannot be checked against",
            "enum": [
              "skip",
              "deliver"
            ],
            "type": "string",
            "description": "Monitor mode only, and it almost never fires. A removed licence is checked against your filters using the row the last run saved, which holds every field the filters read. It can only be uncheckable when the saved baseline was written by an older build of this Actor that did not keep that field — in practice, the FIRST run after this Actor was updated, and only if a filter reads a field that baseline never stored. “Skip” (the default) does not deliver and does not charge for a removal it cannot vouch for; the licence numbers are still handed to you for free in RUN_SUMMARY under `monitor.removalsSkippedAsUnmatchable`, and the run log names the missing fields. “Deliver” ships them anyway at $0.006 each, flagged with `removalFilterCheck: \"not-checkable...\"`. From the second run onwards the baseline carries everything, so every removal is checked properly either way.",
            "default": "skip"
          },
          "maxSourceAgeDays": {
            "title": "Refuse an export older than (days)",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "THE MOST IMPORTANT SETTING IN THIS ACTOR. The eight CSVs sit behind CloudFront, and a stale edge answers HTTP 200 with a MONTH-OLD file and no error of any kind. Measured 2026-09-08: from one machine every route landed on an edge serving the 07 Aug export while the origin already had the 07 Sep one, and query-string cache-busting, Cache-Control and Pragma all changed nothing. Rotating the egress IP does work — 7 of 12 Apify proxy sessions hit an edge holding the fresh object. So this Actor HEAD-probes each file, rotates until the Last-Modified is inside this window, and only then downloads. 0 disables the check and takes whatever the first edge serves.",
            "default": 14
          },
          "freshnessRetries": {
            "title": "Freshness rotations per file",
            "minimum": 0,
            "maximum": 30,
            "type": "integer",
            "description": "How many extra egress IPs to try per file before giving up on getting a fresh copy. At the measured 58% hit rate, 8 rotations leaves roughly a 1-in-2,000 chance of not finding one.",
            "default": 8
          },
          "freshnessBudgetSecs": {
            "title": "Freshness probe budget per file (seconds)",
            "minimum": 5,
            "maximum": 1800,
            "type": "integer",
            "description": "Hard wall on the rotation so a slow proxy can never stretch a run. When it runs out the freshest object found so far is used and the run says so.",
            "default": 120
          },
          "onStaleSource": {
            "title": "If only a stale export can be reached",
            "enum": [
              "warn",
              "skip",
              "fail"
            ],
            "type": "string",
            "description": "\"Deliver and flag\" is the safe default: you get the data, every row carries sourceIsStale=true plus the real Last-Modified, and the run log names the edge. \"Fail\" is for pipelines that must never ingest an old snapshot. \"Skip\" drops just that traffic area.",
            "default": "warn"
          },
          "verifySourceListing": {
            "title": "Check the data.gov.uk listing",
            "type": "boolean",
            "description": "One extra request to the CKAN record at the start of the run, to notice if the publisher adds, renames or drops a regional export. The S3 prefix itself refuses a directory listing (403), so this is the only way a ninth traffic area would ever be spotted. Never fatal — it warns.",
            "default": true
          },
          "includePersonNames": {
            "title": "Include people's names",
            "type": "boolean",
            "description": "The register names individuals: 129,674 directors and partners and 45,955 transport-manager posts. Turning this OFF drops the `transportManagers` and `directorsOrPartners` arrays while keeping the counts, which is the setting to use if your own data policy does not want named natural persons in the dataset. The source is published under the Open Government Licence and permits commercial reuse with attribution; UK GDPR still applies to the natural-person rows independently of that, and that judgement is yours.",
            "default": true
          },
          "includeOperatingCentres": {
            "title": "Include the operating-centre array",
            "type": "boolean",
            "description": "The nested `operatingCentres` array (address + postcode per centre) and `operatingCentrePostcodes`. Turn off for a slimmer row; the counts stay either way.",
            "default": true
          },
          "includeSourceProvenance": {
            "title": "Include full source provenance",
            "type": "boolean",
            "description": "Adds sourceFile, sourceUrl, sourceEtag, sourceVersionId, sourceCloudfrontPop, sourceEdgeCacheAgeSecs, sourceTransport and sourceRangeLimited to every row. sourceLastModified, sourceAgeHours and sourceIsStale ship whatever you set here — a completeness flag is never optional.",
            "default": true
          },
          "includeRawRow": {
            "title": "Include the raw source row",
            "type": "boolean",
            "description": "Attaches the 16 untouched CSV cells under `raw` so you can audit any derived field against the publisher's own bytes.",
            "default": false
          },
          "normaliseWhitespace": {
            "title": "Normalise whitespace",
            "type": "boolean",
            "description": "The source pads addresses with runs of spaces (\"CRUGYBAR ABERNAINT FAWR   LLANWRDA\"). On, those collapse to single spaces. Turn off if you need the bytes exactly as published.",
            "default": true
          },
          "dedupeAcrossRuns": {
            "title": "Skip licences delivered by earlier runs",
            "type": "boolean",
            "description": "Remembers every licence number delivered, in a named key-value store, and skips them next time — for building a corpus incrementally. Skipped rows are dropped BEFORE billing, so you are never charged for them. This is not the same as Monitor mode: it never tells you a licence CHANGED, only that you have not seen it before.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Used to ROTATE the egress IP when an edge is serving a stale export — that is what defeats CloudFront caching, and it is why the default is on. Apify datacenter proxy is enough; all eight files returned HTTP 200 through it, and through residential exits in GB, US, DE and IE. The proxy is also 35x slower than the direct route on this source (81 MB took ~8s direct and ~612s through the proxy), which is exactly why \"Freshness only\" below is the default.",
            "default": {
              "useApifyProxy": true
            }
          },
          "proxyMode": {
            "title": "When to use the proxy",
            "enum": [
              "freshnessOnly",
              "always",
              "never"
            ],
            "type": "string",
            "description": "\"Freshness only\" tries the direct route first and rotates through the proxy only when the file it gets is too old — fast when the platform's own edge is already current, correct when it is not. \"Always\" routes every request through a rotated session. \"Never\" disables the proxy entirely: fastest, but a stale edge then cannot be routed around.",
            "default": "freshnessOnly"
          },
          "onRegionFailure": {
            "title": "If one traffic area cannot be downloaded",
            "enum": [
              "warn",
              "fail"
            ],
            "type": "string",
            "description": "A file that stalls is retried on a different egress IP first (proxy throughput on this source was measured anywhere between 12s and 270s for the same 11 MB file), and the last attempt gets a six-times-longer timeout. If it still will not come down, \"Skip it\" delivers the other traffic areas, names the failed one in the run log and in RUN_SUMMARY's regionsFailed, and sets corpusComplete to false — nothing is passed off as a full sweep. \"Fail the run\" is for pipelines that must have all eight or none. A run where the ONLY selected traffic area fails always fails, whichever you pick.",
            "default": "warn"
          },
          "rangeFetch": {
            "title": "Fetch only the bytes needed",
            "type": "boolean",
            "description": "When a row cap makes the whole file unnecessary and nothing is filtered, ranked or monitored, only the first slice of each CSV is downloaded (the export supports HTTP byte ranges). Measured: 50 licences from Wales needs ~150 KB instead of 4.5 MB. Automatically off whenever a filter, a ranking or Monitor mode is active, because a prefix cannot tell \"does not match\" from \"not read yet\".",
            "default": true
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 15,
            "maximum": 600,
            "type": "integer",
            "description": "Per HTTP request. Proxy throughput on this source varies wildly (measured 12s to 270s for a single regional file), so a timeout here triggers a retry on a different session rather than failing the run.",
            "default": 90
          },
          "maxRetries": {
            "title": "Transport retries",
            "minimum": 0,
            "maximum": 8,
            "type": "integer",
            "description": "Retries per request before the run gives up on that file. Recon saw zero non-200 responses and zero retries across ~70 requests, so this is headroom, not a workaround.",
            "default": 3
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}