# UK Visa Sponsor Register: New Sponsors, Removals & Downgrades (`moment-data/uk-visa-sponsor-register-changes`) Actor

UK Home Office Register of Licensed Sponsors (workers) as clean JSON, plus a daily change feed: newly licensed sponsors, removed sponsors, A-to-B rating downgrades and route changes (Skilled Worker, Global Business Mobility...). Filter by town, county, route, rating, keyword.

- **URL**: https://apify.com/moment-data/uk-visa-sponsor-register-changes.md
- **Developed by:** [Luke Miller](https://apify.com/moment-data) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 sponsor records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK Visa Sponsor Register: New Sponsors, Removals & Rating Downgrades

Turn the UK Home Office **Register of Licensed Sponsors (workers)** into clean JSON, and get a **daily change feed** of what moved since your last run:

- **Newly licensed sponsors**: employers that can now sponsor Skilled Worker, Global Business Mobility, Scale-up, Creative Worker and other visas.
- **Removed sponsors**: licences surrendered, expired or revoked (the organisation disappeared from the register).
- **Rating changes**: **A -> B downgrades** (the Home Office put the sponsor on an action plan) and B -> A upgrades.
- **Route changes**: a sponsor added or lost a route, e.g. a company that just added *Global Business Mobility: Senior or Specialist Worker*.
- **Location changes**: same organisation, new town or county.

Filter by **town/city or county, route, rating, licence type and name keyword**. No API key, no login and no web scraping: the Actor finds the current CSV through the official gov.uk Content API and streams the file (about 143,000 rows, 128,000 organisations) in a few seconds.

Other register tools on Apify give you a lookup or a dump of today's register. **Only this one tells you what changed.**

### Who it's for

- **Recruiters and international hiring agencies:** every newly licensed sponsor can now hire from abroad and is actively building a pipeline. Get them the morning they appear, filtered by city and route.
- **Immigration law firms and compliance consultants:** sponsors downgraded to a **B rating** need an action plan and help now. Workers at removed sponsors may need to find a new sponsor quickly.
- **Relocation, global mobility and HR-tech vendors:** new Global Business Mobility and Scale-up sponsors are companies expanding into the UK.
- **Job boards and candidate platforms:** keep a "visa sponsorship available" flag on employers accurate every day.
- **Journalists and researchers:** track sponsor growth and churn by region and sector.

### Quick start

1. Click **Start** with the default input. You get 40 sponsors from today's register (no saved state).
2. All B-rated sponsors in London: `{"ratings": ["B"], "locations": ["London"], "maxItems": 500}`
3. Care sector sponsors in Scotland: `{"keyword": "care, nursing", "locations": ["Scotland", "Glasgow", "Edinburgh", "Aberdeen", "Dundee"], "maxItems": 1000}`
4. Every Global Business Mobility sponsor: `{"routes": ["Global Business Mobility"], "maxItems": 20000}`
5. **Daily change feed (schedule this):** `{"mode": "changes", "stateKey": "uk-all", "maxItems": 5000}`
6. New sponsors and downgrades in Manchester only: `{"mode": "changes", "stateKey": "manchester", "locations": ["Manchester"], "changeTypes": ["added", "downgrade"]}`

#### How `changes` mode works

1. The **first** run for a `stateKey` saves today's full register as a baseline snapshot (named key-value store `uk-visa-sponsor-register-changes-state`) and returns a sample flagged `changeType: "baseline"`.
2. Every later run downloads the current register, compares it with the snapshot and returns **one record per changed organisation**, then saves the new register as the next baseline.
3. If the Home Office has not published a new file since the snapshot, the run returns nothing (and costs only the start fee).

The register is published every working day, so a **daily schedule** (for example 12:00 UK time) works well. Create an Apify **Schedule** with the input from example 5 and send the results to Slack, email, Google Sheets, HubSpot, Airtable, Make, Zapier, n8n or a webhook with Apify integrations. Use one `stateKey` per schedule. Snapshots always hold the full register, so you can change filters at any time without missing anything. If a run hits your spending limit, the snapshot is **not** advanced, so no change is silently skipped.

**Comparing historical versions:** the gov.uk page only exposes the current file. If you have an older register CSV (e.g. one you downloaded earlier, or a web.archive.org copy), set `compareRegisterUrl` in `changes` mode to diff today's register against it, and optionally `registerUrl` to choose the newer file too.

### Input

| Field | Meaning |
|---|---|
| `mode` | `latest` (default, stateless) or `changes` (diff against your saved snapshot) |
| `locations` | Whole-word match on Town/City or County: `London`, `Birmingham`, `Kent`, `Scotland` |
| `routes` | Partial match on route: `Skilled Worker`, `Global Business Mobility` / `GBM`, `Senior or Specialist Worker`, `Scale-up`, `Creative Worker`, `Charity Worker`, `Religious Worker`, `International Sportsperson`, `Seasonal Worker`, `Government Authorised Exchange` |
| `ratings` | `A`, `B`, `Provisional` (UK Expansion Worker), `Premium`, `SME+` |
| `licenceType` | `any`, `worker`, `temporaryWorker` |
| `keyword` | Words that must all appear in the organisation name; comma-separate alternatives (`care home, nursing`) |
| `changeTypes` | `changes` mode: `added`, `removed`, `downgrade`, `upgrade`, `ratingChanged`, `routeAdded`, `routeRemoved`, `locationChanged`. Empty = all |
| `maxItems` | Cap on delivered records (also caps spend). Default 40 |
| `stateKey` | Name of the snapshot for `changes` mode (one per schedule) |
| `updateSnapshot` | Advance the snapshot after the run (default true) |
| `compareRegisterUrl`, `registerUrl` | Advanced: compare any two register CSV versions |

### Output

One record per organisation. The register has one row per organisation **per route**; this Actor groups them, so a company with three routes is one record with three `licences`.

| Field | Meaning |
|---|---|
| `changeType` | `current` (latest mode), `baseline` (first changes run), `added`, `removed`, `modified` |
| `changeKinds` | For `modified`: `ratingChanged`, `downgrade`, `upgrade`, `routeAdded`, `routeRemoved`, `locationChanged` |
| `changes` | Detail list: `{kind, route, from, to, fromRating, toRating, direction}` |
| `isDowngrade` | `true` if any route went A -> B |
| `name`, `townCity`, `county` | As published (trailing commas tidied) |
| `typesAndRatings` | e.g. `Worker (A rating)`, `Temporary Worker (B rating)` |
| `routes` | e.g. `Skilled Worker`, `Global Business Mobility: Senior or Specialist Worker` |
| `licenceTypes`, `ratings`, `overallRating` | Worker / Temporary Worker; A, B, Provisional, A (Premium), A (SME+); worst rating across routes |
| `hasBRating`, `isProvisional` | Quick flags |
| `licences` | `[{route, licenceType, rating, typeRating}]` |
| `previous` | For `modified`: previous town, county, types/ratings and routes |
| `registerDate`, `registerVersion`, `registerFileUrl`, `registerUpdatedAt` | Which register file this came from |
| `previousRegisterDate`, `previousRegisterFileUrl` | The baseline it was compared with (`changes` mode) |
| `sourcePageUrl`, `fetchedAt` | Provenance |

Example change record (`changes` mode, organisation name anonymised):

```json
{
  "changeType": "modified",
  "changeKinds": ["ratingChanged", "downgrade"],
  "changes": [{ "kind": "ratingChanged", "route": "Skilled Worker", "from": "Worker (A rating)", "to": "Worker (B rating)", "fromRating": "A", "toRating": "B", "direction": "downgrade" }],
  "isDowngrade": true,
  "name": "Example Care Services Ltd",
  "townCity": "Leicester",
  "county": "Leicestershire",
  "typesAndRatings": ["Worker (B rating)"],
  "routes": ["Skilled Worker"],
  "licenceTypes": ["Worker"],
  "ratings": ["B"],
  "overallRating": "B",
  "hasBRating": true,
  "isProvisional": false,
  "licences": [{ "route": "Skilled Worker", "licenceType": "Worker", "rating": "B", "typeRating": "Worker (B rating)" }],
  "registerDate": "2026-10-30",
  "registerVersion": "SP_-_Worker_and_Temporary_Worker_Web_Register_-_2026-10-30.csv",
  "registerFileUrl": "https://assets.publishing.service.gov.uk/media/6ae46c20976f73ef5cfb68ac/SP_-_Worker_and_Temporary_Worker_Web_Register_-_2026-10-30.csv",
  "registerUpdatedAt": "2026-10-30T10:06:24.000Z",
  "sourcePageUrl": "https://www.gov.uk/government/publications/register-of-licensed-sponsors-workers",
  "previous": { "townCity": "Leicester", "county": "Leicestershire", "typesAndRatings": ["Worker (A rating)"], "routes": ["Skilled Worker"] },
  "previousRegisterDate": "2026-10-29"
}
```

See `sample-output.json` for added and removed examples. Each run also writes a summary to the `OUTPUT` record: register file and size, baseline used, number of changes by type, filters and why the run stopped.

### Pricing

Pay per event (Apify takes care of billing; you only pay for what is delivered):

| Event | Price | Charged |
|---|---|---|
| `apify-actor-start` | $0.005 | once per run |
| `sponsor-record` | $0.003 | per sponsor record delivered (register row or change record) |

A default test run (40 records) costs about $0.125. A daily all-UK change feed is typically tens to a few hundred records a day (about $0.10-$0.90/day). `maxItems` and Apify's *max cost per run* cap your spend; the Actor never delivers more records than your run budget allows. No separate platform-usage charges: compute is included in the event prices.

### Data source and notes

- [Register of licensed sponsors: workers](https://www.gov.uk/government/publications/register-of-licensed-sponsors-workers), UK Home Office, found via the [gov.uk Content API](https://content-api.publishing.service.gov.uk/). Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). This Actor is not affiliated with or endorsed by the Home Office or GOV.UK.
- The register lists **organisations only** (name, town, county, licence type and rating, route). It has no company numbers, addresses, contacts or job vacancies.
- Organisations are identified by name + town + county, because the register has no ID. If the Home Office edits a name, that shows up as one removal plus one addition; a town/county edit with an unchanged, unique name is reported as `locationChanged`.
- Removal from the register does not say why (surrender, expiry or revocation). Always check the official register before acting on a sponsor's status.
- The Home Office usually publishes a new file every working day; `registerDate` comes from the file name.

### FAQ

**Can I download the whole register?** Yes: `{"maxItems": 200000}` returns every organisation (about 128,000 records).
**Does it include student sponsors?** No, this is the workers register (Worker and Temporary Worker licences).
**How fast is it?** The default run takes a few seconds; `changes` runs about 5-15 seconds at 512 MB.
**Why did my first `changes` run return "baseline" records?** There was nothing to compare with yet. Changes start from the second run.

# Actor input Schema

## `mode` (type: `string`):

latest (default): sponsors from today's register that match your filters, no saved state. changes: compares today's register with the snapshot saved by your previous changes run (per State key) and returns sponsors added, removed, re-rated (e.g. A -> B) or with routes added/removed. The first changes run saves the baseline and returns a sample flagged changeType = baseline.

## `locations` (type: `array`):

Whole-word match on the register's Town/City or County, e.g. London, Manchester, Scotland, West Midlands. Empty = all of the UK.

## `routes` (type: `array`):

Partial match on the visa route, e.g. Skilled Worker, Global Business Mobility (or GBM), Senior or Specialist Worker, Scale-up, Creative Worker, Charity Worker, Religious Worker, International Sportsperson, Seasonal Worker, Government Authorised Exchange. Empty = all routes.

## `ratings` (type: `array`):

A, B (sponsors with compliance issues on an action plan), Provisional (UK Expansion Worker), Premium, SME+. Empty = all ratings.

## `licenceType` (type: `string`):

Worker (long-term routes like Skilled Worker) or Temporary Worker (Creative, Charity, Religious, GAE, Seasonal...).

## `keyword` (type: `string`):

Words that must all appear in the organisation name, e.g. "care", "nhs trust", "university". Comma-separate alternatives: "care home, nursing".

## `changeTypes` (type: `array`):

Only these changes: added, removed, downgrade (A -> B), upgrade (B -> A), ratingChanged, routeAdded, routeRemoved, locationChanged. Empty = all.

## `maxItems` (type: `integer`):

Hard cap on delivered records (also caps your spend). The full register has about 128,000 organisations.

## `stateKey` (type: `string`):

Name of the saved snapshot in the named key-value store uk-visa-sponsor-register-changes-state. Use one key per schedule. Snapshots always hold the full register, so filters can change between runs.

## `updateSnapshot` (type: `boolean`):

Save today's register as the new baseline after delivering the changes. Turn off to re-run the same comparison. The snapshot is never advanced if the run hits your spending limit, so no change is skipped.

## `compareRegisterUrl` (type: `string`):

changes mode: diff against this older register CSV (e.g. a previous assets.publishing.service.gov.uk file you saved, or a web.archive.org copy) instead of the saved snapshot. The snapshot is not touched.

## `registerUrl` (type: `string`):

Override the current register with a specific CSV URL. Together with compareRegisterUrl this compares any two register versions. Empty = today's register from the gov.uk Content API.

## Actor input object example

```json
{
  "mode": "latest",
  "licenceType": "any",
  "maxItems": 40,
  "stateKey": "default",
  "updateSnapshot": true
}
```

# Actor output Schema

## `sponsors` (type: `string`):

One record per licensed sponsor organisation (latest) or per changed sponsor (changes).

## `summary` (type: `string`):

Register version, baseline used, change counts, filters, why the run stopped.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("moment-data/uk-visa-sponsor-register-changes").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("moment-data/uk-visa-sponsor-register-changes").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call moment-data/uk-visa-sponsor-register-changes --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,moment-data/uk-visa-sponsor-register-changes"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XzqamBLqaOCfBmCTX/builds/yvebYfQETBtQHJEEh/openapi.json
