# UK Food Hygiene Rating Change Signals (`starshaped_bullsnake/uk-food-hygiene-rating-change-signals`) Actor

Detect business-level changes in official FSA/FHRS public data.

- **URL**: https://apify.com/starshaped\_bullsnake/uk-food-hygiene-rating-change-signals.md
- **Developed by:** [Starshape Tools](https://apify.com/starshaped_bullsnake) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 food hygiene rating change signals

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 Hygiene Rating Change Signals

This Actor compares complete official Food Standards Agency authority snapshots and emits one machine-readable business change per Dataset row. It reports changes rather than re-exporting the full food-business directory.

### Official source and supported signals

It requests [FSA API v2 authority metadata](https://api.ratings.food.gov.uk/Authorities/1/1000) with `x-api-version: 2`, then downloads the authority's official daily XML `FileName`. No authentication, browser, or proxy is required.

Signals: `RATING_DROPPED`, `RATING_IMPROVED`, `RATING_PENDING`, `RATING_PENDING_CLEARED`, `NEW_INSPECTION`, `NEW_BUSINESS`, `BUSINESS_REMOVED`, `BUSINESS_NAME_CHANGE`, `ADDRESS_CHANGE`, and `BUSINESS_TYPE_CHANGE`.

FHRS numeric ratings compare 0–5. FHIS compares `Improvement Required < Pass < Pass and Eat Safe`. Exempt, awaiting-inspection, awaiting-publication, and unknown values are not ordinal. Numeric input filters apply only to FHRS and leave FHIS signals unaffected. Severity is a heuristic description of observed change, never a prediction.

### Baseline, incremental state, and safety

The first successful run creates a baseline and emits no `NEW_BUSINESS` rows. `baselineOnly: true` cannot reset an existing baseline. `localAuthorities` accepts exact authority ID, code, or name and defines source scope; all other filters are output-only.

The named KVS `uk-food-hygiene-rating-change-signals-state` keeps a small scope `CURRENT` manifest referencing authority-partitioned generations and deterministic 2,000-record chunks. Unchanged `LastPublishedDate` partitions are reused. Complete XML status and `ItemCount` are validated before diffing. Failed or partial downloads never imply removal. If an established authority shows anomalous mass new/removal churn, its old partition remains committed, ordinary mass signals are suppressed, and run metadata sets `humanReviewRequired` while normal authorities continue.

Detected signals are persisted before `CURRENT` commit. A durable cursor advances only after successful Dataset writes. `maxItems` can split a large batch across several runs without losing changes; pending signals drain before new source processing. Deterministic `signalId` supports downstream deduplication around rare low-level partial failures.

### Try it with sample data

```json
{"mode":"sample"}
```

Sample mode makes zero external requests, uses no production KVS, is deterministic, and runs the production diff engine. It demonstrates FHRS 5→2, FHRS 2→5, pending-rating, later-inspection, and FHIS degradation.

### Example live input

```json
{"mode":"live","localAuthorities":["277"],"baselineOnly":true,"maxItems":100}
```

Schedule daily after the authority's normal publication time. A later run with unchanged authority markers skips downloads and emits zero rows.

### Example output

```json
{"signalId":"…","signalType":"RATING_DROPPED","severity":"HIGH","signalSummary":"Rating dropped from 5 to 2","detectedAt":"2026-01-02T00:00:00.000Z","sourceObservedAt":"2026-01-02T00:00:00.000Z","businessId":"1","businessName":"One","businessType":null,"address":null,"postcode":null,"localAuthority":null,"previousRating":"5","currentRating":"2","previousInspectionDate":"2025-01-01","currentInspectionDate":"2025-01-01","ratingValue":"2","schemeType":"FHRS","sourceUrl":"https://ratings.food.gov.uk/","previousValue":"5","currentValue":"2"}
```

### Limitations and compute

`FHRSID` is the primary identity, but FSA notes IDs can change during database or authority migrations and can be retained if a local business ID is reused; the anomaly guard limits damage but cannot infer real-world continuity. Private-address fields may be absent. Whole-UK scope downloads many authority files and uses substantial network, memory, and KVS operations; a specific authority scope is recommended for focused monitoring. Data is source-publication current, not real-time.

# Actor input Schema

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

Live official sources or offline sample.

## `signalTypes` (type: `array`):

Optional output-only filter using a documented supported signal name.

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

IDs, codes, or exact names defining source scope.

## `postcodePrefix` (type: `string`):

Optional output filter.

## `businessTypes` (type: `array`):

Optional exact output filter.

## `minPreviousRating` (type: `integer`):

Numeric filter applied only to FHRS.

## `maxCurrentRating` (type: `integer`):

Numeric filter applied only to FHRS.

## `baselineOnly` (type: `boolean`):

Create a missing baseline without resetting one.

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

Maximum queued signals emitted per run.

## Actor input object example

```json
{
  "mode": "sample",
  "baselineOnly": false,
  "maxItems": 100
}
```

# Actor output Schema

## `OUTPUT` (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 = {
    "mode": "sample"
};

// Run the Actor and wait for it to finish
const run = await client.actor("starshaped_bullsnake/uk-food-hygiene-rating-change-signals").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 = { "mode": "sample" }

# Run the Actor and wait for it to finish
run = client.actor("starshaped_bullsnake/uk-food-hygiene-rating-change-signals").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 '{
  "mode": "sample"
}' |
apify call starshaped_bullsnake/uk-food-hygiene-rating-change-signals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,starshaped_bullsnake/uk-food-hygiene-rating-change-signals"
        }
    }
}

```

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/rzd11QZuuCPAPjm8y/builds/C6ipoOfbJavAsPpHl/openapi.json
