# UK Waste Carrier Licence Monitor (`nerolabs/uk-waste-carrier-monitor`) Actor

Verify and monitor UK Environment Agency waste carrier licences: single lookup, bulk compliance checks, and a recurring monitor mode that flags status changes and expiry windows.

- **URL**: https://apify.com/nerolabs/uk-waste-carrier-monitor.md
- **Developed by:** [Adam Pearce](https://apify.com/nerolabs) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 record returneds

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/platform/actors/running/actors-in-store#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 Waste Carrier Licence Monitor

Hired a skip firm, a house clearance company, or a waste contractor? If you cannot show you checked they were a registered waste carrier, you can be fined an unlimited amount even if you had no idea they were unlicensed. This Actor checks the Environment Agency's public register in seconds, and can keep checking it for you on a schedule so a licence that lapses next month does not become your problem next month.

### The legal exposure this covers

Section 34(1) of the Environmental Protection Act 1990 puts a duty of care on "any person who imports, produces, carries, keeps, treats or disposes of controlled waste" to take "all such measures applicable to him in that capacity as are reasonable in the circumstances", including, on transfer of the waste, to secure that it goes "only to an authorised person or to a person for authorised transport purposes." Handing your waste to an unregistered carrier fails that duty. Ignorance is not a defence, the duty is to check, not to hope.

The Environment Agency's own public register (environment.data.gov.uk) is the official source. It has no bulk lookup, no alerts, and no way to ask "has anything changed since I last checked." That is the gap this Actor fills.

### Upper tier vs lower tier, in plain English

- **Upper tier**: the carrier moves other people's waste (or their own construction/demolition waste) as part of their business. This is what most skip hire firms, house clearance companies, and commercial waste contractors need. Upper tier registrations expire and must be renewed every 3 years.
- **Lower tier**: mostly for businesses that only carry their own waste (e.g. a shop taking its own packaging to be recycled) or certain categories like waste brokers who never physically handle it. Lower tier registrations are indefinite, they do not expire.

Both tiers appear on the same public register. This Actor returns which tier a registration holds and, for upper tier, how close it is to expiry.

### Example: a real registration, pulled live

**Input**

```json
{ "registrationNumber": "CBDU370419" }
```

**Output**

```json
{
  "registrationNumber": "CBDU370419",
  "registeredBusinessName": "RITEMIX LIMITED",
  "companyNumber": "03091351",
  "licenceType": "upper_tier",
  "registrationTypeLabel": "Carrier, Broker, Dealer",
  "registrationStatus": "active",
  "registrationDate": "2023-12-11",
  "expiryDate": "2027-01-20",
  "daysUntilExpiry": 161,
  "indefiniteRegistration": false,
  "expiryBand": "ok",
  "registeredAddress": "RITEMIX, HALTWHISTLE, NE49 0ND",
  "sourceUrl": "https://environment.data.gov.uk/public-register/waste-carriers-brokers/registration/CBDU370419.html",
  "dataRetrievedAt": "2026-08-11T22:17:39.769Z",
  "notFound": false
}
```

A lower-tier holder looks almost the same, except `expiryDate` and `daysUntilExpiry` come back `null` and `indefiniteRegistration` is `true`, because that registration never expires. A number that is not on the register at all comes back as a clean, structured `notFound: true` record with a plain-English reason, never a crash or a generic error.

### Monitor mode: the part that pays for itself

A one-off check tells you a carrier is licensed today. It tells you nothing about next month. Monitor mode fixes that:

1. Set `monitorMode: true` and give it `registrationNumbers` (an exact registration number is a stable ID to track, a company name is not, so name search is not trackable over time).
2. The first run establishes a baseline for each number, and still flags anything already worth knowing on day one (already expired, already close to expiry, already missing from the register).
3. Every run after that compares fresh data against what it saw last time and returns **only what changed**, plus a plain-English summary. No change still returns a cheap confirmation, so you know the check actually ran.
4. Schedule the Actor in Apify (Actor page → Schedules → Add schedule, e.g. daily or weekly) with the same input every time. That is the entire setup, no separate cron infrastructure needed.

Changes it watches for: status moving away from active, expiry crossing your warning threshold (default 90 days) or urgent threshold (default 30 days) in either direction, the registered role changing (e.g. a carrier authorisation being dropped), the registered address changing (a real fraud signal worth a second look), and a registration disappearing from the register entirely, see the FAQ below for exactly what that last one does and does not tell you.

### Use cases

**A construction firm vetting a new skip hire company.** Before the contract is signed, one lookup confirms the registration number on the quote is real, active, and upper tier, not just a number printed on a van.

**A facilities manager running weekly checks on 50 contractors.** Put all 50 registration numbers into `registrationNumbers` with `monitorMode: true` and a weekly schedule. Most weeks, every result is a cheap no-change confirmation. The week one contractor's licence lapses, that is the one result that costs more, because it is the one result that matters.

**An AI agent triggering an alert when a contractor's licence is about to expire.** Point an agent at this Actor with a watchlist and an `expiryWarningDays` threshold. It gets back structured JSON it can act on directly, no HTML to parse, no manual register check to remember.

### Pricing (pay per event)

| Event | Price | When it fires |
|---|---|---|
| Single or name-search lookup | $0.01 / record | `registrationNumber` or `companyName`, not using Monitor mode |
| Bulk lookup | $0.008 / record | `registrationNumbers` array, not using Monitor mode |
| Monitor mode, change detected | $0.05 / event | Something worth knowing changed since last run |
| Monitor mode, no change | $0.002 / event | Confirms the check ran and nothing changed |

Realistic cost example: watching 50 contractors weekly in Monitor mode, where 2 or 3 typically have something change in a given week, costs roughly $0.10 to $0.15 (2-3 change events) plus $0.09 to $0.10 (47-48 no-change confirmations) per run, call it under $0.25/week, under $13/year, for 50 contractors checked every single week. A one-off single lookup costs a cent.

### FAQ

**Does this catch a revoked or suspended licence?** Honestly: the Environment Agency's register does not publish a separate "revoked" or "suspended" status field, only a registration date and (for upper tier) an expiry date, confirmed by reading the register's own API specification before building this, not assumed. What it does do reliably: if a registration is removed from the register for any reason, including revocation, this Actor's lookup for that number starts returning `not_found_on_register`, and in Monitor mode that transition is flagged as a change event immediately. You get the fact that it disappeared, which is the actionable part, the register itself just does not label the cause. If you need the specific reason, that requires contacting the Environment Agency directly.

**Does this work for trade or commercial waste carriers, not just household names?** Yes, that is the primary use case. Skip hire firms, house clearance companies, and demolition/construction contractors are almost always upper tier, and upper tier is exactly where the expiry-tracking in Monitor mode earns its keep, lower-tier registrations never expire so there is nothing to track there beyond the address and role fields.

**Can I look someone up by their Companies House number alone?** Not directly, the Environment Agency register has no Companies House filter. Supply `companiesHouseNumber` alongside a `registrationNumber` or `companyName` instead, and the Actor cross-checks it against the number the Environment Agency itself has on file for that holder, flagging a match or mismatch. Sole traders and partnerships have no company number and come back as "not applicable."

**What happens if a registration number is wrong or was never valid?** A clean, structured result: `notFound: true` with a plain-English reason. Never a crash, never a generic error, that distinction between "this carrier was never registered" and "the lookup failed" matters for compliance records.

If this Actor helped you catch a licence problem before it became your problem, a review on this page helps a lot and helps decide what to build next.

### Source

Data comes from the Environment Agency's official public register at [environment.data.gov.uk](https://environment.data.gov.uk/public-register/view/search-waste-carriers-brokers), used under the [Environment Agency Conditional Licence](https://www.gov.uk/government/publications/environment-agency-conditional-licence/environment-agency-conditional-licence), which explicitly permits commercial use "by including it in your own product or application." This Actor is not affiliated with the Environment Agency, always attribute the source when redistributing this data further.

# Actor input Schema

## `registrationNumber` (type: `string`):

Exact Environment Agency registration number, e.g. CBDU370419 (upper tier) or CBDL73797 (lower tier). Use this for a single-record lookup.

## `companyName` (type: `string`):

Full or partial business/individual name. Returns every matching registration, not one guaranteed match, since two carriers can share a similar name. Not usable with Monitor mode, see below.

## `companiesHouseNumber` (type: `string`):

Pair with Registration number or Company name. Compared against the company number the Environment Agency itself has on file for that holder, to flag a match or mismatch. This is not a live Companies House lookup, sole traders and partnerships have no company number and come back as 'not applicable' rather than a mismatch.

## `registrationNumbers` (type: `array`):

A list of exact registration numbers. Required for Monitor mode (a fuzzy name doesn't identify a stable record to track over time). Also the fastest way to check many known contractors in one run.

## `monitorMode` (type: `boolean`):

When true, the Actor loads what it saw last time for each registration number, compares against what the register says now, and returns only the change (or a cheap no-change confirmation). Schedule this Actor to run daily or weekly with the same registrationNumbers and watchlistId for ongoing compliance monitoring. Requires registrationNumber and/or registrationNumbers, not companyName.

## `watchlistId` (type: `string`):

Only needed if you're running more than one independent monitored watchlist from the same Apify account (e.g. one per client or site) and want their delta history kept separate. Leave as default otherwise.

## `expiryWarningDays` (type: `integer`):

Monitor mode flags a registration as a change event when its expiry falls within this many days.

## `expiryUrgentDays` (type: `integer`):

A second, more urgent flag when expiry falls within this many days.

## `maxNameSearchResults` (type: `integer`):

Caps how many matches a company-name search returns.

## Actor input object example

```json
{
  "registrationNumber": "CBDU370419",
  "registrationNumbers": [],
  "monitorMode": false,
  "watchlistId": "default",
  "expiryWarningDays": 90,
  "expiryUrgentDays": 30,
  "maxNameSearchResults": 20
}
```

# 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 = {
    "registrationNumber": "CBDU370419"
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerolabs/uk-waste-carrier-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 = { "registrationNumber": "CBDU370419" }

# Run the Actor and wait for it to finish
run = client.actor("nerolabs/uk-waste-carrier-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 '{
  "registrationNumber": "CBDU370419"
}' |
apify call nerolabs/uk-waste-carrier-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nerolabs/uk-waste-carrier-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/mdpZKFnlo4pcJJo3q/builds/DnZ05y1cyoqzB1c2f/openapi.json
