# UK Food Business Monitor - FSA hygiene register (`minimal_ricegrass/uk-food-monitor`) Actor

Watches the Food Standards Agency register for newly registered food businesses and hygiene-rating changes in the local authorities you choose. Each scheduled run emits only what changed since the last run, with address, rating and coordinates, plus optional webhook POST.

- **URL**: https://apify.com/minimal\_ricegrass/uk-food-monitor.md
- **Developed by:** [FJ Banks](https://apify.com/minimal_ricegrass) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.02 / actor start

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UK Food Business Monitor — FSA hygiene register

Watches the **Food Standards Agency** register for **newly registered food
businesses** — and, optionally, **hygiene-rating changes** — in the local
authorities you choose. Each scheduled run emits only what changed since the last
run, with address, rating and coordinates. Optional webhook POST to Slack,
Zapier, Make or n8n.

### Who it's for

| Buyer | Use |
|---|---|
| EPOS / booking / kitchen-display vendors | A new restaurant or takeaway = a system to sell, week one |
| Food & drink wholesalers, packaging suppliers | New caterers in your delivery area |
| Commercial kitchen equipment / refrigeration | New premises fitting out |
| Business insurance brokers | New food businesses need cover |
| Accountants & bookkeepers | New hospitality clients in your town |
| Food-safety consultants / PR | A **rating drop** to 0–2 = an urgent-help lead |
| Local press / researchers | New-opening and rating-change feeds by area |

### Quick start

1. No API key — the FSA register is free open data.
2. Set **`localAuthorities`** to the councils you cover (names like `"Manchester"`
   or `"Leeds"`; fragments match, so `"Kensington"` finds "Kensington and
   Chelsea"). Optionally narrow by **`businessTypeIds`**.
3. Run once — the first run **only seeds** the state (emits nothing), because it
   would otherwise dump every establishment. Set `emitOnFirstRun: true` if you
   want them all once.
4. **Schedule** it daily. Later runs report new registrations and rating changes.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `localAuthorities` | string\[] | — | **Required.** Local-authority names or numeric IDs. Name fragments match. |
| `businessTypeIds` | string\[] | `[]` (all) | FSA type IDs. Common: `1` Restaurant/Cafe/Canteen · `7844` Takeaway/sandwich shop · `7843` Pub/bar/nightclub · `7846` Mobile caterer · `7840` Supermarket · `7842` Hotel/B\&B · `4613` Retailers-other · `5` Hospitals/Childcare/Caring. |
| `trackRatingChanges` | boolean | `true` | Also emit establishments whose rating changed since last run. |
| `emitOnFirstRun` | boolean | `false` | First run seeds silently by default; set true to emit every establishment once. |
| `webhookUrl` | string | — | POST `{source, authorities, count, establishments[]}` each run. |
| `maxResults` | integer 1–10000 | `1000` | Cap per run. |

### Output

One dataset item per changed establishment:

| Field | Example |
|---|---|
| `fhrs_id` | `593681` |
| `change_type` | `"new"` or `"rating_changed"` |
| `previous_rating` | `"5"` *(only for rating\_changed)* |
| `business_name` | `"210 BISTRO"` |
| `business_type` / `business_type_id` | `"Restaurant/Cafe/Canteen"` / `1` |
| `rating_value` / `rating_date` | `"Pass"` / `"2026-03-26T00:00:00"` |
| `local_authority` | `"Aberdeen City"` |
| `address` / `post_code` | `"210 Market Street, Aberdeen, AB11 5PQ"` / `"AB11 5PQ"` |
| `latitude` / `longitude` | `57.142278` / `-2.092258` |
| `link` | `https://ratings.food.gov.uk/business/en-GB/593681` |
| `scraped_at` | `"2026-09-03T09:00:00.000Z"` |

Ratings are `0`–`5` in England/Wales/NI and `Pass` / `Improvement Required` in
Scotland.

### How it works

Cross-run state lives in a **named** key-value store,
`uk-food-monitor-state`, namespaced by a hash of the authority + business-type
filter. It holds the set of seen FHRS IDs and, when `trackRatingChanges` is on, a
map of `FHRSID → last rating`. Each run pulls the current establishment list for
every configured authority and:

- an FHRS ID not seen before → `change_type: "new"`
- a seen FHRS ID whose rating differs from the stored one → `change_type: "rating_changed"`

The seen-set is capped at 200,000 IDs.

### Notes on the FSA API

- No key. Requires `x-api-version: 2` and rejects unfiltered establishment
  queries — this actor always scopes by local authority.
- Transient 5xx / 429 are retried (3 attempts, backoff); a hard failure mid-run
  pushes what was collected and resumes next run (`partial: true`).
- The register is refreshed by each local authority on its own cadence (most
  daily). "New" means new to the register, which closely tracks new registrations.

### Local development

```bash
npm install
npm test
echo '{"localAuthorities":["Aberdeen City"],"businessTypeIds":[7846]}' > storage/key_value_stores/default/INPUT.json
npm start
```

### Deploy

```bash
npm i -g apify-cli && apify login && apify push
```

### Data source & licensing

Data from the **Food Standards Agency** food hygiene rating scheme
(`ratings.food.gov.uk`), published under the
[Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).

### Related actors

Part of a small suite of UK public-register monitors, same publisher, same
pattern (incremental, no duplicates, webhook-ready):

- **[UK New Company Monitor](https://apify.com/minimal_ricegrass/uk-newco-monitor)** — new incorporations by SIC / location
- **[UK Insolvency Notice Monitor](https://apify.com/minimal_ricegrass/uk-insolvency-monitor)** — winding-up, administration, bankruptcy (The Gazette)
- **[UK Public Tender Monitor](https://apify.com/minimal_ricegrass/uk-tender-monitor)** — public-sector tenders by CPV / value (Contracts Finder)
- **[UK Company Change Monitor](https://apify.com/minimal_ricegrass/uk-company-change-monitor)** — watchlist diffing: status, charges, officers, filings

***

### Changelog

- **0.1** — Initial release: multi-authority, business-type filter, new-business
  - rating-change detection, first-run seeding, webhook, transient-error retry.

# Actor input Schema

## `localAuthorities` (type: `array`):

One or more local-authority names (e.g. "Manchester", "Leeds") or numeric IDs. Name fragments match, so "Kensington" matches "Kensington and Chelsea".

## `businessTypeIds` (type: `array`):

FSA business-type IDs to include, e.g. 1 = Restaurant/Cafe/Canteen, 7844 = Takeaway/sandwich shop, 7843 = Pub/bar/nightclub, 7846 = Mobile caterer, 7840 = Supermarket. Empty = all types.

## `trackRatingChanges` (type: `boolean`):

Also emit establishments whose hygiene rating has changed since the last run (a rating drop is a PR / consultancy lead).

## `emitOnFirstRun` (type: `boolean`):

By default the first run only seeds state (emits nothing), since it would otherwise dump every establishment. Turn on to get them all once.

## `webhookUrl` (type: `string`):

POST the changed establishments from each run to this URL (Slack, Zapier, Make, n8n, custom).

## `maxResults` (type: `integer`):

Safety cap on records emitted in a single run. Excess is picked up next run.

## Actor input object example

```json
{
  "localAuthorities": [
    "Manchester"
  ],
  "businessTypeIds": [],
  "trackRatingChanges": true,
  "emitOnFirstRun": false,
  "maxResults": 1000
}
```

# Actor output Schema

## `establishments` (type: `string`):

New food businesses and rating changes since the last run.

## `table` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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 = {
    "localAuthorities": [
        "Manchester"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("minimal_ricegrass/uk-food-monitor").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 = { "localAuthorities": ["Manchester"] }

# Run the Actor and wait for it to finish
run = client.actor("minimal_ricegrass/uk-food-monitor").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 '{
  "localAuthorities": [
    "Manchester"
  ]
}' |
apify call minimal_ricegrass/uk-food-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,minimal_ricegrass/uk-food-monitor"
        }
    }
}

```

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/rHPBvlpPRdQPGo371/builds/H8EitCelRyTWjE5rd/openapi.json
