# TrustATrader Scraper \[$2/1K💰] | UK Trades | Vetted Leads (`ahmed_jasarevic/trustatrader-scraper`) Actor

Scrape TrustATrader (trustatrader.com), the UK's vetted tradespeople directory. Trader name, trade category, town/area with lat/lon, phone, website, rating, review breakdown, vetting checks, insurer + verification date, owner name and qualifications. Cheerio-based, no browser.

- **URL**: https://apify.com/ahmed_jasarevic/trustatrader-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 results

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

## Trustatrader Scraper

Scrape TrustATrader (trustatrader.com), the UK's directory of vetted tradespeople, into clean structured JSON: trader name, trade category, town/area with lat/lon, phone, website, rating, review count with a per-category breakdown, vetting checks, insurer + verification date, owner name and qualifications. Built for UK home-services lead generation, trade-insurance brokers (renewal calendars) and franchise expansion teams who need tradesperson data without the TrustATrader website's manual search.

Plain HTTP server-rendered HTML parsing (CheerioCrawler, no browser) with schema.org microdata - the cheapest and most reliable surface, since all trader data is present in the served HTML.

### Main Use Cases

- **UK home-services lead generation** - plumbers, electricians, builders and more by trade and location, with phone, rating and review count
- **Trade insurance lead targeting** - insurer + insurance verification date per trader for renewal calendars
- **Tradesperson database building** - vetted traders with vetting checks, qualifications, associations and company legal form
- **Local market analysis** - rating distributions and review volume by trade and UK region
- **Franchise and expansion planning** - trader density and coverage per town/city

### How It Works - Strategy & Bypass

**Strategy: CheerioCrawler over server-rendered HTML (no browser).** Reconnaissance established that TrustATrader is a classic server-rendered site behind nginx + an AWS ALB. There is **no** embedded JSON blob (`__NEXT_DATA__`, `__NUXT__`, `__APOLLO_STATE__`, `ld+json`), **no** Algolia/Meilisearch/Elasticsearch backend, and **no** internal JSON/GraphQL API. The only external API referenced by the site JS is a location typeahead (`api.geouk.xyz/places/typeahead`), which carries no trader data.

Instead, **all trader data is present in the served HTML as schema.org microdata** (`itemtype="http://schema.org/LocalBusiness"`, `AggregateRating`, `Review`, `PostalAddress`, `telephone`), so a plain HTTP + HTML parse is the cheapest, most reliable surface - a browser is unnecessary.

The only obstacle is **IP-level rate limiting** (the origin returns HTTP 429 to datacenter IPs). This is an IP problem, not a JS problem, so it is solved with the standard Apify proxy input rather than a headless browser.

#### Endpoints Used

| Surface | URL pattern | Data |
| --- | --- | --- |
| Category / listing | `https://www.trustatrader.com/{trade}-in-{location}` (e.g. `/plumbers-in-london`) | Trader cards: name, trade, town, postcode, rating, review count, phone(s), profile URL |
| Pagination | `https://www.trustatrader.com/search?trade_name=...&search_trade_id=...&location_str=...&page=N` (the "View more"/"Next" link) | Next page of trader cards (10 per page) |
| Trader profile | `https://www.trustatrader.com/traders/{slug}` | Full record: insurance details, vetting checks, qualifications, associations, lat/lon, website, description, reviews + per-category breakdown |

#### Flow

1. Build start URLs from `trade` + `location` (or use `startUrls` directly).
2. On each listing page, parse every trader card (`li.profile-review-card` on category pages with microdata; `li.profile-card` on paginated `/search` pages, same data, class-based markup) and enqueue each trader's profile URL for enrichment.
3. Queue all remaining result pages at once - a paginated `/search` page reports how many pages exist, so the rest are added in a single batch (ahead of profile pages) instead of following "Next" one page at a time. The batch stops once `maxItems` traders are queued, uses `maxPagesPerStart` as the minimum page budget, and skips traders already seen.
4. On each profile page, parse the full record and push it to the dataset. Stop at `maxItems`.

Listing and profile pages are fetched concurrently (`maxConcurrency`), so a 50-trader scrape is a handful of parallel HTTP requests rather than a sequential walk. Measured on 50 traders: 512 MB at concurrency 20 ~39 s vs 256 MB with default autoscaling ~82 s, at roughly the same cost.

### Input Parameters

| Field | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `startUrls` | array | No | `[]` | TrustATrader category/search URLs (e.g. `https://www.trustatrader.com/plumbers-in-london`) or trader profile URLs (`/traders/{slug}`). If provided, these are used instead of the Trade + Location fields |
| `trade` | string | No | `plumbers` | Trade category slug as used in URLs, e.g. `plumbers`, `electricians`, `builders`, `roofers-roofing`, `heating-engineers-and-gas-fitters`, `painters-and-decorators` |
| `location` | string | No | `london` | UK town, city or county, e.g. `manchester`, `birmingham`, `kent` |
| `maxItems` | integer | No | 50 | Maximum number of trader records to return (free users capped at 10) |
| `fetchProfiles` | boolean | No | true | Fetch each trader's profile page for the full record (insurance, vetting, qualifications, lat/lon, website, review breakdown). Disable for a faster listing-only scrape |
| `maxPagesPerStart` | integer | No | 5 | Minimum result pages (10 traders each) per start URL; pagination continues past this when more pages are needed to reach `maxItems` |
| `maxConcurrency` | integer | No | 20 | Parallel page fetches (1-50). Listing and profile pages crawl together; used as both floor and ceiling so the run starts at full speed. 5-20 is the sensible range - the site rate-limits aggressive crawling |
| `proxy` | object | No | off | Standard Apify proxy picker. **Recommended** - the origin returns HTTP 429 to datacenter IPs |

### Output Fields

One dataset row per trader (full field list in the dataset schema):

- **Identity & location**: `name`, `trade`, `town`, `region`, `postcode`, `latitude`, `longitude`, `profileUrl`, `sourceUrl`
- **Contact**: `phone`, `phones`, `website`
- **Reputation**: `rating` (out of 5), `reviewCount`, `reviewBreakdown` (Initial impression, Punctuality, Cleanliness, Quality, Value, Overall opinion), `reviews` (recent reviews with author, date, rating, body and per-category breakdown)
- **Vetting & compliance**: `vettingChecks`, `companyType`, `incorporatedOn`, `verifiedOn`, `insurer`, `insuranceVerifiedOn`
- **Professional profile**: `ownerName` (best-effort), `qualifications` (e.g. Gas Safe Registered), `associations`, `description`
- **Provenance**: `scrapedAt`

#### Notes & Limitations (verified)

- **Proxy recommended**: the origin rate-limits datacenter IPs (HTTP 429). Enable Apify Proxy for reliable production runs.
- **`ownerName` is best-effort**: TrustATrader does not expose a dedicated owner field; the actor extracts a name when the profile text states one (e.g. "Owner: John Smith"), otherwise `null`.
- **`postcode`** is only exposed on category-page cards (schema.org microdata) and is merged into the profile record during enrichment; traders found on paginated `/search` pages have no postcode in the HTML, so it is `null` for them.
- **`reviewBreakdown`** is averaged across the reviews present on the profile page.
- **Memory matters**: Apify scales container CPU with memory, and HTML parsing is CPU work, so the actor runs on 512 MB with concurrency pinned to `maxConcurrency`.

### Example Input And Output

#### Example Input

```json
{
  "trade": "electricians",
  "location": "manchester",
  "maxItems": 100,
  "fetchProfiles": true,
  "maxPagesPerStart": 5,
  "proxy": { "useApifyProxy": true }
}
```

#### Example Output

```json
{
  "name": "0800 Homefix Ltd",
  "trade": "Plumbers",
  "town": "Wokingham",
  "region": "Berkshire",
  "latitude": 51.4117,
  "longitude": -0.8336,
  "phone": "07360 278675",
  "website": "http://www.0800homefix.com/",
  "rating": 4.82,
  "reviewCount": 647,
  "reviewBreakdown": {
    "Initial impression": 5,
    "Punctuality": 4.75,
    "Cleanliness": 4.75,
    "Quality": 4.75,
    "Value": 4.25,
    "Overall opinion": 4.5
  },
  "vettingChecks": [
    "Identification checked",
    "Company address verified",
    "Customer references checked",
    "Agreed to TrustATrader's high standards",
    "Agreed to complaints procedure"
  ],
  "companyType": "Limited company",
  "incorporatedOn": "Incorporated as from 15th December 2021",
  "verifiedOn": "Verified on 31st January 2022",
  "insurer": "Simply Business Insurance",
  "insuranceVerifiedOn": "21st July 2025",
  "qualifications": ["Gas Safe Registered."],
  "associations": ["TrustATrader.com", "Gas Safe Register"],
  "profileUrl": "https://www.trustatrader.com/traders/0800-homefix-ltd-plumbers-wokingham"
}
```

### Integrations & Automation

- **Apify API** - run via `POST https://api.apify.com/v2/acts/{actorId}/runs`, export via `GET /v2/datasets/{datasetId}/items?clean=true&format=json`
- **Schedules** - Apify Schedules for weekly trader-list refreshes (recommended for lead-gen databases; recurring usage improves Store recommendation signals)
- **Webhooks** - new or updated trader records into a CRM or lead pipeline
- **Zapier / Make** - via the Apify integration (e.g. new traders to Airtable/Sheets)
- **Pay-per-event** - $2.00 per 1,000 traders, no subscription, free 10-item preview

### Related Actors

- [checkatrade-scraper](https://apify.com/trev0n/checkatrade-scraper): the largest UK trades-directory scraper on the Store (Checkatrade), a reference point for the cluster.
- [checkatrade-scraper](https://apify.com/memo23/checkatrade-scraper): another Checkatrade scraper, for cross-directory coverage.
- [checkatrade-scraper](https://apify.com/ahmed_jasarevic/checkatrade-scraper): own UK trades-directory actor in the same cluster.
- [hipages-scraper](https://apify.com/ahmed_jasarevic/hipages-scraper): AU home-services marketplace data as a companion international source.
- [taskrabbit-scraper](https://apify.com/ahmed_jasarevic/taskrabbit-scraper): on-demand services marketplace data to widen the services cluster.

### FAQ

#### Is there a TrustATrader API?

No public API exists. The site is server-rendered HTML behind nginx + an AWS ALB, with all trader data present as schema.org microdata on the pages (confirmed during recon: no embedded JSON blob, no Algolia/Meilisearch/Elasticsearch backend, no internal JSON/GraphQL API). The only external API is a location typeahead with no trader data.

#### How do I scrape TrustATrader for leads?

Set `trade` (e.g. `plumbers`) + `location` (e.g. `london`) or paste category/trader URLs into `startUrls`, set `maxItems`, and run. Enable the proxy to avoid HTTP 429 rate limits. Turn off `fetchProfiles` for a fast, light listing-only scrape (name, trade, town, rating, review count, phone).

#### What are the alternatives for UK tradespeople data?

Store alternatives: trev0n/checkatrade-scraper and memo23/checkatrade-scraper (Checkatrade), plus scrapersdelight UK trades actors. Pair with own actors checkatrade-scraper and hipages-scraper for multi-directory coverage.

#### Does the actor return insurance and vetting data?

Yes - with `fetchProfiles: true` (default) each record includes `vettingChecks`, `insurer`, `insuranceVerifiedOn` (insurance verification date), `verifiedOn`, `companyType`, `incorporatedOn`, `qualifications` and `associations`, all parsed from the published profile.

#### Why do I get 429 errors?

The origin rate-limits datacenter IPs. This is an IP-level block, not a JS challenge - enable the Apify proxy (recommended for production runs) and keep `maxConcurrency` in the 5-20 range.

#### Which trades and areas are covered?

Any trade slug used in TrustATrader URLs (`plumbers`, `electricians`, `builders`, `roofers-roofing`, `heating-engineers-and-gas-fitters`, `painters-and-decorators`, ...) across any UK town, city or county. `startUrls` accept any category or profile URL directly.

#### How much does a run cost?

$2.00 per 1,000 trader records, pay-per-event, with a free 10-item preview. A typical 50-trader run with profiles costs ~$0.14 incl. the start event; `fetchProfiles: false` halves the request volume for listing-only datasets.

### SEO Keywords

trustatrader scraper, uk tradespeople directory, trade leads uk, plumbers leads, electricians leads, tradesman database, checkatrade scraper, vetted trades data, home services lead generation, uk trade insurance leads, gas safe engineers data, roofing leads uk, local business data uk, trader reviews data, plumbing business leads, trustatrader api, contractor data uk, tradespeople email list, building contractor leads, verified trader directory

### For AI Agents & LLM Apps

- **Purpose**: returns one JSON row per UK tradesperson from TrustATrader (name, trade, location + lat/lon, phone, website, rating/reviews, vetting checks, insurance and verification dates, qualifications).
- **Minimal working input**:

```json
{ "trade": "plumbers", "location": "london", "maxItems": 10, "fetchProfiles": false }
```

- **Variant input** (full profiles - only when `fetchProfiles: true`):

```json
{ "trade": "electricians", "location": "manchester", "maxItems": 50, "fetchProfiles": true, "proxy": { "useApifyProxy": true } }
```

- **Output field names**: `name, trade, town, region, postcode, latitude, longitude, phone, phones, website, rating, reviewCount, reviewBreakdown, vettingChecks, companyType, incorporatedOn, verifiedOn, insurer, insuranceVerifiedOn, ownerName, qualifications, associations, description, reviews, profileUrl, sourceUrl, scrapedAt`.
- **Behaviors an agent should know**:
  - `startUrls` take priority over `trade`/`location` when both are set - pass category or profile URLs directly, or let the actor build them from trade + location.
  - `fetchProfiles` (default true) roughly doubles the request volume (listing + profile pages) and is what unlocks `insurer`, `vettingChecks`, `qualifications`, lat/lon, website and `reviewBreakdown`. Turn it off for a fast listing-only run; those fields are then `null`.
  - `maxConcurrency` is both floor and ceiling - the run starts at full speed; the site rate-limits aggressive crawling (429), so 5-20 is the sensible range and 20 is the default.
  - `postcode` is `null` for traders found via paginated `/search` pages (the HTML does not publish it); `ownerName` is best-effort from profile text.
  - `reviewBreakdown` is averaged only over reviews present on the profile page at scrape time.
- **Billing**: pay-per-event - $0.002 per trader row, $0.00005 per run start. Proxy usage billed separately when enabled.

### Legal & Compliance Disclaimer

This actor is an independent tool and is not affiliated with, endorsed by, or sponsored by TrustATrader (trading of the UK company behind trustatrader.com). It accesses only publicly available directory pages; it does not bypass login, authentication or paywalls, and does not solve CAPTCHAs.

Users are responsible for complying with TrustATrader's terms of service, robots.txt and applicable data-protection law (UK GDPR and the Privacy and Electronic Communications Regulations apply to trader contact data). The output includes phone numbers intended for legitimate business contact; do not use them for unsolicited commercial outreach in violation of applicable law.

# Actor input Schema

## `startUrls` (type: `array`):

Optional. TrustATrader category/search URLs (e.g. https://www.trustatrader.com/plumbers-in-london) or trader profile URLs (https://www.trustatrader.com/traders/{slug}). If provided, these are used instead of the Trade + Location fields.

## `trade` (type: `string`):

Trade category slug as used in TrustATrader URLs, e.g. "plumbers", "electricians", "builders", "roofers-roofing", "heating-engineers-and-gas-fitters", "painters-and-decorators". Combined with Location to build a category URL.

## `location` (type: `string`):

UK town, city or county, e.g. "london", "manchester", "birmingham", "kent". Combined with Trade to build a category URL.

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

Maximum number of trader records to return. Free users are capped at 10.

## `fetchProfiles` (type: `boolean`):

If enabled, each trader's profile page is fetched for the full record (insurance details, vetting checks, qualifications, lat/lon, website, review breakdown). Disable for a faster, lighter listing-only scrape.

## `maxPagesPerStart` (type: `integer`):

Minimum number of result pages (10 traders each) to crawl per start URL. Pagination follows the "View more" / "Next" link and continues past this number when more pages are still needed to reach Max traders.

## `maxConcurrency` (type: `integer`):

How many pages to fetch in parallel. Listing pages and trader profiles all crawl concurrently, and this value is used as both the floor and the ceiling, so the run starts at full speed instead of ramping up. Higher values finish faster (the site rate-limits aggressive crawling â€” 5-20 is a sensible range).

## `proxy` (type: `object`):

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "startUrls": [],
  "trade": "plumbers",
  "location": "london",
  "maxItems": 50,
  "fetchProfiles": true,
  "maxPagesPerStart": 5,
  "maxConcurrency": 20,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [],
    "trade": "plumbers",
    "location": "london",
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/trustatrader-scraper").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 = {
    "startUrls": [],
    "trade": "plumbers",
    "location": "london",
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/trustatrader-scraper").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 '{
  "startUrls": [],
  "trade": "plumbers",
  "location": "london",
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call ahmed_jasarevic/trustatrader-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/trustatrader-scraper"
        }
    }
}
```

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/gtoKEHyZVmDfxHJaI/builds/WshCO05MwCNsFipdf/openapi.json
