# GuildQuality Contractor Scraper (`crawlerbros/guildquality-scraper`) Actor

Scrape GuildQuality.com - verified customer-satisfaction data for North American home-improvement contractors. Browse by state/rating or pull full profile details, ratings, and verified customer reviews for any contractor.

- **URL**: https://apify.com/crawlerbros/guildquality-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## GuildQuality Contractor Scraper

Scrape **GuildQuality.com** — the leading independent customer-satisfaction survey platform for North American home-improvement and construction contractors (predominantly US, with some Canadian contractors also listed). Browse thousands of verified contractors by state and rating, or pull full profile details, verified ratings, and individual customer reviews for any contractor. No login, no cookies, no proxy required.

### What this actor does

- **Three modes:** `browse` (discover contractors, optionally filtered by state / city / rating / type of work / name), `profile` (full profile + reviews for specific contractors), `categories` (the site's reference trade-category list)
- **50,000+ contractors** discovered via GuildQuality's own public sitemaps — ~7,860 verified members with their own ratings/reviews, ~28,700 additional unclaimed/free directory listings, plus a third "additional" pool of thousands more companies only reachable via GuildQuality's per-project feedback-report sitemap (not listed in either of the other two), all selectable via `sitemapSource`
- **Filters:** US state, city, minimum average rating, minimum review count, minimum "would recommend" percentage, type of work, company name contains
- **Independently verified reviews** — GuildQuality reviews come from a third-party customer-satisfaction survey process, not self-submitted testimonials
- **Empty fields are omitted** — a field only appears in the output when GuildQuality actually disclosed that data

### Output: contractor profile (mode = `browse` or `profile`)

- `companySlug`, `companyName`, `memberId` — GuildQuality's internal numeric company ID (present on essentially every profile, verified or unclaimed)
- `profileUrl`, `logoUrl`
- `description` — the company's own "About" text
- `aggregateRating` (0–5 scale), `reviewCount`, `surveyCount`, `hasVerifiedReviews`
- `respondentCount` — how many past customers GuildQuality invited to review this company
- `recommendCount`, `recommendTotal`, `recommendPercentage` — how many of those respondents said they'd recommend the company
- `typesOfWork[]` — the company's self-declared project categories with project counts (e.g. `Remodeling & renovation`, `Roofers`, `New home`)
- `address`, `city`, `state`, `zipCode`
- `phone`, `website`
- `socialLinks[]` — external profiles the company has linked (Facebook, X, LinkedIn, Pinterest, blog, Better Business Bureau, Google Reviews, etc.), each with `platform` and `url`
- `teamMembers[]` — company-listed crew/staff, each with `name` and, when at least one peer endorsement exists, `endorsementCount`; `teamMemberCount` is the company's total crew size, which can exceed the number of members returned unless `includeExtendedCrew` is enabled
- `badges[]` — GuildQuality milestone achievements (e.g. "5 Consecutive Great Reviews", "Actively Surveying", "Guildmaster Award Winner"), each with `title`, `detail` (the year earned, or a short status note), and `iconUrl` (the badge's icon image)
- `projectLocationCount` — total completed projects GuildQuality has logged for this company (from its project map), which can exceed `reviewCount` since not every project receives a review
- `photos[]` — direct image URLs from the company's uploaded project photos (up to the base profile page's preview cap, or every photo when `includeExtendedPhotos` is enabled); `photoCount` is the company's total photo count, which can exceed the number of URLs returned unless `includeExtendedPhotos` is enabled
- `ratingBreakdown[]` — how many reviews fell at each star level, each with `stars` (1–5) and `count`; `unratedReviewCount` is respondent feedback left with no star rating
- `reviews[]` — each with `reviewId`, `ratingValue`, `reviewBody`, `reviewerName` (first name + last initial, or a location-based label when anonymous), `reviewerLocation`, `datePublished`, `isVerified`, `reviewUrl` (deep link to this review on the profile page)
- `serviceAreas[]` — de-duplicated, sorted `City, ST` locations where the company has completed a logged project, sourced from its project-location map (only present when `includeServiceAreas` is enabled and the company has at least one geocoded project — fetched for every profile regardless of whether its page visibly renders a Map section)
- `recordType: "contractor_profile"`, `scrapedAt`

### Output: category (mode = `categories`)

- `categorySlug`, `categoryTitle`
- `recordType: "category"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `browse` | `browse` / `profile` / `categories` |
| `state` | string (select) | – | Only emit contractors in this US state (mode=browse) |
| `cityContains` | string | – | Only emit contractors headquartered in a city containing this text (mode=browse) |
| `nameContains` | string | – | Only emit contractors whose name contains this text (mode=browse) |
| `typeOfWorkContains` | string | – | Only emit contractors whose declared project types contain this text (mode=browse) |
| `minRating` | number | – | Drop contractors rated below this (0–5, decimals allowed, e.g. 4.5) |
| `minReviewCount` | integer | – | Drop contractors with fewer reviews than this |
| `minRecommendPercentage` | number | – | Drop contractors recommended by fewer than this % of respondents (0–100, decimals allowed) |
| `sitemapSource` | string (select) | `verified` | `verified` / `unclaimed` / `all` / `additional` — which contractor pool to discover from (mode=browse); `additional` finds companies not listed in either of the other two pools, at the cost of one extra request per candidate |
| `maxProfilesScanned` | integer | `40` | How many profiles to fetch and check against filters before stopping (mode=browse) |
| `includeExtendedReviews` | boolean | `false` | Fetch up to 50 reviews per contractor instead of the default 25 (one extra request + crawl delay per contractor) |
| `includeExtendedCrew` | boolean | `false` | Fetch every crew member per contractor instead of the base page's ~3-member preview (one extra request + crawl delay per contractor, only when `teamMemberCount` shows more exist) |
| `includeExtendedPhotos` | boolean | `false` | Fetch every uploaded photo per contractor instead of the base page's capped preview (one extra request + crawl delay per contractor, only when `photoCount` shows more exist) |
| `includeServiceAreas` | boolean | `false` | Fetch the contractor's project-location map and add `serviceAreas` — up to 5 pages (~1000 projects) of extra requests per contractor |
| `profileUrlsOrSlugs` | array | – | Profile URLs or slugs to fetch (mode=profile) |
| `maxItems` | integer | `20` | Hard cap on emitted records (1–300) |

### Examples

#### Example: browse highly-rated Illinois contractors

```json
{
  "mode": "browse",
  "state": "IL",
  "minRating": 4,
  "minReviewCount": 50,
  "maxProfilesScanned": 200,
  "maxItems": 30
}
```

#### Example: browse recommended roofers in Austin, including unclaimed listings

```json
{
  "mode": "browse",
  "cityContains": "Austin",
  "typeOfWorkContains": "roof",
  "minRecommendPercentage": 90,
  "sitemapSource": "all",
  "maxProfilesScanned": 300,
  "maxItems": 20
}
```

#### Example: browse nationally, no filters

```json
{
  "mode": "browse",
  "maxItems": 20
}
```

#### Example: get full profile + reviews for specific contractors

```json
{
  "mode": "profile",
  "profileUrlsOrSlugs": [
    "https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric",
    "BandE-General-Contractors-Inc"
  ]
}
```

#### Example: reference trade-category list

```json
{
  "mode": "categories"
}
```

### Use cases

- **Lead generation** — build a list of verified, highly-rated contractors in a given state
- **Market research** — analyze review volume, rating distribution, and geographic concentration of contractors
- **Reputation monitoring** — pull a contractor's current independently-verified rating and recent reviews
- **Directory/aggregator sites** — feed structured, verified contractor data into your own listing
- **Competitive analysis** — compare review counts and ratings across competing contractors in the same market

### FAQ

**What is the data source?**
All data comes from the public pages of guildquality.com — GuildQuality's own sitemap of contractor profile pages, plus the profile pages themselves. No login or account is required to view this data.

**Is this affiliated with GuildQuality?**
No. This is an independent, third-party actor that reads GuildQuality's publicly accessible pages. It is not affiliated with or endorsed by GuildQuality.

**How is "browse" mode different from a keyword or category search?**
GuildQuality's own category+city search interface (`/find/...`) is disallowed for all crawlers in its `robots.txt`, so this actor never uses it. Instead, `browse` mode discovers contractors through GuildQuality's public sitemaps (`sitemap_company_profiles.xml` and, optionally, `sitemap_company_profiles_free.xml` — both explicitly listed as crawlable in `robots.txt`) and applies your filters — state, city, rating, review count, recommend percentage, type of work, name — after fetching each profile. `maxProfilesScanned` controls how many candidate profiles are checked before the run stops.

**What's the difference between "verified", "unclaimed", and "additional" contractors (`sitemapSource`)?**
GuildQuality publishes two separate sitemaps of contractor profiles with no URL overlap between them. "Verified" members have run GuildQuality's independent customer-satisfaction survey process and have their own `aggregateRating`, `reviewCount`, and `reviews[]`. "Unclaimed" listings are directory-only entries — real company name, address, phone, website, and description — and the great majority have no ratings or reviews of their own yet, though a small minority (companies that have since been claimed/surveyed but whose URL GuildQuality still also lists in the free sitemap) do show a rating. Default (`verified`) preserves the original review-focused behavior; set `sitemapSource` to `all` to also discover the ~28,700 unclaimed listings, or `unclaimed` to browse only those. "Additional" is a third, distinct pool: companies discoverable only through GuildQuality's per-project feedback-report pages (not listed in either sitemap above) — a mix of verified and unclaimed-style profiles, resolved via one extra request per candidate before the normal profile fetch.

**What is `typesOfWork`?**
Many contractor profiles list the categories of projects they've completed (e.g. "Remodeling & renovation", "Roofers", "New home") together with a project count for each, as declared on their own profile page. This is separate from GuildQuality's site-wide reference trade-category list (`mode=categories`) and is not available for every contractor.

**What are `respondentCount`, `recommendCount`/`recommendTotal`, and `recommendPercentage`?**
GuildQuality surveys every past customer of a member contractor. `respondentCount` is how many customers were invited to respond; `recommendCount` out of `recommendTotal` is how many of those respondents said they'd recommend the company, and `recommendPercentage` is that ratio as a percentage. These are only published once a contractor has at least one completed survey response.

**Why are reviews independently verified?**
GuildQuality's business model is built around a third-party customer-satisfaction survey: after a project, GuildQuality itself contacts the customer to collect the review, rather than the contractor collecting and posting it. This is disclosed on every review as "Verified."

**Why do some reviewer names look like "Review in City, ST" instead of a name?**
GuildQuality displays either the reviewer's first name + last initial, or — when the reviewer chose to stay anonymous — a location-based label instead. This actor passes through whichever GuildQuality discloses.

**Why are some fields missing on some records?**
GuildQuality only shows what's on a contractor's public profile page (address, phone, website are all optional fields contractors may leave blank). This actor never invents data — a field is simply omitted when the source page didn't include it.

**What are `socialLinks`, `teamMembers`, `badges`, and `projectLocationCount`?**
These are additional details some contractors publish on their profile page. `socialLinks` are external accounts the company has linked (Facebook, X, LinkedIn, BBB, Google Reviews, etc.). `teamMembers` lists company-listed crew with how many peer endorsements they've received (when any) — `teamMemberCount` is the company's total crew size, which can exceed the number of members returned unless `includeExtendedCrew` is enabled. `badges` are GuildQuality milestone awards for consistent positive reviews, active survey participation, or the Guildmaster distinction award — each includes an `iconUrl` for its badge icon image. `projectLocationCount` is the total number of completed projects GuildQuality has logged for the company (from its project map) — this can be larger than `reviewCount` since not every project gets an individual review. All are optional and only appear when the source profile actually publishes them.

**What are `photos`, `photoCount`, `ratingBreakdown`, and `unratedReviewCount`?**
`photos` are direct image URLs from the company's uploaded project photos, as shown on its profile page (`photoCount` is the company's total photo count on GuildQuality, which can be higher than the number of URLs returned unless `includeExtendedPhotos` is enabled). `ratingBreakdown` shows how many of the company's reviews fell at each star level (5 down to 1); `unratedReviewCount` is how many respondents left feedback without a star rating. Both are only present once a company has at least one review.

**How many reviews are captured per contractor?**
By default, up to 25 — everything the base profile page server-renders. Set `includeExtendedReviews` to `true` to also fetch the contractor's `?tab=reviews` page, which server-renders up to 50 reviews (confirmed to be a strict superset of the base page's 25, i.e. no reviews are lost or reordered). This costs one extra HTTP request plus the site's own 3-second crawl delay per contractor, so it's off by default.

**How many crew members and photos are captured per contractor?**
By default, only the base profile page's capped preview — roughly the first 3 crew members and up to ~11 photos, whichever the page itself server-renders. Set `includeExtendedCrew` and/or `includeExtendedPhotos` to `true` to also fetch the contractor's `?tab=crew` / `?tab=photos` pages, each confirmed to server-render every crew member / photo (a strict superset of the base page preview). Each only fires an extra request when the base page's `teamMemberCount` / `photoCount` actually shows more exist than were already captured, so contractors with a short crew or photo list cost nothing extra even with the flags on.

**Does this actor need a proxy or login?**
No. GuildQuality's pages are reachable directly from Apify's infrastructure with no proxy, no cookies, and no account.

**Is there a rate limit?**
GuildQuality's `robots.txt` specifies a 3-second crawl delay, which this actor honors on every request. This makes `browse` mode with a large `maxProfilesScanned` slower but ensures reliable, policy-compliant access.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `state` (type: `string`):

Only emit contractors headquartered in this US state.

## `nameContains` (type: `string`):

Only emit contractors whose company name contains this text (case-insensitive).

## `cityContains` (type: `string`):

Only emit contractors headquartered in a city containing this text (case-insensitive, e.g. `Austin` or `spring`).

## `typeOfWorkContains` (type: `string`):

Only emit contractors whose self-declared project types (e.g. `Remodeling & renovation`, `Roofers`, `New home`) contain this text (case-insensitive).

## `minRating` (type: `number`):

Drop contractors with an average rating below this (0-5 scale, decimals allowed, e.g. 4.5).

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

Drop contractors with fewer verified reviews than this.

## `minRecommendPercentage` (type: `number`):

Drop contractors whose customers recommend them at a lower rate than this (0-100%, decimals allowed). Only affects contractors that have this stat published.

## `sitemapSource` (type: `string`):

Which GuildQuality contractor pool to discover from.

## `maxProfilesScanned` (type: `integer`):

How many contractor profiles to fetch and check against filters before stopping (separate from maxItems since filters may reject many candidates).

## `includeExtendedReviews` (type: `boolean`):

By default up to 25 reviews per contractor are captured from the profile page. Enable this to also fetch the contractor's `?tab=reviews` page (confirmed to server-render up to 50 reviews, a superset of the default 25) at the cost of one extra request + crawl delay per contractor.

## `includeExtendedCrew` (type: `boolean`):

By default only the first ~3 crew members shown on the base profile page are captured. Enable this to also fetch the contractor's `?tab=crew` page (server-renders every crew member) whenever `teamMemberCount` shows more exist — one extra request + crawl delay per contractor, only when needed.

## `includeExtendedPhotos` (type: `boolean`):

By default only a capped preview of uploaded photos shown on the base profile page is captured. Enable this to also fetch the contractor's `?tab=photos` page (server-renders every photo) whenever `photoCount` shows more exist — one extra request + crawl delay per contractor, only when needed.

## `includeServiceAreas` (type: `boolean`):

Fetch the contractor's project-location map data and add `serviceAreas` — a de-duplicated, sorted list of `City, ST` locations where the contractor has completed a logged project (up to 5 pages / ~1000 projects per contractor). Only populated for contractors whose profile renders a project map (verified members with at least one geocoded project) — one or more extra requests + crawl delay per contractor.

## `profileUrlsOrSlugs` (type: `array`):

GuildQuality profile URLs (e.g. `https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric`) or bare slugs.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "browse",
  "sitemapSource": "verified",
  "maxProfilesScanned": 15,
  "includeExtendedReviews": false,
  "includeExtendedCrew": false,
  "includeExtendedPhotos": false,
  "includeServiceAreas": false,
  "profileUrlsOrSlugs": [
    "https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `contractors` (type: `string`):

Dataset containing all scraped GuildQuality records — profile info, verified ratings, reviews, social links, crew, badges, and completed-project counts.

# 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 = {
    "mode": "browse",
    "sitemapSource": "verified",
    "maxProfilesScanned": 15,
    "includeExtendedReviews": false,
    "includeExtendedCrew": false,
    "includeExtendedPhotos": false,
    "includeServiceAreas": false,
    "profileUrlsOrSlugs": [
        "https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/guildquality-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 = {
    "mode": "browse",
    "sitemapSource": "verified",
    "maxProfilesScanned": 15,
    "includeExtendedReviews": False,
    "includeExtendedCrew": False,
    "includeExtendedPhotos": False,
    "includeServiceAreas": False,
    "profileUrlsOrSlugs": ["https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/guildquality-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 '{
  "mode": "browse",
  "sitemapSource": "verified",
  "maxProfilesScanned": 15,
  "includeExtendedReviews": false,
  "includeExtendedCrew": false,
  "includeExtendedPhotos": false,
  "includeServiceAreas": false,
  "profileUrlsOrSlugs": [
    "https://www.guildquality.com/profile/ravinia-plumbing-heating-and-electric"
  ],
  "maxItems": 10
}' |
apify call crawlerbros/guildquality-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/guildquality-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/cZr44W0mHN0bOjo6H/builds/a6i51sf8R4RSzQk0I/openapi.json
