{
  "openapi": "3.0.1",
  "info": {
    "title": "Checkatrade Scraper — UK Tradespeople, Ratings & Reviews",
    "description": "Checkatrade scraper — search any trade + town, or paste profile/search URLs. One flat row per tradesperson: phone, locality, score out of 10, review counts, skills, badges, vetting and member-since. Opt-in full review history from Checkatrade's public API. Pure HTTP, no browser.",
    "version": "0.1",
    "x-build-id": "KPUS0P6sa7HudyTnF"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/memo23~checkatrade-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-memo23-checkatrade-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/memo23~checkatrade-scraper/runs": {
      "post": {
        "operationId": "runs-sync-memo23-checkatrade-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/memo23~checkatrade-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-memo23-checkatrade-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "trade": {
            "title": "Trade / category",
            "type": "string",
            "description": "The trade to search for. Any Checkatrade category works, e.g. `Plumber`, `Builder`, `Electrician`, `Gas Boiler Servicing Repair`. Combined with each location below into a `/Search/{Trade}/in/{Location}` URL, which is then paginated. Multi-word trades are Title-Cased and dash-joined automatically. No default — leave empty if you are pasting URLs instead. Example: `Plumber`."
          },
          "locations": {
            "title": "Locations",
            "type": "array",
            "description": "Towns, cities or areas to search in — any place Checkatrade supports, e.g. `London`, `Manchester`, `Birmingham`, `Milton Keynes`. Each entry is combined with the trade above into its own search and paginated. Multi-word places are Title-Cased and dash-joined automatically. Leave empty with a trade set to run one UK-wide search. Example: `[\"London\", \"Leeds\"]`.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "Search & profile URLs",
            "type": "array",
            "description": "Checkatrade URLs to scrape, auto-classified into two kinds: search listings (`https://www.checkatrade.com/Search/Builder/in/London`) which are paginated, and trade profiles (`https://www.checkatrade.com/trades/{slug}`) which each produce one fully detailed row. Mixable with the trade + locations above in the same run. Anything that is not a checkatrade.com search or profile URL is logged and skipped. Default: empty.",
            "items": {
              "type": "string"
            }
          },
          "scrapeDetails": {
            "title": "Scrape full profile pages",
            "type": "boolean",
            "description": "When true, each tradesperson found in a search also gets their profile page fetched, adding the full description, the complete skill/sub-skill list, services offered, per-dimension review metrics, up to 6 recent reviews and — where the tradesperson published one — their own website (present on roughly 1 profile in 3). Costs about 2× the requests. When false you still get the ~30 listing-card fields per tradesperson. Profile URLs you paste in startUrls are always fully detailed regardless. Default: false.",
            "default": false
          },
          "maxReviewsPerTrade": {
            "title": "Reviews per tradesperson (deep history)",
            "minimum": 0,
            "maximum": 1000,
            "type": "integer",
            "description": "How many customer reviews to collect per tradesperson. `0` (the default) = only the up-to-6 recent reviews embedded in the profile page, which needs 'Scrape full profile pages' above. Any value above 0 pulls that many from Checkatrade's public reviews API — full history with per-dimension scores (workmanship, reliability, communication), reviewer name, work category, price band, the trade's reply and review photos. The API needs only the company ID that listing rows already carry, so this works with profile scraping off and adds no profile fetches. Costs 1 request per 25 reviews (e.g. 400 reviews = 16 requests) but no extra charge. Example: `100`. Max 1000.",
            "default": 0
          },
          "enrichEmails": {
            "title": "Enrich with contact emails (experimental, low hit rate)",
            "type": "boolean",
            "description": "Opt-in, best-effort contact-email discovery. It seeds from the tradesperson's own website when their profile exposes one — measured live that is only 25–35% of profiles, and ~30% of those domains are dead, so the realistic seed rate is ~17%. In a 7-row test this returned 0 emails. Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object when something is found. Billed only on an actual find ($0.05 each); no email, no charge. Treat it as an occasional bonus, not a lead source. Default: false.",
            "default": false
          },
          "maxItems": {
            "title": "Max results (whole run)",
            "minimum": 1,
            "type": "integer",
            "description": "Hard cap on the number of tradesperson rows across the entire run, counting every search and URL. Each row is one paid `tradesperson` event. The run also trims itself down to whatever your maximum-cost-per-run setting can actually pay for. Free-tier Apify accounts are additionally capped at 100 rows. Example: `500`. Default: 1000.",
            "default": 1000
          },
          "maxItemsPerSearch": {
            "title": "Max tradespeople per search",
            "minimum": 1,
            "type": "integer",
            "description": "Cap on rows produced by each individual trade+location search, applied before the whole-run cap above. Checkatrade returns ~12 tradespeople per page and this actor follows at most 60 pages per search (~700 rows), so lower this to sample many towns evenly instead of exhausting the first one. Example: `50`. Default: 1000.",
            "default": 1000
          },
          "maxConcurrency": {
            "title": "Max parallel requests",
            "minimum": 1,
            "maximum": 15,
            "type": "integer",
            "description": "How many HTTP requests run in parallel. Checkatrade is protected by Cloudflare, so keep this moderate — 4 to 8 is the sweet spot; pushing higher gets more challenges, not more speed. Example: `6`. Default: 6. Max 15.",
            "default": 6
          },
          "maxRequestRetries": {
            "title": "Max request retries",
            "minimum": 0,
            "type": "integer",
            "description": "Retry budget per request for Cloudflare challenges (403), rate limits (429), 5xx and network errors. Each retry rotates to a fresh GB residential IP with a short backoff, which is what gets a blocked page through. The challenge is flaky, so a generous budget helps. Example: `15`. Default: 15.",
            "default": 15
          },
          "proxy": {
            "title": "Proxy configuration (optional override)",
            "type": "object",
            "description": "Leave this empty. The actor already routes every page request through its own GB residential pool at no extra cost to you — which is what gets past Checkatrade's Cloudflare challenge. Set it only if you must force your own proxies; a non-UK or datacenter pool will get challenged and return fewer rows. Default: unset (built-in GB residential)."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}