# UK Tariff Watch: Duties, Controls & Measure Changes (`thatmike1/uk-tariff-measure-watch`) Actor

Check UK commodity codes on the official Trade Tariff: duties, controls, conditions, quotas and geographies per code, with changes since the last check. Successful unchanged checks are billed. Unofficial.

- **URL**: https://apify.com/thatmike1/uk-tariff-measure-watch.md
- **Developed by:** [Michal Pšenčík](https://apify.com/thatmike1) (community)
- **Categories:** Other, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 commodity code checks

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 Tariff Watchlist: Duty & Measure Changes

Check 1–100 exact declarable UK commodity codes against the official public Trade Tariff API. Each complete stored receipt contains the commodity, all returned import/export measures, the reachable JSON:API resources and any changes since the last delivered snapshot in an optional persistent watch.

The output preserves duty expressions, controls, conditions and components, legal references, additional codes, quota definitions, footnotes, geographical groups and published exclusions. Directions are explicit. A successful unchanged check is billed at the same price as a baseline or revised check.

This is an unofficial factual-data export. It provides no classification, landed-cost arithmetic, customs or legal advice, preference eligibility, quota availability calculation or country filtering. Coverage is the requested UK Global Tariff code only; Northern Ireland/XI is outside scope. Source `origin: "eu"` describes historical measure provenance, not the origin of your goods.

### Input

```json
{
    "commodityCodes": ["0101210000", "0901210000"],
    "watchName": "my-uk-portfolio",
    "maxItems": 100,
    "requestIntervalMs": 2000
}
```

| Field               | Meaning                                                                                                                                                                                                                               |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `commodityCodes`    | 1–100 strings, exactly ten digits after outer trimming. Leading zeroes are preserved. Duplicates are checked once, in first-occurrence order. Default: `0101210000`. Numbers, URLs, headings and lists over 100 entries are rejected. |
| `watchName`         | Optional persistent namespace, 1–64 ASCII letters, digits, `_` or `-`. Omit/null for stateless checks. Serialize schedules for each name.                                                                                             |
| `asOf`              | Optional real `YYYY-MM-DD`, from 2021-01-01 through today's Europe/London date. Allowed only without a watch. Historical views may be corrected retrospectively.                                                                      |
| `maxItems`          | Integer 1–100, default 100. Caps actually delivered code receipts. SUMMARY lists codes left unattempted.                                                                                                                              |
| `requestIntervalMs` | Idle time after each completed HTTP attempt, 1500–10000 ms; default 1500. Examples and live tests use 2000.                                                                                                                           |

Unknown input fields are rejected. There are no credentials, proxies, caller URLs or alternative hosts. Schedule stateless runs at most daily. Apify scheduling and webhooks can consume the results; the actor does not send notifications.

### Output

One default-dataset row is one complete code receipt. Measures and deltas are nested within that row, with no separate item charges. Export JSON for the full graph; the table is a compact overview.

| Field                                    | Meaning                                                                                                                                                                                 |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `recordId`, `commodityCode`              | SHA-256 run/code/date receipt identity and exact requested code.                                                                                                                        |
| `schemaVersion`, `normalizationVersion`  | Both `1`.                                                                                                                                                                               |
| `sourceJurisdiction`, `requestedAsOf`    | `UK_GLOBAL_TARIFF` and requested date or null.                                                                                                                                          |
| `observedAt`, `previousObservedAt`       | Successful source observation and prior delivered observation. A reused cached observation retains its original retrieval time.                                                         |
| `changeType`, `changedFields`, `changes` | `baseline`, `unchanged` or `revised`; sorted entity IDs; whole old/new entity evidence with relative RFC 6901 field paths. Arrays compare as whole normalized values.                   |
| `snapshotHash`, `previousSnapshotHash`   | Canonical normalized snapshot SHA-256 values. Retrieval times and HTTP headers are excluded.                                                                                            |
| `snapshot`                               | Commodity attributes/footnotes, measure directions and complete reachable resource graph. Unrelated hierarchy/trade-summary resources are excluded.                                     |
| `measures`                               | Projection of the same graph: IDs, directions, source origin, type, geography and immediate member IDs, exclusions, raw duty text, conditions/component IDs and source effective dates. |
| `source`                                 | Exact source URL, status 200, original UTF-8 body hash, attribution and licence URL. `sourcePublishedAt` is null; no authoritative publication timestamp was established.               |
| `coverage`, `warnings`                   | Complete requested-code response counts, null next page and `notReturnedIsDeletion: false`; any receipt warnings.                                                                       |

Duty text such as `121.00 GBP / 1000 kg` is preserved, including units and currency. Null remains distinct from an empty string. Published HTML/Markdown stays inert text: escape it when displaying output. Geographical membership and exclusions do not determine preferential-origin eligibility. A resource disappearing from a complete response means `no_longer_returned`, never a claim of legal repeal or deletion. A wording/audit timestamp change can revise a snapshot without changing any duty.

Free default key-value records `SUMMARY` and `OUTPUT` link results and report every original input index, duplicates, not-due checks, errors, delivered/charged counts, HTTP attempts/bytes, stop reasons and remaining codes. Error/status records never enter the default dataset. A 404 does not replace a watch baseline. A valid explicit zero-measure declarable response can produce a receipt; missing relationships cannot.

### Watches and delivery

A named watch stores its baseline and cached observations in `uk-tariff-watch-v1-<first 40 hex characters of SHA256(watchName)>` (59 characters, inside Apify's 63-character store-name limit). The last delivered snapshot advances only after exact receipt readback from the dataset. A row stored with zero charges still advances the baseline; a trimmed/no-op delivery does not. Before delivery, a small per-code recovery checkpoint records the receipt ID and its expected predecessor. Resuming an older run never replaces a newer baseline or resets its due time, even when snapshots and observation times match. A legacy recovered receipt without a checkpoint preserves any existing baseline. An ambiguous delivery stops further work. A post-delivery state error fails the run while preserving delivered rows.

Successfully delivered identities are not due for 24 hours. Successful fetched observations are separately cached for 24 hours and can be reused after failed delivery. A cache write never advances the baseline. Watches support at most 100 identities and 128 MiB of baseline/cache payloads, with no silent eviction. Version mismatches fail rather than resetting state.

Use one active run per watch. The owner lease rejects detected overlap, but KV storage is not a transactional distributed lock: simultaneous starts can race. Cross-run delivery is at least once. A crash between row delivery and state persistence can report a revision again on the next run. Within a resumed run, the deterministic receipt ID is reconciled before another push.

### Same-run restarts and late storage writes

This is a known limit. Apify key-value stores have no compare-and-swap, so a storage write sent by the original invocation of a run can land after a same-run restart (a platform migration or a resurrected run) has already read the run's state. Every storage request is sent once, with no client retries and a 20-second client timeout, and a restart that finds an earlier process's journal waits 30 seconds before it reads that journal again and trusts any readback. A write that lands later than that, or one sent by an original invocation that is still running, is not detected. Concretely, it can do two things. A late `MANIFEST` write can restore a stale watch reservation, so the 128 MiB baseline/cache cap undercounts what is stored. A late `RUN-CHECKPOINT-<runId>` write can roll the run journal back, for example to before a source 403 stop, so a later process of the same run sends one more source request; a rolled-back journal can likewise forget an unresolved push, which that process could then send again. Callers must let the original invocation and its storage requests stop before restarting a run: run one run at a time per watch, and use a schedule interval longer than the 420-second run timeout. A new run (a new run ID) is not affected by a dead run's journal.

### Prices

The Console reference prices are **$0.002 per complete stored code check** (`apify-default-dataset-item`) and **$0.00005 per platform start unit** (`apify-actor-start`). Successful unchanged checks are billed. Duplicates, not-due checks, errors and free KV output do not incur item events. A failed or zero-item run may still incur a start charge. The platform's displayed pricing governs actual charges; memory above 1 GB can multiply start units.

At one start unit, the event sums for 1/10/100 delivered codes are $0.00205/$0.02005/$0.20005. These are event-fee sums, not measured all-in invoices. Hosted access, spending-cap behavior and cost headroom require prepublication platform validation. Only the two built-in events are used; the code never manually charges them.

### Bounds and access

Requests are sequential, with a 20-second attempt timeout, at most two transient retries (2s then 5s backoff, or longer Retry-After), two validated same-route redirects, 125 total attempts, 64 MiB of response traffic and 2 MiB per body. Two consecutive exhausted transient sequences stop source work. Source 403, challenge content or 429 immediately stops further source requests, without bypass; challenge markers are checked in partial and undecodable bodies too, and an encoded HTML document is treated as a challenge. Auth/payment errors and 404s are free errors. Unexpected pagination, unresolved/conflicting resources, partial responses and malformed/media-type errors fail the code free.

Work has a 360-second deadline plus at most 30 seconds for persistence/termination. SDK persistence calls are bounded by that termination deadline; an operation already sent to storage cannot be cancelled. Each storage request is sent once, with the SDK client's retries turned off and a 20-second client timeout, so a dataset push or key-value write whose answer is lost is never resent behind the actor's back. A hard kill can leave the latest SUMMARY and free `RUN-CHECKPOINT-<runId>` journal. Restarting that run restores its original deadline, counters, receipt and charge accounting, source stops, Retry-After window, pacing and retry/redirect allowances before source work. The item cap and spending limit are recomputed from the dataset and the platform's charged count before every fetch and every push, so a receipt stored just before a kill still counts against the run's maximum charge. A push or state write whose result is unknown (a timeout or a lost answer) is never sent again in the same run. Until readback proves it landed, the run stops with `delivery_uncertain` or `state_write_uncertain`, and SUMMARY lists that code's `recordId` so you can check the dataset. Start a new run to check those codes again. A code whose retries were exhausted is recorded as an error in the same journal write, so a restart never requests it again; a restart after the third attempt already fetched a code ends that code as an error rather than sending a fourth request. Confirmed receipts are counted before any watch read, so a watch outage preserves delivery totals. An unreadable or mismatched journal stops work without overwriting the previous journal or SUMMARY. An interrupted in-flight request reserves its unread body allowance against the traffic budget; after recovery `responseBytes` can therefore conservatively exceed bytes actually read. Rows over 4 MiB or normalized baselines over 1 MiB fail free and are never truncated. MaxItems/deadlines can leave codes in SUMMARY for explicit caller continuation. No automatic retry run is scheduled.

The public source is `https://www.trade-tariff.service.gov.uk/api/v2/commodities/{code}`. Availability from one machine does not guarantee Apify/US datacenter access or an SLA. No browser, login, API key, cookies, proxy or challenge workaround is used. Source data is available directly from the free native API.

Source: HMRC UK Trade Tariff; Crown copyright, [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) unless otherwise stated. See the [official API field guide](https://docs.trade-tariff.service.gov.uk/the-trade-tariff-api.html). This actor is unaffiliated with HMRC. It does not target personal data; users control their watch inputs, Apify storage retention and downstream processing. Source resource text is preserved as published and may include incidental public contact information.

# Actor input Schema

## `commodityCodes` (type: `array`):

1–100 exact 10-digit declarable UK commodity codes as strings. Leading zeroes are preserved. Duplicates are checked once.

## `watchName` (type: `string`):

Optional persistent watch name, 1–64 ASCII letters/digits/underscore/hyphen. Serialize runs for each name. Successful delivered identities are not due for 24 hours.

## `asOf` (type: `string`):

Optional actual YYYY-MM-DD date from 2021-01-01 through today in Europe/London. Only allowed without a watch. Historical views may be corrected retrospectively.

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

Caps complete stored code receipts, including unchanged checks.

## `requestIntervalMs` (type: `integer`):

Idle time after each HTTP attempt completes, including redirects and retries. Live examples use 2000 ms.

## Actor input object example

```json
{
  "commodityCodes": [
    "0101210000"
  ],
  "maxItems": 100,
  "requestIntervalMs": 1500
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

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

No description

## `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 = {
    "commodityCodes": [
        "0101210000"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thatmike1/uk-tariff-measure-watch").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 = { "commodityCodes": ["0101210000"] }

# Run the Actor and wait for it to finish
run = client.actor("thatmike1/uk-tariff-measure-watch").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 '{
  "commodityCodes": [
    "0101210000"
  ]
}' |
apify call thatmike1/uk-tariff-measure-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thatmike1/uk-tariff-measure-watch"
        }
    }
}
```

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/C07xpZJxqVDtoVuGO/builds/mx44Ig8oF2YHOCSyz/openapi.json
