# UK Food Hygiene Ratings (`wildorigins/uk-food-hygiene-ratings`) Actor

🏷️ From $0.60 / 1K | Official FSA food hygiene ratings for every UK food business. Search by council, postcode, business type or rating, and isolate businesses rated 2 or below.

- **URL**: https://apify.com/wildorigins/uk-food-hygiene-ratings.md
- **Developed by:** [Wild Origins](https://apify.com/wildorigins) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 establishments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Food Hygiene Ratings

Food hygiene ratings for every food business in the UK, straight from the official Food Standards Agency register. Restaurants, cafes, takeaways, pubs, hotels, schools, care homes, shops and manufacturers. No API key needed.

### 🔍 What does UK Food Hygiene Ratings do?

It queries the FSA's own ratings API and returns every matching establishment as a clean row of data, ready for a spreadsheet, a CRM or your own product. Search by council, postcode, coordinates, business name, business type or rating.

The useful bit is `actionableOnly`. Set it and you get only businesses rated 2 or below, meaning the FSA has told them to improve. Those are the businesses actively looking to fix something right now.

That matters if you sell:

- Food hygiene consultancy, audits or Level 2 and 3 training
- Pest control
- Commercial kitchen deep cleaning or equipment
- HACCP software or temperature monitoring
- EPOS and compliance systems

Every record includes the address, postcode, coordinates, and the council's own environmental health contact.

### 🏛️ What the official route gives you, and what this adds

The Food Standards Agency is the rare register with a genuinely good official API. [api.ratings.food.gov.uk](https://api.ratings.food.gov.uk/Help/Index) is free, needs no key, and its search endpoint filters by name, coordinates, business type, rating and local authority. If you want to write and maintain your own integration, it is a fine place to start.

What this Actor adds is everything around that call. The FSA API has sharp edges that fail silently: measured against the same Manchester postcode, an `address` search matches literal address text rather than proximity and returned 1 establishment where a geocoded coordinate search with the same radius returned 3,179, and the rating filter is quietly dropped when it is combined with an address search, so a query for rating 2 comes back rated 5 with no warning. This Actor geocodes postcodes properly, applies every filter and verifies it narrowed the result, adds the `actionableOnly` flag the API does not have, and delivers flat rows straight into CSV, Excel, Sheets or a webhook on a schedule. Same official data, none of the integration work.

### 📊 What data can I extract from the FSA?

One row per establishment:

| Field | Description |
|---|---|
| 🆔 `fhrsId` | FHRS id of the establishment |
| 🏢 `businessName` | Establishment name |
| 🏷️ `businessType` | Kind of business, such as Restaurant or Takeaway |
| 🏷️ `businessTypeId` | FSA id of the business type |
| 🏠 `address` | Postal address as published |
| 📍 `postcode` | UK postcode |
| 📞 `phone` | Published phone number, when any |
| 📍 `latitude` | Latitude of the establishment |
| 📍 `longitude` | Longitude of the establishment |
| ⭐ `ratingValue` | Hygiene rating, 0 to 5, or a Scottish scheme value |
| ⭐ `ratingNumeric` | Rating as a number where the scheme allows, otherwise null |
| 📅 `ratingDate` | Date of the rating inspection |
| ✅ `newRatingPending` | Whether a new rating is pending publication |
| ✅ `isActionable` | Whether the rating is 2 or below |
| 🔢 `hygieneScore` | Inspector score for food handling, lower is better |
| 🔢 `structuralScore` | Inspector score for premises condition, lower is better |
| 🔢 `managementConfidenceScore` | Inspector confidence in management, lower is better |
| 🏷️ `schemeType` | FHRS in England, Wales and Northern Ireland, FHIS in Scotland |
| 🏢 `localAuthority` | Local authority that inspected |
| 📧 `localAuthorityEmail` | Contact email of the local authority |
| 🔗 `localAuthorityWebsite` | Website of the local authority |
| 🔗 `fsaUrl` | Establishment page on the FSA site |
| ⏱️ `retrievedAt` | When the row was collected, ISO 8601 |

Note that Scotland uses a different scheme with Pass and Improvement Required rather than 0 to 5, so `ratingNumeric` and `isActionable` are null and false for Scottish records.

### 💡 Why scrape food hygiene ratings?

**Lead generation.** Every business in an area rated 2 or below, with the address and the council's environmental health contact attached.

**Market research.** Rating distribution by area or business type, from the official register rather than a sample.

**Property and franchise.** Check a site's hygiene history before signing a lease or approving a franchisee.

**Consumer apps.** Enrich a restaurant listing with its official rating and a link back to the FSA record.

### 🧭 Use cases

A hygiene consultant building Monday's call list. `actionableOnly` within 5 miles of the office returns every business the FSA has just told to improve, with the address and phone number where published.

A commercial kitchen cleaning firm targeting one council at a time. `localAuthorityId` with `actionableOnly` gives every low rated premises in the borough in one run, with the council's environmental health contact on each row.

A franchise operations manager checking every branch's current rating each month. A scheduled run on the brand in `name`, diffed against last month's sheet, flags the branch that slipped from 5 to 3 before head office hears it from a customer.

A landlord or agent checking a restaurant tenant's hygiene history before renewing a lease. The premises postcode returns the current rating, the three component scores and whether a re-inspection result is pending.

A food delivery or listings product showing the official rating next to each restaurant. Call the Actor from the API with the restaurant's name and postcode and store `ratingValue` and `fsaUrl` against the listing.

A journalist ranking takeaway hygiene across a city for a local story. Every establishment of one `businessTypeId` in the council, with component scores, straight into a spreadsheet that cites the official register.

### 🚀 How do I use UK Food Hygiene Ratings?

1. Click **Try for free**.
2. Set at least one filter, most often `postcodeOrAddress` with a `radiusMiles`.
3. Narrow it with `name`, `businessTypeId` or `ratingValue`.
4. Turn on `actionableOnly` to keep only businesses rated 2 or below, and set `maxResults` to cap the run.
5. Click **Start**, then download the results as JSON, CSV or Excel, or pull them from the API.

### ⬇️ Input

The FSA rejects unfiltered queries, so set at least one of `localAuthorityId`, `name`, `postcodeOrAddress`, `latitude` and `longitude`, `businessTypeId` or `ratingValue`.

```json
{
  "postcodeOrAddress": "M1 1AE",
  "radiusMiles": 5,
  "ratingValue": "2",
  "actionableOnly": true,
  "maxResults": 200
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `postcodeOrAddress` | string | `M1 1AE` | Free text address or postcode |
| `name` | string | | Business name contains |
| `businessTypeId` | integer | | Numeric business type id |
| `ratingValue` | string | | Exact rating, 0 to 5 |
| `actionableOnly` | boolean | `false` | Keep only businesses rated 2 or below |
| `radiusMiles` | integer | `1` | Radius around the postcode or coordinates |
| `maxResults` | integer | `500` | Hard cap on billable results |

Three further inputs are available. `localAuthorityId` takes one of 363 councils, listed at [api.ratings.food.gov.uk/Authorities](https://api.ratings.food.gov.uk/Authorities). `latitude` and `longitude` do a radius search from coordinates instead of a postcode. `sortBy` takes rating, alpha or distance. Business type ids are listed at [api.ratings.food.gov.uk/BusinessTypes](https://api.ratings.food.gov.uk/BusinessTypes).

### ⬆️ Output

#### Table view

Results arrive as a Ratings table you can sort and filter in the Console, with the business, rating, address and council lined up for scanning. There is a second view, Inspection scores, which lines up the three component scores for comparing establishments against each other.

#### JSON

A typical row:

```json
{
  "businessName": "Abaseen",
  "businessType": "Takeaway/sandwich shop",
  "ratingValue": "2",
  "ratingDate": "2025-04-18T00:00:00.000Z",
  "address": "484 Cheetham Hill Road, Manchester",
  "postcode": "M8 9JW",
  "localAuthority": "Manchester City Council",
  "fsaUrl": "https://ratings.food.gov.uk/business/1820175",
  "fhrsId": 1820175
}
```

Download it from the run as JSON, CSV or Excel, or read it straight from the API.

### Postcode formats

Type the postcode however you have it. All of these reach the same place:

```
M1 1AE       m1 1ae       M11AE        m11ae
M1-1AE       M1.1AE       "M1 1AE"     M1 1AE.
```

Case, spacing and surrounding punctuation are all normalised before the lookup runs. This matters more than it looks: the postcode is geocoded so that the radius is honoured, and a postcode that fails to resolve falls back to being matched as literal address text, which quietly returns a single business instead of the surrounding area.

`GIR 0AA` is handled as the special case it is. Anything that is not a postcode, such as a place name or a full address, is matched as address text instead.

### Data source

Food Standards Agency, [ratings.food.gov.uk](https://ratings.food.gov.uk/), published under the Open Government Licence v3.0. Ratings are a snapshot of the last inspection and can change. Always link back to `fsaUrl` for the current position.

### ⏱️ How long does a run take?

Measured on real runs, so you know what normal looks like and can tell it apart from a run that has stalled.

| Establishments returned | Typical run time |
|---|---|
| 10 | 2 to 4 seconds |
| 100 | about 10 seconds |
| 200 | 10 to 12 seconds |
| 500 | 25 to 41 seconds |

The FSA's own API is fast and the Actor reads it 200 establishments to a page, so the run time is close to linear in the number of rows and a whole council or a five mile radius around a city centre postcode is done inside a minute. `pageSize` is the only setting that changes the shape of that, by trading more rows per request against fewer requests.

Two things add a second or two at the front. A **postcode search** is resolved to coordinates through postcodes.io before the radius search can run, which is one extra lookup, and a **rating filter combined with an address** makes the Actor take the coordinate route rather than the address route so the filter is actually applied. Neither is something you have to configure. The first few seconds of any run are the container starting rather than the work.

A run is never silently stuck. Each page of results is logged as it is read, and establishments are written to the dataset as they are parsed rather than held back to the end, so a run that hits its time limit still leaves everything it had already collected. A search that matches nothing is a successful run with an empty dataset and a status message telling you to widen the radius, not an error.

**Set the run timeout to suit the size of the ask.** This Actor's default is 3600 seconds, which is comfortably more than the largest run in the table above (500 premises takes about 1 minute). You are charged per delivered result rather than per minute, so a generous timeout costs you nothing and a tight one risks losing the run's work. Lower it only if you want a hard ceiling on how long a scheduled run may sit.

### 💰 How much does it cost?

You pay per establishment returned. Set `maxResults` to cap what a run can cost, and use `actionableOnly` or `ratingValue` to avoid paying for records you were only going to filter out afterwards.

Runs that return nothing cost nothing. Paid Apify plans get a lower rate per establishment.

### 🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make or your own webhook using [Apify integrations](https://docs.apify.com/platform/integrations). You can also trigger a run whenever something happens in another tool, which is the usual way to drop new low rated businesses into a CRM as leads.

UK Food Hygiene Ratings is also callable by AI agents through the [Apify MCP server](https://mcp.apify.com). An agent connected to the server can discover the Actor, read its input schema and call it as a tool under the identifier `spookyweb/uk-food-hygiene-ratings`, then read the dataset back in the same session. That lets an assistant asked about a restaurant's hygiene rating answer from the FSA register rather than from training data. Setup is covered in the [Apify MCP documentation](https://docs.apify.com/platform/integrations/mcp).

### 🔗 Using UK Food Hygiene Ratings with the Apify API

```bash
curl -X POST "https://api.apify.com/v2/acts/spookyweb~uk-food-hygiene-ratings/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"postcodeOrAddress": "M1 1AE", "radiusMiles": 5, "ratingValue": "2", "actionableOnly": true, "maxResults": 200}'
```

Or with the Apify client:

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/uk-food-hygiene-ratings').call({
  postcodeOrAddress: 'M1 1AE',
  radiusMiles: 5,
  ratingValue: '2',
  actionableOnly: true,
  maxResults: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Full detail is in the [Apify API reference](https://docs.apify.com/api/v2), and every run is also callable from the [Python](https://docs.apify.com/api/client/python) and [JavaScript](https://docs.apify.com/api/client/js) clients.

### ❓ FAQ

#### What does a rating of 2 or below mean?

That the FSA has told the business to improve. A 5 is very good and a 0 means urgent improvement is necessary, so anything at 2 or lower is a business with an open problem and a reason to buy help. Set `actionableOnly` to true and you get only those, and the `isActionable` field flags them on every row.

#### Does it cover Scotland?

Yes, but Scotland runs a different scheme. FHIS publishes Pass or Improvement Required instead of a 0 to 5 number, so `ratingValue` carries that text, and `ratingNumeric` is null and `isActionable` is false for Scottish records. The `schemeType` field tells you which scheme a row came from.

#### How current are the ratings?

A rating is a snapshot of the last inspection, and the register is updated as councils publish new inspections. `ratingDate` tells you when the current rating was given, and `newRatingPending` flags an establishment whose re-inspection result is due. Always link back to `fsaUrl` for the current position.

#### Can I search a whole council area?

Yes, set `localAuthorityId` to the council you want. The full list of 363 councils is at [api.ratings.food.gov.uk/Authorities](https://api.ratings.food.gov.uk/Authorities). Pair it with `actionableOnly` to get every low rated business in that council in one run.

#### What are the three component scores?

`hygieneScore` covers food handling, `structuralScore` covers the condition of the premises, and `managementConfidenceScore` covers how much the inspector trusts management to keep standards up. Lower is better on all three, and together they produce the headline rating. They are useful for telling apart two businesses that both scored a 2 for different reasons.

#### Do I need an FSA API key?

No. The FSA publishes this as open data with no key and no registration. The only rule to know is that it rejects an unfiltered query, so you must set at least one of the filters listed in the Input section.

### ⚖️ Is it legal to scrape food hygiene ratings?

The Food Standards Agency publishes hygiene ratings as open data so the public can check where they eat. This is business information about premises, not personal data, and it comes from the FSA's own API.

Data is published under the Open Government Licence v3.0, so commercial use is permitted with attribution. Apify's [ethical scraping guide](https://blog.apify.com/is-web-scraping-legal/) covers the wider picture.

### 👍 Your feedback

Found a bug, or want a field that is not here yet? Open an issue on the Actor's Issues tab. Requests that make the data more useful get built, and problems get fixed quickly.

### 🔎 You might also like

| Actor | What it does |
|---|---|
| [UK Planning Applications](https://apify.com/spookyweb/uk-planning-applications) | Planning applications from every UK council, tagged by the type of work proposed |
| [UK Insolvency Register Search](https://apify.com/spookyweb/uk-insolvency-register-search) | Individual Insolvency Register search for bankruptcies, IVAs and debt relief orders |
| [Website Contact Scraper](https://apify.com/spookyweb/website-contact-scraper) | Company websites in, one contact record per domain out, with emails and social links |

# Actor input Schema

## `localAuthorityId` (type: `integer`):

Numeric FSA local authority id, for example 1 for Cambridge City or 27 for Welwyn Hatfield. See https://api.ratings.food.gov.uk/Authorities for the full list.

## `postcodeOrAddress` (type: `string`):

Free text address or postcode search, for example CB1 or Cambridge.

## `latitude` (type: `string`):

Latitude for a radius search. Use with longitude and radius.

## `longitude` (type: `string`):

Longitude for a radius search. Use with latitude and radius.

## `radiusMiles` (type: `integer`):

Search radius around the given coordinates.

## `name` (type: `string`):

Search by business name, for example greggs.

## `businessTypeId` (type: `integer`):

Numeric FSA business type id, for example 1 for Restaurant, Cafe or Canteen. See https://api.ratings.food.gov.uk/BusinessTypes for the list.

## `ratingValue` (type: `string`):

Only return businesses with this exact rating, 0 to 5. Applies to the England, Wales and Northern Ireland scheme.

## `actionableOnly` (type: `boolean`):

Keep only businesses that have been told to improve. These are the ones actively in the market for hygiene consultancy, training, pest control or equipment.

## `sortBy` (type: `string`):

How the FSA should order results before paging.

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

Hard cap on results. Each establishment is one billable result.

## `pageSize` (type: `integer`):

Records per API call. Leave as default unless you have a reason.

## Actor input object example

```json
{
  "postcodeOrAddress": "M1 1AE",
  "radiusMiles": 5,
  "ratingValue": "",
  "actionableOnly": false,
  "sortBy": "rating",
  "maxResults": 200,
  "pageSize": 200
}
```

# Actor output Schema

## `results` (type: `string`):

One row per item: establishments with their hygiene ratings.

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

Counts and stop reason for the run.

# 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 = {
    "postcodeOrAddress": "M1 1AE",
    "radiusMiles": 5,
    "maxResults": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("wildorigins/uk-food-hygiene-ratings").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 = {
    "postcodeOrAddress": "M1 1AE",
    "radiusMiles": 5,
    "maxResults": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("wildorigins/uk-food-hygiene-ratings").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 '{
  "postcodeOrAddress": "M1 1AE",
  "radiusMiles": 5,
  "maxResults": 200
}' |
apify call wildorigins/uk-food-hygiene-ratings --silent --output-dataset

```

## MCP server setup

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

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/yTDuT6xAODNgHZmYG/builds/CAEzVWJrcO9J48RXU/openapi.json
