# TrustATrader Scraper — Vetted UK Trades Leads & Phones (`scrapersdelight/trustatrader-uk-scraper`) Actor

Scrape vetted UK tradespeople from TrustATrader: name, trade, area, phone, website, rating, reviews, owner name, vetting checks, insurer and insurance verification date. Pick from 1,379 real trades and 100 UK areas. Promoted adverts are deduped before billing — never pay for a repeat.

- **URL**: https://apify.com/scrapersdelight/trustatrader-uk-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Automation, Lead generation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 per trader 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/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

## TrustATrader Scraper — Vetted UK Trades Leads & Phones

Turn [TrustATrader](https://www.trustatrader.com) into a clean UK tradesperson lead list. One row per
trader advert, carrying **business name, trade, area covered, phone, website, town, latitude/longitude,
rating, review count, per-category rating breakdown, missed appointments, membership number, years as a
member, business type, owner name, the five vetting checks, insurance provider, insurance verification
date, qualifications and trade associations**. Filter by trade, area, phone, website, rating, review
count and membership tenure — or paste listing and profile URLs.

**No login. No cookies. No CAPTCHA solving.**

**30,972 live trader adverts. 1,557 trades. 100 UK areas. 156,500 trade × area listing pages** — every
one of those numbers read live off TrustATrader's own sitemaps on 2026-08-12, not quoted from a
marketing page. (156,500 is a clean cross-product: 1,565 trades × 100 areas. Eight trades carry
listing pages without appearing in the trade sitemap.)

The wedge, measured on a 200-advert contiguous sample the same day: **100% carry an insurance provider
and the date TrustATrader last verified that insurance**, and **100% carry a missed-appointment count
for the last six months**. Google Maps has neither.

```json
{
  "source": "search",
  "trades": ["plumbers"],
  "locations": ["london"],
  "maxItems": 30
}
```

Click **Try for free** and hit **Start** — that is the input the Actor ships prefilled with (plus a page
depth of 10 and the residential-GB proxy, both already set). It returned **30 rows in 2 minutes
34 seconds** on 2026-08-12, with nothing edited.

***

### The wedge: the vetting paperwork, not another star rating

Every other UK trades scraper — ours included — gives you a name, a phone and a rating. TrustATrader is
a *vetted* directory, and the vetting file prints on the public profile. That file is the product.

Measured on **200 trader adverts, contiguous sitemap walk, 2026-08-12**:

| Field only TrustATrader publishes | Fill | What it is worth |
|---|---|---|
| `insuranceProvider` + `insuranceVerifiedOn` | **100%** | The firm's insurer and the date TrustATrader last checked the policy. A renewal calendar for a trade-insurance broker. 61 distinct insurer strings in 200 rows, and the big names are split across several of them: Simply Business 45 rows over three spellings, Hiscox 21 over three, Aviva 16 over two, Direct Line 15 over two, QBE 13 over four. |
| `missedAppointments` | **100%** | Missed appointments in the last 6 months. 8 of 200 traders had one or more; 192 had zero. |
| `vettingChecks` + `vettingVerifiedOn` | **100%** | The five checks TrustATrader ran (ID, company address, customer references, standards agreement, complaints procedure) and the date they were run. |
| `businessType` + `ownerName` | 100% / **71.5%** | Legal form, and where TrustATrader prints "Owned by …", a named decision-maker. In the sample: 143 "Owned by …", 39 limited companies, 18 sole traders. |
| `membershipNumber` | **100%** | The business-level ID. It is what lets this Actor collapse a firm's many adverts into one row — see below. |

None of those five are on a Google Maps pull, and none are on the listing card — they exist only on the
profile page, which is why this Actor fetches it.

***

### Read this before you buy rows

Five things that will otherwise surprise you. All measured, all facts about TrustATrader rather than
about this Actor.

1. **A TrustATrader "profile" is an advert, not a business.** Adverts are per trade *and* per service
   area, so one firm holds many. In a contiguous 100-advert sitemap window at offset 15,000, **100
   adverts belonged to just 25 distinct businesses — a 75% collapse**; one firm held 16 of them. A
   second window at offset 0 was far tamer: 100 adverts, 90 businesses, 10% collapse. Across both,
   **200 adverts = 115 businesses.** Turn on **One row per business** if you want companies, not
   adverts; leave it off if you want the trade and area dimensions. Either way you are billed for what
   you receive — collapsed rows are dropped before billing.
2. **Three promoted adverts are pinned to the top of every listing page.** They are the *same three
   traders* on page 2, page 3 and page 27. This Actor drops them at enumeration time, before anything
   is fetched or billed — see the uniqueness section.
3. **There is no street address and no postcode.** TrustATrader publishes the town and a map pin only;
   `itemprop="streetAddress"` and `"postalCode"` appear on **0 of 109** live profiles sampled. Rather
   than ship two permanently empty columns you get `addressLocality` plus `latitude`/`longitude`
   (100% / 100%).
4. **There are no e-mail addresses.** TrustATrader routes contact through its own form. Nothing in this
   Actor can produce one, and it will not invent one.
5. **"Years trading" is not published.** The only tenure signal on the page is the *"N year member"*
   badge, so `membershipYears` means **years as a TrustATrader member**, not years in business.

***

### What you get — one row per trader advert

All dates that TrustATrader prints as prose (`vettingVerifiedOn`, `insuranceVerifiedOn`) are kept
verbatim as published, e.g. `"7th April 2026"`. `scrapedAt` is ISO-8601 UTC. `rating` is a number out
of 5, never a percentage. `phone` is E.164; `phoneDisplay` is the printed form.

| Group | Fields |
|---|---|
| Identity | `traderName` · `profileUrl` · `traderSlug` · `traderId` · `membershipNumber` |
| Trade & area | `trade` · `tradeSlug` · `areaCovered` · `locationSlug` · `listingUrl` |
| Location | `addressLocality` · `latitude` · `longitude` |
| Contact | `phone` · `phoneDisplay` · `phones[]` · `website` |
| Ratings | `rating` · `reviewCount` · `ratingBreakdown{}` · `missedAppointments` |
| Vetting | `vettedAndApproved` · `vettingChecks[]` · `vettingVerifiedOn` · `businessType` · `ownerName` · `membershipYears` · `guaranteeEligible` |
| Insurance | `insuranceProvider` · `insuranceVerifiedOn` |
| Credentials | `qualifications[]` · `associations[]` |
| Content | `shortDescription` · `description` · `logo` · `galleryImageCount` |
| Provenance | `scrapedAt` |

***

### Field fill — two measured samples

Fill differs by how you reach the rows, so both samples are published. **Plan against the sitemap
sample** if you are buying volume; the search sample is what a trade + area run looks like.

#### A. 200 trader adverts, contiguous sitemap walk, 2026-08-12 (67 trades, 93 towns)

| Field | Fill |
|---|---|
| `traderName` · `trade` · `membershipNumber` · `businessType` | 100% |
| `rating` · `reviewCount` · `ratingBreakdown` · `missedAppointments` | 100% |
| `addressLocality` · `latitude` · `longitude` | 100% |
| `vettingChecks` · `vettingVerifiedOn` · `vettedAndApproved` | 100% |
| **`insuranceProvider` · `insuranceVerifiedOn`** | **100%** |
| `membershipYears` | 100% |
| `description` · `shortDescription` · `galleryImageCount` · `guaranteeEligible` | 100% |
| `areaCovered` | 99.5% |
| **`phone` · `phoneDisplay` · `phones`** | **99.5%** (1 of 200 had none) |
| `tradeSlug` · `locationSlug` · `listingUrl` | 99.0% |
| `logo` | 98.0% |
| **`website`** | **83.5%** |
| **`ownerName`** | **71.5%** |
| `qualifications` | 70.0% |
| `associations` | 66.5% |

**`0` and `false` count as filled, because they are real answers.** `missedAppointments: 0` means
TrustATrader recorded none (192 of the 200), and `galleryImageCount: 0` means the trader uploaded no
photos (69 of the 230 rows scraped that day). Neither is a `null`, and neither is a parse failure.
Where a value is genuinely absent — `website`, `ownerName`, `qualifications` — the field is `null` or an
empty array, and that is what the percentages above measure.

#### B. 196 live profiles, random national sample, 2026-08-12 (79 trades, 139 towns)

| Field | Fill |
|---|---|
| `traderName` · `trade` · `rating` · `reviewCount` · `ratingBreakdown` | 100% |
| `phone` · `phoneDisplay` | 100% |
| `missedAppointments` · `addressLocality` · `latitude` · `longitude` | 100% |
| `membershipNumber` · `businessType` · `vettingChecks` · `vettingVerifiedOn` | 100% |
| `description` · `shortDescription` | 100% |
| `areaCovered` | 99.0% |
| `insuranceProvider` · `insuranceVerifiedOn` | 98.5% |
| `logo` | 93.9% |
| `tradeSlug` · `locationSlug` · `listingUrl` | 94.4% |
| **`membershipYears`** | **76.0%** |
| **`website`** | **74.0%** |
| **`ownerName`** | **59.7%** |
| `qualifications` | 41.3% |
| `associations` | 35.2% |

**The headline that could mislead you: this is not a 100%-website product, and the two samples disagree
about how far off it is** — 83.5% on the sitemap walk, 74.0% on the national random sample. Budget
against the lower one. `ownerName` (59.7–71.5%) and `qualifications` (41.3–70.0%) swing the same way,
because they depend on which trades and which membership vintages a given slice happens to hit.

Every run prints its own measured fill in the log, so you never have to trust this table for the rows
you actually bought.

***

### How to run it

#### 1. Search — trade + area (the usual choice)

```json
{
  "source": "search",
  "trades": ["plumbers", "electricians"],
  "locations": ["london", "manchester"],
  "maxItems": 200
}
```

Both fields are **pickers, not free-text boxes**. The trade list carries **1,379 real TrustATrader trade
slugs with TrustATrader's own published names**, so `heating-engineers-gas-fitters` and
`fascias-soffits-guttering` are things you click, not strings you have to guess. The area list carries
the **100 areas TrustATrader lists in its own sitemaps**. Every trade is crossed with every area, so
2 × 2 = 4 listings.

Seven of the 100 areas are counties or regions rather than towns — `essex`, `kent`, `hertfordshire`,
`lancashire`, `derbyshire`, `oxfordshire`, `scotland` — and they cover far more traders than a single
town. They are the cheapest way to sweep a region.

Those 100 are the areas TrustATrader *promotes*, not every area that works. Smaller towns resolve fine
without appearing in the sitemaps — `Bishop's Stortford` typed into **Extra areas** returned rows on
2026-08-12. That is what the free-text boxes are for.

#### 2. Sitemap — the whole UK directory

```json
{ "source": "sitemap", "sitemapTrades": ["roofers-roofing"], "maxItems": 1000 }
```

Reads TrustATrader's own trader sitemap: **30,972 advert URLs** (30,932 distinct — TrustATrader itself
lists 40 twice), optionally filtered by trade slug. The trade filter is applied to the sitemap XML
*before* any profile page is fetched, so narrowing here is free. Leave `sitemapTrades` empty to walk the
entire directory, and use `skip` to page through it across separate runs without re-buying rows.

#### 3. Profile URLs — specific traders

```json
{
  "source": "profileUrls",
  "profileUrls": ["https://www.trustatrader.com/traders/ace-rapid-plumbing-plumbers-southwark"]
}
```

A non-empty `profileUrls` list **takes over the run** whatever `source` says: only those profiles are
fetched, and no listing or sitemap is walked. Run a search and a named list as two runs, not one.

#### 4. Listing URLs — anything the pickers do not cover

```json
{ "startUrls": [{ "url": "https://www.trustatrader.com/electricians-in-manchester" }] }
```

Page numbers are appended for you; do not include `?page=N`. This, plus the free-text **Extra trades**
and **Extra areas** fields, is how you reach the 178 trades TrustATrader publishes no display name for.

All four routes produce **identical rows** — same fields, same shape, same price.

#### The cheap dial

There is no listing-only mode, because the listing card carries none of the fields this Actor exists
for — no phone, no insurer, no vetting file, no geo. Every row costs exactly one profile fetch. Your
real cost dials are `maxItems` (hard cap) and `sitemapTrades` (free pre-filtering).

#### If a run runs out of time or budget

The Actor watches its own run-time limit and its charge cap, and stops **cleanly** at either — it
pushes everything it has already collected, ends **Succeeded**, and writes a status message saying how
many traders it returned and what to raise. It never ends a run with an error because TrustATrader
was slow, was answering HTTP 429, or because your filters removed everything; those all finish
Succeeded with a status message explaining what happened, and nothing is charged for rows you did not
receive. The only conditions that hard-fail a run are the three that mean the data itself cannot be
trusted: the trader sitemap coming back materially short, more than a fifth of fetched pages having
unparseable markup, and the run-wide uniqueness assertion failing.

***

### Sample row

One real row, captured on the run of 2026-08-12. Only the `description` is abbreviated (it is 1,900
characters of the trader's own copy); every other value is exactly as delivered.

```jsonc
{
  "traderName": "Greensted Plumbing & Heating",
  "profileUrl": "https://www.trustatrader.com/traders/greensted-property-development-24-hour-emergency-services-medway",
  "traderSlug": "greensted-property-development-24-hour-emergency-services-medway",
  "traderId": "5b1a6be98811d3750a0d4815",
  "membershipNumber": "TT9935",

  "trade": "24 Hour Emergency Services",
  "tradeSlug": "24-hour-emergency-services",
  "areaCovered": "the majority of Kent and surrounding areas.",
  "locationSlug": "gillingham",
  "listingUrl": "https://www.trustatrader.com/24-hour-emergency-services-in-gillingham",

  "addressLocality": "Gillingham",
  "latitude": 51.3466,
  "longitude": 0.56988,

  "phone": "+447308500815",
  "phoneDisplay": "07308 500815",
  "phones": ["+447308500815"],
  "website": "https://www.greenstedplumbing.co.uk/",

  "rating": 4.96,
  "reviewCount": 649,
  "ratingBreakdown": {
    "Initial impression": 4.96, "Punctuality": 4.92, "Cleanliness": 4.96,
    "Quality": 4.98, "Value": 4.94, "Overall opinion": 4.98
  },
  "missedAppointments": 0,

  "membershipYears": 10,
  "businessType": "Owned by Stephen Greensted",
  "ownerName": "Stephen Greensted",
  "vettedAndApproved": true,
  "vettingChecks": [
    "Identification checked",
    "Company address verified",
    "Customer references checked",
    "Agreed to TrustATrader's high standards",
    "Agreed to complaints procedure"
  ],
  "vettingVerifiedOn": "26th May 2016",

  "insuranceProvider": "SB-Simply Business",
  "insuranceVerifiedOn": "1st September 2025",
  "qualifications": [
    "Gas Safe Registered",
    "LCL Awards Certificate of Competence - Domestic Electrical Installer",
    "LCL Awards Level 3 Certificate - Initial Verification & Certification Of Electrical Installations",
    "LCL Awards Certificate of Competence",
    "City & Guilds - Certificate - Understanding The Requirements Of Electrical Installations BS767:2018"
  ],
  "associations": ["Oftec", "Gas Safe Register"],

  "guaranteeEligible": true,
  "shortDescription": "24 Hour Emergency Services. Greensted Plumbing & Heating has been vetted and approved by TrustATrader. Covering the majority of Kent and surrounding areas.",
  "description": "Welcome to Greensted Plumbing & Heating. We are a highly professional and well-established family company with more than 30 years' of combined experience …",
  "logo": "https://assets.trustatrader.com/greensted-property-group-logo---nov-2018.zaa75o.view.m3q.jpg",
  "galleryImageCount": 28,
  "scrapedAt": "2026-08-13T01:51:58.889Z"
}
```

Fields people misread — every one of these is visible in the row above:

- **`traderName` and `traderSlug` disagree**, and that is normal. This firm trades as "Greensted
  Plumbing & Heating"; its TrustATrader URL still says `greensted-property-development`. Key on
  `membershipNumber` or `profileUrl`, never on the slug's business part.
- `rating` is **out of 5**, not out of 10 and not a percentage. So are all six `ratingBreakdown`
  sub-scores. Median 4.95 across the 200-advert sample; the lowest was 4.6.
- `missedAppointments: 0` means "TrustATrader recorded none in six months" — a real, good value, not a
  missing one. Only 8 of 200 sampled traders had any.
- `ownerName` is filled here because `businessType` reads `"Owned by …"`. On a `"Limited company"` or
  `"Sole trader"` row it is `null`, meaning TrustATrader named no individual — not that extraction
  failed.
- `membershipYears: 10` is ten years as a **TrustATrader member**. TrustATrader publishes no founding
  year for anybody.
- **`insuranceProvider` is free text, not a code.** 200 rows produced 61 distinct strings, and Simply
  Business alone appears as `Simply Business`, `Simply Business Insurance` and `SB-Simply Business`.
  Normalise before you group by insurer.
- **`locationSlug` is the profile's own breadcrumb area, and its vocabulary is wider than the area
  picker.** `gillingham` is not one of the 100 areas with trade × location listing pages. Of 198 rows
  carrying a `locationSlug`, 103 fell inside the picker's 100 and the rest used 56 other slugs.
- `galleryImageCount` counts gallery tiles rendered on the page. 69 of 230 rows scraped that day had
  zero, and 7 sat at exactly `1000`, which looks like a page-side cap rather than a true count — treat
  large values as "lots", not as an exact figure.

***

### Input

Twenty-two fields, grouped into five sections in the Console. Every one carries a default, and the six
that make up a real demo run are prefilled, so **Start works with zero edits**.

| Field | Type | Default | What it does |
|---|---|---|---|
| **🔎 What to scrape** ||||
| `source` | select | `search` | Search listings · whole-directory sitemap · pasted profile URLs. All three cost the same per row. |
| `trades` | multi-select | `["plumbers"]` | Picker of **1,379 real trade slugs with TrustATrader's published names**. Crossed with every area. SEARCH only. |
| `locations` | multi-select | `["london"]` | Picker of **all 100 TrustATrader area slugs**. SEARCH only. Seven are counties/regions. |
| **🔗 Off-list trades, areas and URLs** ||||
| `extraTrades` | stringList | `[]` | Free text for the 178 trades with no published name. Slugified for you (`Painters & Decorators` → `painters-decorators`). Merged with the picker. |
| `extraLocations` | stringList | `[]` | Free text for an area added after this build. Slugified. If it is not live, the run errors rather than returning national results. |
| `startUrls` | requestListSources | `[]` | Paste listing pages. Page numbers appended automatically — omit `?page=N`. |
| `profileUrls` | stringList | `[]` | Paste trader profile pages. **Anything here overrides `source`:** if this list is not empty the run scrapes only these URLs and no listing or sitemap is walked. One fetch, one charge each. |
| `sitemapTrades` | stringList | `[]` | SITEMAP only. Filters the sitemap XML before any fetch — **free**, costs you nothing. |
| **🎯 Lead-quality filters** ||||
| `onlyWithPhone` | checkbox | `false` | Phone is 99.5–100% measured, so this normally removes nothing. |
| `onlyWithWebsite` | checkbox | `false` | Discards 16.5–26% of rows. The *inverse* is a product: traders with no website are the agency target list. |
| `minRating` | select | `Any` | Weak here — 0 of 200 and 0 of 196 sampled traders scored below 4.5. |
| `minReviewCount` | number | `0` | The filter that actually bites. Median review count **34** on the 196-profile national sample and **71** on the 200-advert sitemap walk; range 7 → 1,606. |
| `minMembershipYears` | number | `0` | Membership tenure, **not** years trading. Any value above 0 drops every trader with no badge — 24% of the national random sample, 0% of the sitemap sample. |
| `dedupeByBusiness` | checkbox | `false` | Collapse a firm's many adverts onto `membershipNumber`. Measured collapse ranged 10% to 75% by directory window. |
| **💷 Depth, limits and cost** ||||
| `maxItems` | number | `30` | **Your hard cost cap.** 30 = $0.12, 1,000 = $4.00. Raise to 500–5,000 for production. |
| `maxPagesPerSearch` | number | `10` | ~10 new traders per page. Walks stop early when a page returns nothing new, so a high value is free on a short listing. |
| `skip` | number | `0` | Resume across runs without re-buying rows. |
| `includeDescription` | checkbox | `true` | The trader's own "About" text (100% fill, longest column). Free either way. |
| `includeReviewsMeta` | checkbox | `true` | Rating breakdown + missed appointments. Free either way. |
| **⚙️ Advanced** ||||
| `maxConcurrency` | number | `2` | 2 is deliberate — robots.txt declares `Crawl-delay: 5`. Max 5. |
| `requestDelayMs` | number | `1500` | ~1.3 req/s at concurrency 2. Set 1 + 5000 to honour the crawl-delay literally. |
| `proxyConfiguration` | proxy | RESIDENTIAL, GB | The rung that has never failed (233/233). Datacenter answered 200 on 18 of 20 fetches on 2026-08-13 and 0 the day before — cheaper, but unstable. |

Nothing is required. There is a `dedupeByBusiness`-shaped decision and a `maxItems`-shaped decision;
everything else has a defensible default.

***

### Pricing

**$0.004 per trader returned — $4.00 per 1,000.** Charged on the `trader-scraped` event. No monthly
platform fee from this Actor.

Rows are charged **as they are pushed** (`Actor.pushData(items, 'trader-scraped')`), so if you hit a
budget cap you get whole rows and stop, never a half-billed dataset.

You are **not** charged for:

- a promoted-advert repeat — duplicates are dropped at enumeration, before the profile is even fetched;
- a trader removed by your filters, even though the Actor paid the fetch to evaluate it;
- an advert collapsed by **One row per business**;
- a retry, a proxy request, or a listing/sitemap page. Only delivered trader rows bill.

| Run | Traders | Cost |
|---|---|---|
| The shipped default (Plumbers in London) | 30 | **$0.12** |
| One trade in one big city, walked out | 264 | **$1.06** |
| A working lead list | 1,000 | **$4.00** |
| Every advert in the directory | 30,932 (the sitemap's 30,972 URLs less the 40 it repeats) | **$123.73** |

`maxItems` is the hard cost cap (default 30). Duplicates and filtered rows never count against it.

***

### Honest limits

- **No e-mail addresses. None, for anybody.** TrustATrader routes contact through its own form and
  publishes no address. There is no `email` field and nothing in this Actor can produce one. You get
  the phone (99.5%), the website (83.5% / 74.0%) and the owner's name (71.5% / 59.7%) — enough to find
  one with a separate enrichment step, which this Actor does not perform.
- **No street address, no postcode.** `itemprop="streetAddress"` and `"postalCode"` appear on **0 of
  109** live profiles sampled. That is a TrustATrader publishing decision, not a scraping failure. You
  get the town and a map pin instead, both at 100%.
- **One firm can be many rows.** Measured collapse on contiguous 100-advert sitemap windows: **10% at
  offset 0, 75% at offset 15,000.** If you need companies rather than adverts, `dedupeByBusiness` is
  not optional — and if you need a *predictable* company count, buy more rows than you think you need.
- **Ratings are compressed at the top and are close to useless as a filter.** Median 4.95, minimum 4.6,
  and **0 of 200** sampled traders below 4.5 (0 of 196 on the earlier sample too). Vetted traders who
  slip get removed. Use `minReviewCount` instead — median 34 reviews on the national sample, 71 on the
  sitemap walk, range 7 to 1,606.
- **`membershipYears` is not years trading**, and on the national random sample **24% of traders carry
  no badge at all** (members under a year). Any `minMembershipYears` above 0 discards them.
- **The website field is the one that moves.** 83.5% on the sitemap walk versus 74.0% on the national
  random sample. Budget against 74.0%.
- **The insurer name is free text.** 200 rows produced **61 distinct `insuranceProvider` strings**, with
  Simply Business appearing under three of them. The field is 100% filled and genuinely useful, but you
  must normalise it before you group or dedupe by insurer. This Actor delivers what TrustATrader
  printed rather than guessing at a canonical name.
- **This is a small directory and a small lane.** The 30,972 adverts group into roughly **9,000
  distinct businesses** — that is every profile slug in the trader sitemap grouped into advert families
  (3.3 adverts per business; the grouping was checked against `membershipNumber` on 200 scraped rows and
  never merged two different firms). Either way it is a fraction of Checkatrade's coverage. If you want national UK trades coverage, this Actor is one input,
  not the whole answer — see the sibling table below.
- **It does not log in, and it collects no data behind any authentication.** No CAPTCHA solving, no
  challenge-bypass service. TrustATrader's `robots.txt` disallows `/api/`, `/json/`, `/consumers/`,
  `/promoters/` and the trader sign-in paths, and none of them is touched.

***

### How it works, and the transport ladder

TrustATrader server-renders everything. There is no JSON-LD, no `__NEXT_DATA__` and no XHR to
intercept: the record lives in **schema.org microdata** (`itemprop="…"`) plus a handful of BEM-classed
blocks (`profile-checks`, `profile-insurance`, `profile-rating`, `profile-qualifications`). The Actor
reads listing pages or the public sitemap to enumerate, then fetches `/traders/{slug}` and parses it.
No login, no cookie jar, no API key.

Two details worth knowing: the *"N year member"* badge is an SVG whose **filename** carries the number
(`…/badges/svg/year/6-year-member.<hash>.svg`), and the map pin is encoded in the **static map tile URL**
(`/static/atlas/-0.0535,51.4954,10/…`). Both are read rather than guessed.

**Transport ladder — measured through Apify, in this order:**

| Rung | Result |
|---|---|
| Direct, no proxy | **HTTP 429** nginx on every request (re-confirmed 2026-08-13) |
| Apify proxy, auto / datacenter | **18 / 20 = 90%** HTTP 200 on profile pages, all parseable (2026-08-13); the 2 misses were HTTP 403. It was 0/n when first measured on 2026-08-12, so treat this rung as *unstable*, not free. |
| Apify RESIDENTIAL, country GB | **HTTP 200** — 233 / 233 requests on 2026-08-13 |
| RESIDENTIAL + GB, fresh session per attempt | the shipped default |

Datacenter IPs therefore mostly work today, and with the Actor's retry-on-a-new-session they would
probably carry a run — if you are cost-sensitive, that is a legitimate experiment. RESIDENTIAL + GB is
the default because it is the only rung that has never failed on us, and this Actor tells you the
measured numbers rather than pretending the cheaper rung is impossible.

**Sustained load, all through Apify RESIDENTIAL + GB:**

| Test | Result |
|---|---|
| 30 profile pages, concurrency 4 | **29 / 30 = 96.7%**, 1.63 req/s |
| 200 profile pages, concurrency 5 | **197 / 200 = 98.5%**, 1.99 req/s |
| Parse success on those 197 pages | **196 / 197 = 99.5%** |
| 284 profile pages across four runs, 2026-08-12, concurrency 2 | **284 / 284 = 100%**, 0 unparseable |
| 34 sitemap and trade-index requests, 2026-08-12 | **34 / 34 = 100%**, first attempt |

The handful of misses at higher concurrency were Apify proxy upstream errors, not TrustATrader blocks.

**Pacing.** `robots.txt` declares `Crawl-delay: 5`. Defaults are deliberately gentler than the measured
ceiling: concurrency 2 with a 1,500 ms per-worker delay, about 1.3 requests/second. Set `maxConcurrency`
to 1 and `requestDelayMs` to 5000 to honour the declared crawl-delay literally.

***

### Uniqueness and dedupe

TrustATrader pins **3 promoted adverts to the top of every page of a listing**. They are the same three
traders on page 2, page 3 and page 27. Measured on contiguous walks of `plumbers-in-london`:

| Contiguous walk | Trader links returned | Unique traders | Duplicates |
|---|---|---|---|
| Page 1 | 13 | 13 | **0%** |
| Pages 1–5 | 65 | 53 | **18.5%** |
| Pages 1–7 | 91 | 73 | **19.8%** |
| Pages 1–8 | 104 | 83 | **20.2%** |
| Steady state, pages 2+ | 13 per page | 10 per page | **23.1% per page** |
| Full walk to exhaustion (27 pages) | 344 | **263 unique** | listing ends at page 28 |

Re-measured page by page on 2026-08-13: every page returns 13 trader links, and from page 2 on exactly
3 of them are the pinned promoted adverts, so each page adds 10 new traders until the listing runs out
part-way through page 27. Page 1 carries no repeats, which is why a short walk shows a lower rate than
the steady state. Sparse sampling would have hidden this entirely.

A run that hits its `maxItems` cap stops enumerating mid-page, so its own log will report a *lower*
duplicate rate than the table above — the shipped 30-row default stops after 59 links and 50 unique
profiles (15.3%). That is the cap talking, not a different duplicate rate.

**The dedupe key is the trader profile URL, and dedupe happens at enumeration — before the profile is
fetched, and therefore before anything is billed.** So the promoted repeats cost you neither money nor
requests. On top of that, the Actor **asserts uniqueness across everything it pushed** before the run is
allowed to succeed; if that assertion ever fails, the run errors instead of handing you a dirty dataset.
Every run logs the exact duplicate count it dropped. All four runs on 2026-08-12 verified clean:
30/30, 50/50, 100/100 and 100/100 distinct profile URLs.

The same pinning is how the Actor knows a listing has ended: **the first page that returns only traders
it has already seen is the last page.**

Across runs, `skip` is the resume handle. Within a run, the sitemap route needs no dedupe at all —
0 repeats in both 100-advert windows — because TrustATrader's own sitemap is (nearly) clean; the 40
duplicated URLs it does contain are collapsed too.

Business-level dedupe is a separate switch (`dedupeByBusiness`, keyed on `membershipNumber`) because
adverts and businesses are genuinely different units here. See "Read this before you buy rows".

***

### When a run fails

This Actor fails loudly rather than handing back a quiet empty dataset. There is no legitimate zero-row
run.

- **0 rows** → throws, and names the cause: how many pages were fetched, how many failed on the network,
  how many were unparseable, and how many your own filters removed.
- **Bad trade slug** → HTTP 404 is reported together with the sitemap URL that lists every valid slug.
- **Bad area** → caught via the page's canonical URL. TrustATrader does *not* 404 an unknown area — it
  silently serves the national trade page — so the Actor compares the canonical and stops rather than
  handing you the wrong traders. (The area picker makes this nearly impossible to trigger.)
- **Markup change** → if more than 20% of HTTP 200 pages carry no `LocalBusiness` microdata, the run
  errors instead of shipping a degraded extraction.
- **Shrunken sitemap** → if the trader sitemap lists fewer than 15,000 profiles (the live figure is
  30,972), the run refuses to pass off a partial directory as the whole one.
- **Duplicate leak** → the uniqueness assertion fails the run.

***

### Who buys this

- **Trade-insurance and finance brokers** — `insuranceProvider` plus `insuranceVerifiedOn` on 100% of
  rows is a renewal calendar. Simply Business alone held 45 of 200 sampled policies.
- **Tool brands, materials merchants and trade suppliers** building a UK installer list segmented by
  trade and area, with `qualifications` (Gas Safe, NICEIC, F-Gas) as the segmentation key.
- **Web and marketing agencies selling to trades** — the **16.5–26% with no website** are a ready-made
  target list, and 59.7–71.5% of rows name the owner to address the pitch to.
- **Franchise and lead-gen networks recruiting vetted trades** — `vettingChecks`, `membershipYears` and
  `missedAppointments` are a pre-built screening filter.
- **UK home-services market researchers** — 67 trades × 93 towns in a single 200-row sample; coverage
  and review depth by trade and area.

***

### Sibling Actors — which one to buy

| Actor | What it is | Why you would use it instead |
|---|---|---|
| [Checkatrade Scraper](https://apify.com/scrapersdelight/checkatrade-scraper) | Verified UK tradespeople by trade and town: name, phone, rating, review count, services and reviews | Much larger directory. Go here first for **coverage**. |
| [MyBuilder Scraper](https://apify.com/scrapersdelight/mybuilder-scraper) | Trade + city → ranked tradespeople with verified & featured badges, bio, services, sample reviews | When you want the **ranking** and badge signals rather than credentials. |
| [Rated People Scraper](https://apify.com/scrapersdelight/rated-people-scraper) | UK tradespeople by trade and town: rating, review count, reviews, service area, badges | A third independent rating source for cross-checking a firm. |
| [FreeIndex UK Scraper](https://apify.com/scrapersdelight/freeindex-uk-scraper) | Any FreeIndex category → name, phone, website, **full postal address**, geo, rating | FreeIndex is where you go when you need a **street address and postcode**, which TrustATrader never publishes. |

The honest one-liner: **Checkatrade is where you go for volume, FreeIndex is where you go for a postal
address, and TrustATrader is where you go for the insurance record and the vetting file.**

***

### FAQ

**Does this need an account, a login or an API key?**
No. Every page it reads is public and served without a session. It does not log in and it does not
solve CAPTCHAs.

**Does it need a proxy?**
Yes. A direct request gets HTTP 429, every time. Apify **RESIDENTIAL with country GB** is prefilled
because it has never failed us (233/233 requests on 2026-08-13). Apify's datacenter pool answered 200
on 18 of 20 profile fetches the same day but returned nothing but 403 the day before, so it is the
cheaper gamble, not the safe default. Change it only if you are watching the run log.

**Do I have to know TrustATrader's URL slugs?**
No. Trades and areas are pickers carrying TrustATrader's own published names — 1,379 trades and all
100 areas. Free-text boxes exist for the 178 trades TrustATrader publishes no display name for.

**Do I get e-mail addresses?**
No, and nobody can from this source — TrustATrader publishes none. You get phone at 99.5%, website at
83.5%/74.0% and owner name at 71.5%/59.7%.

**Why is there no street address?**
TrustATrader does not publish one. `streetAddress` and `postalCode` were absent on 0 of 109 sampled
profiles. You get the town plus latitude/longitude at 100%. For UK postal addresses use the FreeIndex
Actor above.

**Two runs — will I get duplicates?**
Within a run, never: dedupe is keyed on the profile URL and asserted before the run may succeed.
Across runs, use `skip` to resume where the last run stopped.

**Am I charged for rows that get filtered out, or for promoted repeats?**
No to both. Promoted repeats are dropped before the profile is even fetched, and filtered-out traders
are fetched at the Actor's cost, not yours. Only delivered rows bill, at $0.004 each.

**Can I get the whole catalogue in one run?**
Yes — `source: "sitemap"`, `sitemapTrades` empty, `maxItems: 30972`. You will receive 30,932 rows (the
40 URLs TrustATrader lists twice are collapsed and not billed) for about $123.73. Most buyers should
filter by trade first, or switch on `dedupeByBusiness` so they pay for companies rather than adverts.

**Is `minRating` worth setting?**
Almost never. 0 of 200 and 0 of 196 sampled traders scored below 4.5, because TrustATrader removes
vetted traders who slip. `minReviewCount` is the filter that separates busy firms from new ones.

**One company appears several times — is that a bug?**
No. TrustATrader adverts are per trade and per service area, so a firm holds one per combination. Turn
on **One row per business** to collapse them on `membershipNumber`. Measured collapse ranged from 10%
to 75% depending on where in the directory you look.

**Will a run ever succeed with zero rows?**
No. A zero-row run throws, and the error names the scope, the HTTP counts and how many rows your own
filters removed.

**Something looks wrong — how do I debug it?**
Read the run log. Every run prints the listings it walked, per-page link and duplicate counts, the total
duplicates dropped, its own measured field fill, and the uniqueness assertion result.

**How often should I re-run it?**
Insurance verification dates and review counts move monthly; the trader roster moves slowly. Save your
input as an Apify **Task**, then attach a **Schedule** (`0 6 1 * *` for monthly) and use `skip` if you
are walking the directory in slices.

***

### Legal and fair use

This Actor reads public TrustATrader pages and the sitemaps TrustATrader publishes in its own
`robots.txt`. It does **not** touch the paths that `robots.txt` disallows (`/api/`, `/json/`,
`/consumers/`, `/promoters/`, trader sign-in), it does not log in, and it collects nothing behind
authentication.

Records can contain **personal data** — sole-trader names, owner names and mobile numbers. You are
responsible for complying with TrustATrader's terms of service and with UK GDPR and PECR in how you
store the data and how you contact the people in it.

TrustATrader® is a trademark of its owner. This Actor is not affiliated with, endorsed by, or
sponsored by TrustATrader.

***

### Feedback

Found a missing field, or want a filter that is not here? Open an issue on the **Issues** tab. If the
Actor did what you needed, a review on the store page genuinely helps a small listing like this one.

# Actor input Schema

## `source` (type: `string`):

SEARCH walks the trade + location listing pages (e.g. plumbers-in-london) — the usual choice, and the one the two pickers below feed. SITEMAP takes the whole directory from TrustATrader's own trader sitemap (30,972 advert URLs), optionally filtered by trade. PROFILE URLS scrapes only the trader pages you paste in. All three produce identical rows at the same price.

## `trades` (type: `array`):

Pick one or more trades. Every option is a real TrustATrader trade slug carrying TrustATrader's own published name, so nothing here can be misspelt — 1,379 of the 1,557 trades in their trade sitemap have a published name and all 1,379 are listed. Each trade is crossed with each area below: 2 trades × 3 areas = 6 listings walked. Used only when Source is SEARCH.

## `locations` (type: `array`):

Pick one or more TrustATrader areas. These are the 100 areas TrustATrader lists in its own CategoryLocation01–04 sitemaps (156,500 pages = 1,565 trades × 100 areas, measured 2026-08-12) — the areas it actively promotes. Seven are counties or regions rather than towns (Essex, Kent, Hertfordshire, Lancashire, Derbyshire, Oxfordshire, Scotland) and cover far more traders than a single town. Smaller towns do resolve live without being in those sitemaps (bishops-stortford works) — use Extra areas below for those. An area TrustATrader does not cover at all is NOT a 404: the site quietly serves the national trade page instead, and this Actor stops with an error rather than handing you the wrong traders. Used only when Source is SEARCH.

## `extraTrades` (type: `array`):

Trade slugs the picker does not list — 178 of TrustATrader's 1,557 trades have no published name and so are not in the dropdown. Free text is slugified for you, so "Painters & Decorators" becomes painters-decorators. The full raw list is at https://www.trustatrader.com/sitemaps/TradeCategory.xml. Merged with the picker selections, then crossed with every area.

## `extraLocations` (type: `array`):

Area slugs the picker does not list. The picker holds the 100 areas TrustATrader promotes in its sitemaps, but smaller towns resolve live too. Free text is slugified, so "Bishop's Stortford" becomes bishops-stortford — that exact input returned rows on 2026-08-12. If the area is not live at all, TrustATrader serves the national trade page and this Actor stops with an error rather than handing you the wrong traders.

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

Paste TrustATrader listing pages directly instead of, or alongside, the pickers — e.g. https://www.trustatrader.com/electricians-in-manchester. Page numbers are added automatically; do not include ?page=N. This is the escape hatch for any trade × area combination, named or not.

## `profileUrls` (type: `array`):

Specific trader pages, e.g. https://www.trustatrader.com/traders/ace-rapid-plumbing-plumbers-southwark. Anything here OVERRIDES Source: if this list is not empty the run scrapes only these profiles and walks no listing or sitemap, so run a search and a named list as two separate runs. One fetch and one charge per URL.

## `sitemapTrades` (type: `array`):

Only used when Source is SITEMAP. Keeps sitemap adverts whose URL contains these trade slugs ("plumbers", "electricians", "roofers-roofing"). Leave empty to take the whole 30,972-advert directory. Filtering here is free — it happens on the sitemap XML, before any profile page is fetched or billed.

## `onlyWithPhone` (type: `boolean`):

Measured fill is 100% on a random national sample of 196 live profiles and 99.5% on a 200-advert sitemap walk (both 2026-08-12), so this normally removes nothing — 1 trader in 200 published no number.

## `onlyWithWebsite` (type: `boolean`):

Website fill is 74.0% on the 196-profile national sample and 83.5% on the 200-advert sitemap walk, so this discards 16.5-26% of rows. The inverse is a product in itself: traders with no website are the target list agencies buy this data for.

## `minRating` (type: `string`):

TrustATrader ratings are out of 5, and vetted traders who slip get removed, so the scale is compressed at the top: median 4.95-4.97, lowest seen 4.6, and 0 of 196 and 0 of 200 sampled traders scored below 4.5. This is a weak filter here — minimum reviews separates the busy from the new far better.

## `minReviewCount` (type: `integer`):

Social-proof floor, and the filter that actually bites on this site. Median review count is 34 on the 196-profile national sample and 71 on the 200-advert sitemap walk, where the range was 7 to 1,606. Newly vetted traders sit in single digits.

## `minMembershipYears` (type: `integer`):

TrustATrader publishes no founding year — the only tenure signal on the page is the "N year member" badge, and members under a year have none. Badge fill was 76.0% on a random national sample of 196 profiles but 100% on a 200-advert sitemap walk, so how much this discards depends entirely on the slice. It is membership tenure, NOT years in business.

## `dedupeByBusiness` (type: `boolean`):

TrustATrader adverts are per trade AND per service area, so one firm can hold many profile pages — 0800 Homefix Ltd alone holds 149 advert URLs in the trader sitemap. Turn this on to collapse them on membership number and get one row per company; leave it off to keep the trade and area dimensions. Collapsed rows are dropped before billing.

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

Stop after this many unique traders. This is your hard cost cap: 30 traders = $0.12, 1,000 = $4.00. The default 30 is a real smoke test that finishes in a couple of minutes; raise it to 500–5,000 for production.

## `maxPagesPerSearch` (type: `integer`):

How deep to walk each trade + area listing. 13 traders per page, of which 3 are promoted repeats from page 2 on, so a page adds about 10 new traders. Listings end naturally — plumbers-in-london runs out at page 27 with 264 unique traders — and the walk stops as soon as a page returns nothing new, so a high value costs nothing on a short listing.

## `skip` (type: `integer`):

Resume where the last run stopped: page further into a listing or the sitemap across separate runs without re-buying rows you already have.

## `includeDescription` (type: `boolean`):

The trader's own long "About" text (100% fill, and by far the longest column). Turn it off for a slimmer CSV. Costs nothing either way — it is already on the page that was fetched.

## `includeReviewsMeta` (type: `boolean`):

Per-category sub-scores (initial impression, punctuality, cleanliness, quality, value, overall opinion) and the count of missed appointments in the last 6 months. Both 100% filled. Free — same page, same price.

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

Kept low on purpose — TrustATrader is a small site and its robots.txt declares Crawl-delay: 5. Measured sustained load at concurrency 4 was 29/30 successful (96.7%) and at concurrency 5, 197/200 (98.5%). Set this to 1 together with a 5000 ms delay to honour the declared crawl-delay literally.

## `requestDelayMs` (type: `integer`):

Pause each worker waits after every request. The default 1500 ms at concurrency 2 works out at roughly 1.3 requests per second.

## `proxyConfiguration` (type: `object`):

REQUIRED, and the default is measured rather than guessed. A plain request gets HTTP 429 every time. Apify RESIDENTIAL with country GB has never failed us (233/233 requests on 2026-08-13) and a fresh session is minted per attempt so a retry lands on a different exit IP. Apify's datacenter pool answered HTTP 200 on 18 of 20 profile fetches on 2026-08-13 but 0 the day before — cheaper, but unstable. Leave this alone unless you are watching the run log.

## Actor input object example

```json
{
  "source": "search",
  "trades": [
    "plumbers"
  ],
  "locations": [
    "london"
  ],
  "extraTrades": [],
  "extraLocations": [],
  "startUrls": [],
  "profileUrls": [],
  "sitemapTrades": [],
  "onlyWithPhone": false,
  "onlyWithWebsite": false,
  "minRating": "0",
  "minReviewCount": 0,
  "minMembershipYears": 0,
  "dedupeByBusiness": false,
  "maxItems": 30,
  "maxPagesPerSearch": 10,
  "skip": 0,
  "includeDescription": true,
  "includeReviewsMeta": true,
  "maxConcurrency": 2,
  "requestDelayMs": 1500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per unique TrustATrader trader: name, trade, area covered, phone, website, rating, review count, membership years, vetting checks, insurance provider and verification dates, qualifications and geo. Promoted adverts pinned to every listing page are deduplicated run-wide, so no trader appears twice.

# 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 = {
    "source": "search",
    "trades": [
        "plumbers"
    ],
    "locations": [
        "london"
    ],
    "maxItems": 30,
    "maxPagesPerSearch": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/trustatrader-uk-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 = {
    "source": "search",
    "trades": ["plumbers"],
    "locations": ["london"],
    "maxItems": 30,
    "maxPagesPerSearch": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/trustatrader-uk-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 '{
  "source": "search",
  "trades": [
    "plumbers"
  ],
  "locations": [
    "london"
  ],
  "maxItems": 30,
  "maxPagesPerSearch": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call scrapersdelight/trustatrader-uk-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/trustatrader-uk-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/uJqpHNdqyjdTxS4xc/builds/7o3FgaRO1yNZKSzaV/openapi.json
