Construction Leads Scraper - Contractor Emails & Site Grades
Pricing
from $8.00 / 1,000 results
Construction Leads Scraper - Contractor Emails & Site Grades
Scrape verified leads for contractors, interior designers & builders from Google Maps. Get emails, SEO/security grades, and ready-to-use sales pitches.
Pricing
from $8.00 / 1,000 results
Rating
0.0
(0)
Developer
Techforce Global
Maintained by CommunityActor stats
1
Bookmarked
1
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Construction Leads Scraper - Contractor, Architect, Interior Designer and Real Estate Leads from Google Maps, with Company Emails, Graded Website Audits and Pitch Playbooks
Purpose-built construction and property prospecting: pick a business type (general contractor, architecture firm, interior designer, roofing contractor, construction company, real estate agency) or type your own construction search, and get every firm's phone, address, website, company email scraped from its own site, rating, reviews, hours and Maps URL. An optional Advanced Web Analysis pass adds lead priority, pitch strategy, a website performance grade, prioritized improvements and a tech audit. Billed per result — $10.00 per 1,000 on the Free tier, down to $8.00 per 1,000 on Business. Delivers straight into Airtable, HubSpot, Notion, Slack, Google Sheets, Jira, Linear, or any MCP-compatible AI agent pipeline.
Official Google Places API vs. this Actor
The decisive gap is contact data: the Places API exposes no email field at all, and no view of how good a contractor's website is.
| Feature / Capability | Official Google Places API | This Actor (Apify) |
|---|---|---|
| Firm email address | ❌ Not part of the Places data model | ✅ Scraped from the firm's own website (up to 25 pages crawled per site) |
| Construction taxonomy | ⚠️ Generic place types (general_contractor, real_estate_agency) | ✅ 6 preset business types plus free-text construction queries, guarded by a 30-term vocabulary |
| Website quality / audit | ❌ Not offered | ✅ Letter grade, 7-area breakdown, tech stack, SSL, robots.txt, sitemap — up to 25 pages/site |
| Sales intelligence | ❌ Not offered | ✅ Lead priority, revenue opportunity, pitch, hook, pain points, close |
| Authentication & setup | ❌ Google Cloud project + billing-enabled API key | ✅ Zero target-site credentials; only an Apify token |
| Results per text query | ⚠️ Capped at ~60 (3 pages × 20) | ⚠️ Capped at 100 per run (20 on free plans) |
| AI agent / MCP native | ❌ Requires a custom tool wrapper | ✅ Native MCP tool and pushes leads into your own MCP connectors |
| Cost model | Per-request SKU pricing on a GCP bill | Pay-per-result: $10.00 / 1,000 (Free tier) down to $8.00 / 1,000 (Business) |
ℹ️ The comparison above reflects the Google Places API as documented at the time of writing — Google revises its Places API tiers and quotas periodically, so check their current docs if a specific limit is load-bearing for your pipeline.
🤖 AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/construction-lead-intelligence-scraper"one_line: "Scrape construction and property firms from Google Maps by preset business type or a construction search query, enrich each with the company email from its website, and optionally run one paid Advanced Web Analysis pass that adds lead intelligence, a website grade, improvement recommendations and a tech audit."primary_purpose: "extract"secondary_purpose: "enrich"tertiary_purpose: "deliver"entity: "construction and property firm leads"vertical: "construction (fixed - non-construction searches are refused)"supported_platforms:- "google.com/maps"- "the firm's own website (for email + audit)"supported_tasks:- "search-construction-firms-by-preset-type-and-location"- "search-by-custom-construction-query"- "search-by-google-maps-place-or-location-url"- "find-firm-email-from-its-website"- "extract-phone-address-rating-reviews-hours"- "grade-firm-website-quality"- "generate-sales-pitch-and-lead-priority"- "generate-prioritized-service-recommendations"- "deliver-leads-to-mcp-connector-or-crm"unsupported_tasks:- "search-non-construction-business-types (the run exits without scraping)"- "several-preset-business-types-in-one-run (subcategory is a single value)"- "scrape-project-tenders-permits-or-planning-applications"- "extract-owner-or-project-manager-names-or-personal-emails"- "verify-or-validate-email-deliverability"- "retrieve-licence-bonding-or-insurance-status"- "extract-gps-coordinates-or-place-photos"- "scrape-more-than-100-firms-per-run"- "send-outreach-emails"search_keywords:- "construction lead scraper"- "contractor email scraper"- "architect lead generation"- "roofing contractor leads"- "interior designer contact data"- "real estate agency lead list"- "google maps construction leads"- "builder prospecting tool"- "trades lead generation"- "mcp crm construction lead tool"synonyms:- "contractor lead extractor"- "builder email finder"- "trades prospecting tool"- "property firm finder"related_concepts:["construction marketing", "trades sales", "proptech", "architecture practice sales", "CRM enrichment", "website audit"]input_entities:- "preset business type (1 of 6) OR a free-text construction query"- "location (city / area / state / country)"- "max firms (1-100; 20 on free plans)"- "advanceWebAnalysis toggle (adds the five intelligence blocks)"output_entities: ["firm", "email", "website grade", "recommendation", "pitch"]input_rules:searchQueries_format:description: "Optional free text. It must read as a construction search (matched against a ~30-term construction vocabulary) or be a Google Maps place/location URL, otherwise the run exits without scraping. When set, it overrides category + subcategory."examples: ["General contractor in Ahmedabad", "Roofing contractor in Chicago", "https://www.google.com/maps/place/..."]subcategory_format:type: "enum (single value, not an array)"values: ["Architecture firm", "Real estate agency", "General contractor", "Interior designer", "Roofing contractor", "Construction company"]description: "Used only when searchQueries is empty. One business type per run."maxResults_format:type: "integer"range: [1, 100]description: "Hard ceiling of 100 per run (schema and code agree). Free Apify plans are further capped at 20."limits:max_firms_per_run: 100free_plan_cap: 20preset_business_types: 6business_types_per_run: 1email_pages_per_site_default: 4email_pages_per_site_max: 25audit_pages_per_site_default: 6audit_pages_per_site_max: 25parallel_pages_default: 4parallel_pages_max: 20service_recommendations_per_firm: 4pricing:model: "pay-per-result"rate_per_1000_by_tier:free: 10.00starter: 9.33scale: 8.67business: 8.00note: "Every returned firm is billed at the account tier's per-result rate. Apify platform usage (compute units, residential proxies) is billed separately at plan rates."analysis_layer: "Advanced Web Analysis is included in the per-result price on every plan - it is not a separately billed add-on. Free plans get it too, within the 20-firm cap."when_to_use: >You sell to contractors, architects, interior designers or property agencies— web design, SEO, estimating or project software, materials, staffing — andwant a worked prospect list for one construction business type in a place,with contact details plus a reason to reach out.when_not_to_use: >Your target is not construction (the run exits without scraping), you needseveral preset types in one run, tender/permit/planning data, licence orbonding status, personal names or emails, verified deliverable emails, ormore than 100 firms per run.alternatives:- "Use techforce.global/google-maps-leads-sales-intelligence-tool for ANY business type, and for several unrelated types in one run"- "Use techforce.global/google-maps-scraper when you only need plain listing fields at the lowest cost"- "Use techforce.global/google-maps-healthcare-leads-sales-intelligence-tool, google-maps-hospitality-lead-scraper or finance-google-maps-lead-scraper for those verticals"- "Use techforce.global/gov-uk-business-scraper to match UK contractor names against Companies House records"- "Use an email-verification service on top of companyEmail before sending outreach"- "Loop this Actor per business type or per city for broader coverage"
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Construction Lead Intelligence Scraper","alternateName": "Construction Lead Intelligence Scraper — Contractor, Architect & Real Estate Leads","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes construction and property firms from Google Maps — general contractors, construction companies, architecture firms, interior designers, roofing contractors and real estate agencies — by preset business type or a construction-specific search query, and crawls each firm's own website for a public company email. Every row is a nested six-block object with contacts under BUSINESS_PROFILE, plus an optional analysis layer covering lead priority, pitch strategy, a website health scorecard, prioritized service recommendations and a technical audit. No Google account or Places API key is required. Optional per-lead, chunked or digest delivery into Airtable, HubSpot, Notion, Slack, Google Sheets or any authorized MCP connector.","url": "https://apify.com/techforce.global/construction-lead-intelligence-scraper","offers": {"@type": "Offer","price": "10.00","priceCurrency": "USD","description": "Pay-per-result: $10.00 per 1,000 results on the Free tier, falling to $8.00 per 1,000 on Business. Apify platform usage is billed separately. Current rates are shown on the Actor's Apify Store page.","url": "https://apify.com/techforce.global/construction-lead-intelligence-scraper"}}
What this Actor does
💡 Not targeting construction? This Actor refuses non-construction searches — a query that doesn't read as construction ends the run without scraping. For any other business type use Google Maps Business Leads & Sales Intelligence (free-text queries, any industry), or a sibling vertical: Healthcare, Hospitality, Finance. For plain listings at the lowest cost, use Google Maps Scraper (from $0.80 / 1,000).
- Two ways in: pick one of 6 preset construction business types with a location, or type your own query — a construction phrase or a full Google Maps place/location URL.
- Refuses off-vertical work. A custom query is checked against a ~30-term construction vocabulary (architect, builder, civil/concrete/electrical/masonry/mechanical/plumbing/roofing contractor, developer, infrastructure, interior design, structural engineer, real estate agency…). Anything else is ignored and the run ends without scraping — you are never billed for a mis-targeted run.
- Collects listings with Playwright using resilient extraction that tolerates Google's rotating markup — name, category, rating, review count, address, phone, website, hours.
- Visits each firm's own website for the company email and social links — 4 pages per site by default, and up to 25 if an API/SDK/MCP call raises
maxEmailPagesPerSite(the field is hidden in the Console form). This layer is not charged by the Actor. - One optional Advanced Web Analysis event — a single per-run charge, not per business — adds
LEAD_OVERVIEW,PITCH_STRATEGY,WEBSITE_HEALTH_SCORECARD,SERVICE_RECOMMENDATIONSandTECHNICAL_INTEL, auditing up to 25 pages per site. - Fails safe on billing. The analysis layer is part of what a result costs, so it runs on free and paid plans alike. In the one case where it cannot run — a
maxTotalChargeUsdtoo low to cover it under pay-per-event pricing — the Actor logs a warning, returns contacts and emails anyway, and never aborts the run over it. - Free Apify plans get the analysis for up to 20 companies.
- Runs email discovery and the website audit concurrently per business (separate pages), roughly halving per-firm wall-clock time.
- Optionally pushes leads to an authorized MCP connector in four delivery modes — one call per lead, chunked parts for long lists, one digest, or none. The dataset is always saved regardless of delivery settings.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| Selling web design / SEO / marketing to contractors and architects | ✅ Strong (this is the core use case) |
| Estimating, project-management or CRM software prospecting | ✅ Strong |
| Materials, equipment or trade-supply distribution | ✅ Strong |
| Construction cold outreach with an email and a reason to call | ✅ Strong |
| A contacts-only list | ✅ Strong — turn advanceWebAnalysis off (results are still billed per result) |
| Deep website audits (up to 25 pages per firm) | ✅ Strong on free plans; ⚠️ currently gated on paid plans — see Pricing |
| CRM / Airtable / HubSpot enrichment via connector | ✅ Strong (deliveryMode: "perLead") |
| Several preset business types in one run | ❌ subcategory is a single value — run once per type |
| A business type outside construction | ❌ The run exits without scraping — use the any-industry Actor |
| More than 100 firms in one execution | ⚠️ Hard cap of 100 (20 on free plans) — split across runs |
| Guaranteed-deliverable email addresses | ⚠️ Emails are scraped, not verified — validate before sending |
| Tenders, permits, planning applications, project pipelines | ❌ Not extracted — Google Maps listings only |
| Licence, bonding or insurance status | ❌ Not extracted — pair with a registry source |
| Owner / project-manager names or personal emails | ❌ Not supported — firm-level public contacts only |
| Review text, reviewer names, place photos, GPS coordinates | ❌ Not extracted |
⚡ Execution Rules (Avoid Common Agent Failures)
- Nothing is required, and every field has a default.
requiredis empty: an input of{}scrapesGeneral contractorinLondon,maxResults: 5, with Advanced Web Analysis ON. It will not raise a validation error. API and MCP callers should always sendsubcategory(orsearchQueries),locationandmaxResultsexplicitly. maxResultsdefaults to just 5 — the lowest default in the family. Set it explicitly or you will get a five-row dataset.subcategoryis a single string, not an array. One preset business type per run:Architecture firm,Real estate agency,General contractor,Interior designer,Roofing contractor,Construction company. To cover several, run the Actor once per type.searchQueriesoverridescategory+subcategorywhen it is non-empty.- A non-construction
searchQueriesvalue ends the run without scraping. The string must contain a construction term (or be a Google Maps URL)."dentist in Austin"produces an empty, successful run — checkitemCountand the log, not just run status. maxResultsis capped at 100 per run, matching every sibling Actor; schema and code agree, so >100 is rejected up front. Free Apify plans are further capped at 20.- Results are billed per result, at the account tier's rate — $10.00 / 1,000 on Free down to $8.00 / 1,000 on Business. The
advanceWebAnalysistoggle changes what each item contains and how long the run takes; it does not add a separate per-result charge. - The analysis layer is included in the per-result price, on every plan.
advanceWebAnalysis: true(the default) runs it for free-plan and paid runs alike. The single case where it is skipped is amaxTotalChargeUsdtoo low to cover it under pay-per-event pricing; the run then returns contacts and emails and logsmax charge limit does not allow the premium event. The output carries no flag for which mode ran — check whetherLEAD_OVERVIEW.leadPriorityis populated, or read the run log. - Every item is the same nested 6-block object —
LEAD_OVERVIEW,BUSINESS_PROFILE,PITCH_STRATEGY,WEBSITE_HEALTH_SCORECARD,SERVICE_RECOMMENDATIONS,TECHNICAL_INTEL. Contact fields live insideBUSINESS_PROFILE, not at the top level — this differs from every sibling Actor, whose profile fields are flat. Readitem["BUSINESS_PROFILE"]["companyEmail"], neveritem["companyEmail"]. - With the analysis off, the analysis blocks are present but empty. They are not removed — expect empty strings/arrays rather than missing keys, so branch on content, not on key presence.
- A missing email is the string
"NA". ReadBUSINESS_PROFILE.emailStatusfirst:ok·no_email_found·failed(site wouldn't load) ·no_website. maxEmailPagesPerSiteandmaxAuditPagesPerSiteare the two biggest run-time levers, and both are API-only. Each accepts1–25, but both are hidden in the Console form, so a Console run always uses the defaults of 4 and 6. Along withbatchSizeandlanguage, they can only be set from the API, an SDK or MCP.- There is no proxy input. This Actor has no
proxyConfigurationfield and always uses the container's direct connection. deliveryModedefaults toperLead, notnone. With a connector selected, a 100-firm run makes 100 connector calls. For dataset-only runs leavemcpConnectorempty or setdeliveryMode: "none".- Delivery placeholders use short names —
{name},{email},{rating},{reviews},{mapsUrl}— not the sibling Actors'{businessName}/{companyEmail}/{googleRating}. A template copied from another Actor in this family will render empty.
dependencies:- rule: "searchQueries (when non-empty) overrides category + subcategory"note: "leave it empty to use the preset business type"- rule: "a non-construction searchQueries value ends the run with zero items"on_violation: "run is SUCCEEDED with itemCount 0; nothing is scraped or charged"- rule: "advanceWebAnalysis is included in the per-result price and needs no billable event"on_violation: "skipped only when maxTotalChargeUsd is too low to cover a configured pay-per-event charge; contacts and emails are still returned"note: "on this pay-per-RESULT listing there is no separate analysis charge to refuse"- rule: "free Apify plans get the analysis but are capped at 20 companies"note: "the cap is on companies per run, not on which blocks are populated"- rule: "contact fields are nested inside BUSINESS_PROFILE"on_violation: "top-level lookups like item['companyEmail'] return nothing"- rule: "delivery requires mcpConnector AND mcpTool"on_violation: "delivery step is skipped with a warning; the dataset is still written in full"- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"on_violation: "message body is empty; the tool is still called"- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery; the dataset is unaffected"authentication:actor_input: noneplatform: "Apify API token required for API / SDK / MCP invocation"target_site: "no Google account, API key, or GCP billing project required (public listings and public website content only)"
📥 Input Contract
.actor/input_schema.json is authoritative — if any table on this page disagrees with the schema files, the schema files win.
Scraping Parameters
| Field | Type | Required | Default | Allowed Values / Format | Example |
|---|---|---|---|---|---|
searchQueries | string | ⬜ No | "" | A construction phrase, or a Google Maps place/location URL. Overrides category+subcategory | Roofing contractor in Chicago |
category | string (enum) | ⬜ No | Construction | Construction only | Construction |
subcategory | string (enum) | ⬜ No | General contractor | One of the 6 preset types | Architecture firm |
location | string | ⬜ No | London | City / area / state / country | Ahmedabad |
maxResults | integer | ⬜ No | 5 | 1–100 (free plans capped at 20) | 100 |
language | string | ⬜ No | en | Google Maps language code — hidden in Console, settable via API / SDK / MCP | en |
batchSize | integer | ⬜ No | 4 | 1–20 parallel browser pages — hidden in Console, settable via API / SDK / MCP | 8 |
maxEmailPagesPerSite | integer | ⬜ No | 4 | 1–25 pages crawled for email/social — hidden in Console, settable via API / SDK / MCP | 10 |
advanceWebAnalysis | boolean | ⬜ No | true | Unlocks all five analysis blocks (see Pricing for the current gating) | false |
maxAuditPagesPerSite | integer | ⬜ No | 6 | 1–25, only used when the analysis runs — hidden in Console, settable via API / SDK / MCP | 20 |
Preset business types — all 6 values
| Business type | Typical buyer for it |
|---|---|
General contractor | Web/SEO, estimating software, materials, staffing |
Construction company | ERP, project management, equipment, insurance |
Architecture firm | CAD/BIM tooling, rendering, marketing, recruitment |
Interior designer | Sourcing platforms, 3D visualisation, lead gen |
Roofing contractor | Local SEO, lead gen, materials, financing |
Real estate agency | Proptech, listing marketing, photography, CRM |
Custom-query vocabulary
A non-empty searchQueries must contain one of ~30 construction terms, or be a Google Maps URL. Accepted terms include:
architecture · architect(s) · builder(s) · building contractor · civil contractor · commercial construction · concrete contractor · construction · construction company · construction firm · contractor(s) · developer(s) · electrical contractor · engineering firm · general contractor · home builder · infrastructure · interior designer · interior design · landscaping contractor · masonry contractor · mechanical contractor · plumbing contractor · property developer · real estate agency · real estate developer · renovation contractor · roofing contractor · structural engineer
Anything else — "dentist in Austin", "cafe in Goa" — is ignored and the run ends without scraping.
Examples
Preset type, contacts only — fastest run, smallest payload:
{"subcategory": "General contractor","location": "Ahmedabad","maxResults": 100,"advanceWebAnalysis": false,"deliveryMode": "none"}
Full intelligence on a focused set — one analysis charge covers the whole run:
{"subcategory": "Architecture firm","location": "London","maxResults": 50,"advanceWebAnalysis": true,"maxAuditPagesPerSite": 20,"deliveryMode": "none"}
Custom construction query with a deeper email crawl. maxEmailPagesPerSite is hidden in the Console form, so this payload can only be sent from the API, an SDK or MCP:
{"searchQueries": "Roofing contractor in Chicago","maxResults": 80,"maxEmailPagesPerSite": 12,"advanceWebAnalysis": false,"deliveryMode": "none"}
MCP Delivery Configuration (Optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | Authorized MCP connector on your Apify account (Airtable, HubSpot, Notion, Slack, Sheets, Jira, GitHub, Linear, …). Leave empty for dataset-only runs. |
deliveryMode | enum | perLead | perLead (one call per lead) · chunked (split a long list across calls) · summary (one digest) · none. |
mcpTool | string | "" | Tool name on the connector: create_record, send_message, create_page, create_issue. Run once with a connector selected — the log lists the connector's available tools. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Placeholders by mode — short names, unlike every sibling Actor:
| Mode | Placeholders |
|---|---|
perLead | {name}, {category}, {address}, {phone}, {email}, {website}, {rating}, {reviews}, {mapsUrl}, {leadPriority}, {salesAngle}, {recommendedPitch}, {finalGrade}, {message} |
summary | {leadCount}, {leads} (all leads as text blocks), {message} |
chunked | the above, plus {part} and {partCount} — and {leads} holds one part |
Chunked delivery groups lead blocks into parts under ~72,000 characters, so services with per-request block or timeout caps (Notion in particular) never reject the call.
One Airtable/CRM record per lead:
{"subcategory": "Roofing contractor","location": "Chicago","maxResults": 60,"mcpConnector": "<your-authorized-airtable-connector>","deliveryMode": "perLead","mcpTool": "create_record","mcpArguments": {"fields": {"Name": "{name}","Type": "{category}","Phone": "{phone}","Email": "{email}","Website": "{website}","Rating": "{rating}","Priority": "{leadPriority}","Grade": "{finalGrade}","Maps": "{mapsUrl}"}}}
One Slack digest for the whole run:
{"subcategory": "General contractor","location": "Manchester","maxResults": 40,"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpArguments": { "channel": "#construction-leads", "text": "{message}" },"mcpMessageTemplate": "{leadCount} construction leads:\n\n{leads}"}
Chunked into Notion pages for a long list:
{"subcategory": "Construction company","location": "Mumbai","maxResults": 100,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": { "title": "Mumbai construction leads (part {part}/{partCount})" },"content": "{leads}"}]}}
📤 Output Contract & Data Structure
Storage: Apify Dataset (one JSON object per firm).
Pagination: limit & offset on the dataset items endpoint.
Duplicates: one record per listing within a run. Cross-run deduplication is the consumer's responsibility — use BUSINESS_PROFILE.googleMapsUrl as the key.
⚠️ Nested shape — different from every sibling Actor. Each item has six top-level blocks and the contact fields live inside BUSINESS_PROFILE:
{LEAD_OVERVIEW: {...}, // analysisBUSINESS_PROFILE: {...}, // ← name, phone, email, website, rating…PITCH_STRATEGY: {...}, // analysisWEBSITE_HEALTH_SCORECARD: {...}, // analysisSERVICE_RECOMMENDATIONS: [...], // analysisTECHNICAL_INTEL: {...} // analysis}
With advanceWebAnalysis off (or disabled by billing), the five analysis blocks are still present but empty — empty strings, empty arrays, Unknown — rather than removed. Branch on content, not on key presence.
BUSINESS_PROFILE — always populated
| Field | Type | Meaning |
|---|---|---|
businessName | string | Firm name as listed |
category | string | Google Maps category for the firm |
address | string | Full address as displayed |
phone | string | Phone number as displayed |
website | string | Website URL from the listing |
companyEmail | string | Email found on the firm's website; the literal string "NA" when none was found |
emailStatus | string | ok · no_email_found · failed · no_website |
workingHours | object/string | Opening hours by day (formatted) |
googleRating | string | Star rating as a string — e.g. "4.7" |
totalReviews | string | Review count as a string |
googleMapsUrl | string | Listing URL — use as the dedupe key |
searchQuery | string | The resolved query that produced this record |
LEAD_OVERVIEW — requires Advanced Web Analysis
| Key | Type | Meaning |
|---|---|---|
leadPriority | string | Triage label — e.g. High |
revenueOpportunityLevel | string | Opportunity size |
salesAngle | string | Why this firm is worth calling |
recommendedPitch | string | The pitch to lead with |
estimatedMonthlyServicePotential | string | Recurring revenue estimate |
estimatedOneTimeProjectPotential | string | Project revenue estimate |
PITCH_STRATEGY — requires Advanced Web Analysis
| Key | Type | Meaning |
|---|---|---|
openingHook | string | Opening line tailored to the firm's rating/reviews/site |
bestServicesToPitch | array | Services worth proposing (up to 4) |
painPointsToHighlight | array | Up to 5 concrete weaknesses to raise |
outcomesToPromise | array | Results to promise |
closingStrategy | string | Suggested close |
WEBSITE_HEALTH_SCORECARD — requires Advanced Web Analysis
| Key | Type | Meaning |
|---|---|---|
finalGrade | string | Letter grade for the website overall |
finalClassification | string | Plain-language verdict — e.g. Needs Improvement |
gradeBreakdown | array of {area, grade, status} | Per-area grades. Areas: Technical Quality · Technical SEO · SSL / Security · Navigation Structure · Performance · SEO Readiness · SEO Parameters. Areas with no grade are omitted. |
SERVICE_RECOMMENDATIONS — requires Advanced Web Analysis
Array, at most 4 items, sorted High → Medium → Low priority:
| Key | Type | Meaning |
|---|---|---|
priority | string | High · Medium · Low |
service | string | Service to sell, in human-readable form |
currentGrade | string | The area's current grade |
whatToFix | string | The concrete work required |
whyItMatters | string | Business rationale |
outcomeAfterFix | string | Expected result once fixed |
sellingPoint | string | The line to use in the proposal |
TECHNICAL_INTEL — requires Advanced Web Analysis
| Key | Type | Meaning |
|---|---|---|
techStack | string | Detected stack, or Unknown |
sslStatus | string | Status with grade — e.g. "Good (Grade A)" |
robotsTxtStatus | string | HTTP status label for robots.txt |
sitemapUrl | string | Sitemap URL if found |
auditedPages | integer | How many pages of that site were audited (up to maxAuditPagesPerSite) |
auditedUrls | array | The exact URLs audited |
navigationNotes | string | Navigation-structure summary |
keyTechnicalIssues | array | Notable problems found |
Consumer rules: read contacts from BUSINESS_PROFILE; googleRating and totalReviews are strings; a missing email is the literal "NA"; analysis blocks exist even when the analysis didn't run; and there is no socialMedia, coordinate, or photo field in the output.
Example output item
{"LEAD_OVERVIEW": {"leadPriority": "High","revenueOpportunityLevel": "High","salesAngle": "Strong local reputation but a dated site with no project gallery or quote form.","recommendedPitch": "Rebuild the site around project photography and a quote request flow.","estimatedMonthlyServicePotential": "$700–$1,400/mo","estimatedOneTimeProjectPotential": "$4,000–$8,000"},"BUSINESS_PROFILE": {"businessName": "Halewood Building Contractors","category": "General contractor","address": "42 Ordsall Ln, Manchester M5 4RR","phone": "+44 161 555 0100","website": "https://halewoodbuilding.co.uk","companyEmail": "office@halewoodbuilding.co.uk","emailStatus": "ok","workingHours": { "Monday": "8:00 AM – 5:00 PM" },"googleRating": "4.7","totalReviews": "58","googleMapsUrl": "https://www.google.com/maps/place/...","searchQuery": "General contractor in Manchester"},"PITCH_STRATEGY": {"openingHook": "You have a 4.7-star rating with 58 reviews - strong social proof.","bestServicesToPitch": ["Website redesign", "Local SEO"],"painPointsToHighlight": ["Performance: slow pages lose quote requests"],"outcomesToPromise": ["More qualified quote requests from the same traffic"],"closingStrategy": "Offer a paid audit that credits toward the rebuild."},"WEBSITE_HEALTH_SCORECARD": {"finalGrade": "C","finalClassification": "Needs Improvement","gradeBreakdown": [{ "area": "Performance", "grade": "D", "status": "Poor (Grade D)" },{ "area": "SSL / Security", "grade": "A", "status": "Good (Grade A)" }]},"SERVICE_RECOMMENDATIONS": [{"priority": "High","service": "Website performance optimization","currentGrade": "D","whatToFix": "Compress project photos and defer unused scripts.","whyItMatters": "Slow galleries lose mobile visitors before they request a quote.","outcomeAfterFix": "Faster load and more completed quote forms.","sellingPoint": "Turn existing search traffic into booked site visits."}],"TECHNICAL_INTEL": {"techStack": "WordPress","sslStatus": "Good (Grade A)","robotsTxtStatus": "200 OK","sitemapUrl": "https://halewoodbuilding.co.uk/sitemap.xml","auditedPages": 6,"auditedUrls": ["https://halewoodbuilding.co.uk/"],"navigationNotes": "Primary navigation is clear; no dedicated services pages.","keyTechnicalIssues": ["Large uncompressed gallery images"]}}
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 47 }}
The terminal log line reads Finished <n> companies in <t>s. plus the connector count when delivery ran. itemCount: 0 with a SUCCEEDED status usually means the query was not recognised as construction — check the log before treating it as a scraper failure. On free plans, itemCount stops at 20.
▶️ Invocation & Integration
Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor → Input tab → either type a construction search or pick a Business type and Location → raise Maximum businesses from the default of 5 → decide on Advanced business opportunity analysis → tune the email/audit page depths → Start → export from the Dataset tab (JSON, CSV, Excel, XML).
Model Context Protocol (MCP)
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/construction-lead-intelligence-scraper"
Python (apify-client)
import osfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))run = client.actor("techforce.global/construction-lead-intelligence-scraper").call(run_input={"subcategory": "General contractor","location": "Manchester","maxResults": 100, # 100 is the ceiling; free plans stop at 20"advanceWebAnalysis": True, # one charge for the whole run"maxAuditPagesPerSite": 10,"deliveryMode": "none",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f"{len(items)} firms")# Contacts are NESTED under BUSINESS_PROFILE - not at the top level.for item in items[:5]:profile = item.get("BUSINESS_PROFILE", {})overview = item.get("LEAD_OVERVIEW", {})print(profile.get("businessName", "-"),"|", profile.get("phone", "-"),"|", profile.get("companyEmail", "NA"),"|", overview.get("leadPriority") or "(no analysis)",)emailable = [i for i in itemsif i.get("BUSINESS_PROFILE", {}).get("companyEmail", "NA") != "NA"]print(f"{len(emailable)} with a usable email")
JavaScript / TypeScript (apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('techforce.global/construction-lead-intelligence-scraper').call({searchQueries: 'Roofing contractor in Chicago',maxResults: 80,advanceWebAnalysis: false, // contacts only - fastest runmaxEmailPagesPerSite: 10,deliveryMode: 'none',});const { items } = await client.dataset(run.defaultDatasetId).listItems();const withEmail = items.filter((i) => i.BUSINESS_PROFILE?.companyEmail && i.BUSINESS_PROFILE.companyEmail !== 'NA',);console.log(`${items.length} contractors, ${withEmail.length} with an email`);
cURL — synchronous (returns items directly; 300s limit)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"subcategory":"General contractor","location":"London","maxResults":20,"advanceWebAnalysis":false,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~construction-lead-intelligence-scraper/run-sync-get-dataset-items'
Use the async pattern below for anything beyond ~20 firms — website crawling and auditing make this Actor far slower per record than a listings-only scraper.
cURL — asynchronous with a spend cap (recommended for production)
# 1. Start execution with a hard spend limit# NOTE: too low a cap makes the Actor skip Advanced Web Analysis and return contacts only.curl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"subcategory":"Architecture firm","location":"London","maxResults":100,"advanceWebAnalysis":true,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~construction-lead-intelligence-scraper/runs?maxTotalChargeUsd=2.00'# 2. Pollcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json&limit=1000&offset=0'
⚠️ Autonomous Agent Error Handling & Resolution Matrix
| Error Code | Detection Condition | Underlying Cause | Deterministic Agent Action |
|---|---|---|---|
AUTH_INVALID | HTTP 401 | Missing or malformed APIFY_TOKEN | Abort & Prompt User: request a valid API token. |
RATE_LIMITED | HTTP 429 | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Run exceeded the 300s synchronous ceiling — easy to hit here | Reconfigure: switch to async POST /runs + polling. |
SILENT_DEFAULT | Results are General contractors in London you never asked for | No field is required; subcategory/location defaulted | Modify Input: always pass both explicitly from API/MCP/SDK. |
TINY_DEFAULT_RUN | Exactly 5 items when you expected many | maxResults defaults to 5 | Modify Input: set maxResults explicitly. |
NON_CONSTRUCTION_QUERY | SUCCEEDED, itemCount: 0, nothing scraped | searchQueries didn't match the construction vocabulary | Modify Input: add a construction term, use a preset subcategory, or re-route to the any-industry Actor. |
MULTI_TYPE_REQUEST | User wants several preset types at once | subcategory is a single value | Reconfigure: one run per type, then merge datasets. |
EMPTY_RESULTS | SUCCEEDED, itemCount: 0, log shows a valid query | Type/location combination has no Google Maps matches | Modify Input: widen the location or change the type. |
CAP_REACHED | itemCount == maxResults == 100 | Per-run maximum of 100 truncated the result set | Reconfigure: partition by city or business type across runs. |
MAX_RESULTS_REJECTED | Input validation rejects maxResults above 100 | The schema maximum is 100 (schema and code agree) | Modify Input: send 100 or less and run repeatedly for more. |
FREE_PLAN_CAP | itemCount stops at 20, log Reached free-tier limit | Free Apify plan caps the run at 20 companies | Prompt User: upgrade for full-volume runs. |
ANALYSIS_INCLUDED | Log Advance Web Analysis is included at no extra charge | The listing is priced per result, so the analysis needs no separate event | Informational. The analysis is running; no action needed. |
ANALYSIS_UNCHARGED | Log event 'advanced-web-analysis' is not configured | Pay-per-event pricing is on but the event is undefined | Informational. The analysis runs without a separate charge; no action needed. |
ANALYSIS_CHARGE_REFUSED | Log max charge limit does not allow the premium event | maxTotalChargeUsd too low for the analysis charge | Reconfigure: raise the spend cap and re-run if you need the analysis. |
ANALYSIS_MISSING | Analysis blocks present but empty | advanceWebAnalysis: false, or the spend cap refused a configured analysis charge | Check LEAD_OVERVIEW.leadPriority and the run log before assuming an extraction bug. |
WRONG_FIELD_PATH | Top-level companyEmail / businessName is undefined | Contact fields are nested inside BUSINESS_PROFILE | Fix Consumer: read item["BUSINESS_PROFILE"]["companyEmail"]. |
NO_EMAIL | BUSINESS_PROFILE.emailStatus: "no_email_found" or email "NA" | The site published no discoverable email within the crawled pages | Not an error. Fall back to phone, or raise maxEmailPagesPerSite from an API/SDK/MCP call. |
NO_WEBSITE | BUSINESS_PROFILE.emailStatus: "no_website" | The listing has no website, so no email or audit is possible | Not an error. Analysis blocks will carry no website grades. |
SITE_UNREACHABLE | emailStatus: "failed", log Homepage load failed for … | The firm's site timed out or refused the connection | Not an error. Retry later if the email matters. |
NO_LICENCE_DATA | No licence / bonding / insurance fields | Not part of Google Maps listings | Enrich elsewhere: pair with a licensing board, or GOV.UK Business Scraper for UK entities. |
PLACEHOLDER_EMPTY | Connector message renders blank fields | Sibling-style placeholders ({businessName}, {companyEmail}) were used | Modify Input: use this Actor's short names — {name}, {email}, {rating}, {mapsUrl}. |
SLOW_RUN | Run approaching the timeout | maxEmailPagesPerSite and maxAuditPagesPerSite both up to 25 | Reconfigure: turn the analysis off, or from an API/SDK/MCP call lower both depths and raise batchSize. |
DELIVERY_SKIPPED | Log no tool name was provided | mcpConnector set but mcpTool empty | Modify Input: set mcpTool; the dataset is already saved. |
DELIVERY_TOOL_UNKNOWN | Log Tool 'x' is not available on this connector | Wrong tool name for that connector | Modify Input: pick a name from the Available tools: list in the same log line. |
DELIVERY_EMPTY | Connector called, body empty | mcpArguments omitted the {message} placeholder | Modify Input: map {message} inside mcpArguments. |
DELIVERY_FLOOD | Hundreds of connector calls / connector rate limits | deliveryMode defaults to perLead | Reconfigure: use summary or chunked. |
MCP_PROXY_MISSING | Log APIFY_MCP_PROXY_URL is not set | Running locally instead of on the platform | Reconfigure: apify push and run on the platform. |
Reading run status:
SUCCEEDEDwithitemCount: 0is a valid outcome — it means the query returned no Google Maps matches, or the custom query was rejected as non-construction. Neither is a run failure; only aFAILEDstatus is a platform-level failure. Every log-line and field condition in the table above comes from this Actor’s own behavior; the401/408/429rows are standard Apify API responses.
🗣️ Natural Language → Actor Mapping
| User says | Intent | Constructed Actor input |
|---|---|---|
| "Find general contractors in Manchester with their emails" | Construction lead gen | {"subcategory":"General contractor","location":"Manchester","maxResults":100} |
| "Get me architecture firms in London" | Practice prospecting | {"subcategory":"Architecture firm","location":"London"} |
| "List roofing contractors in Chicago" | Trade prospecting | {"searchQueries":"Roofing contractor in Chicago","maxResults":80} |
| "Interior designers in Mumbai, contacts only" | Cheap contact list | {"subcategory":"Interior designer","location":"Mumbai","advanceWebAnalysis":false} |
| "Which contractors have bad websites?" | Agency prospecting | {"advanceWebAnalysis":true} then sort on WEBSITE_HEALTH_SCORECARD.finalGrade |
| "Who should I call first?" | Lead triage | {"advanceWebAnalysis":true} then read LEAD_OVERVIEW.leadPriority |
| "What should I fix on this builder's site, and how do I sell it?" | Proposal input | {"advanceWebAnalysis":true} then read SERVICE_RECOMMENDATIONS |
| "Audit these firms' sites properly — 20 pages each" | Deep audit | {"advanceWebAnalysis":true,"maxAuditPagesPerSite":20,"maxResults":40} — API/SDK/MCP only |
| "Real estate agencies in Ahmedabad" | Property prospecting | {"subcategory":"Real estate agency","location":"Ahmedabad"} |
| "Push these contractor leads into Airtable" | CRM delivery | {"mcpConnector":"airtable","deliveryMode":"perLead","mcpTool":"create_record","mcpArguments":{"fields":{"Name":"{name}","Email":"{email}"}}} |
| "Post today's construction leads to Slack" | Digest delivery | {"mcpConnector":"slack","deliveryMode":"summary","mcpTool":"send_message","mcpArguments":{"channel":"#leads","text":"{message}"}} |
| "Save a long lead list into Notion without it timing out" | Chunked delivery | {"deliveryMode":"chunked","mcpTool":"notion-create-pages"} with {part}/{partCount} in the title |
| "Contractors and architects in one run" | Multi-type | ⚠️ subcategory is single-valued — run once per type, or use the any-industry Actor |
| "Find dentists / cafes / accountants" | Non-construction | ❌ The run exits without scraping — route to the matching vertical or any-industry Actor |
| "Show me open tenders or planning applications" | Project pipeline | ❌ Out of scope — Google Maps listings only |
| "Is this contractor licensed and bonded?" | Compliance check | ❌ Out of scope — pair with a licensing board register |
| "Get the owner's personal email" | Contact discovery | ❌ Out of scope — firm-level public emails only |
Should NOT route here: any non-construction business type (the run exits without scraping) · several preset types in one run · plain listing data at lowest cost (→ Google Maps Scraper) · tenders, permits, planning applications or project pipelines · licence, bonding or insurance status · owner names or personal emails · review text, photos or coordinates · email verification/deliverability · sending outreach · more than 100 firms in one run.
🧭 Agent Execution & Routing Logic
[Input User Query]│▼1. Target is Google Maps business listings? NO → Route to the right platform Actor│ YES▼2. Is the target a CONSTRUCTION or PROPERTY firm? NO → Route to the any-industry / matching vertical Actor│ YES (a non-construction query here scrapes nothing)▼3. Needs tenders / permits / licences / project data? YES → Abort (not extracted)│ NO▼4. Pick the input route:├── one of the 6 preset types? → subcategory + location└── a specific phrase or URL? → searchQueries (must contain a construction term)▼5. Several preset types needed? YES → One run per type, merge datasets│ NO▼6. Set maxResults EXPLICITLY (default is only 5; ceiling 100; free plans 20)▼7. Advanced Web Analysis needed?├── YES → advanceWebAnalysis: true│ └─ included in the per-result price on every plan; skipped only if│ maxTotalChargeUsd is too low to cover a configured analysis charge└── NO → advanceWebAnalysis: false (same per-result price, faster run, smaller payload)▼8. Tune depth vs speed (API/SDK/MCP only - all four are hidden in Console):maxEmailPagesPerSite (1-25, default 4), maxAuditPagesPerSite (1-25, default 6), batchSize (1-20, default 4)▼9. Deliver to a connector? YES → mcpConnector + mcpTool + {message}│ use SHORT placeholders: {name} {email} {rating} {mapsUrl}│ NO → set deliveryMode "none" (it defaults to perLead)▼[Execute Apify Actor]│├──► status == "SUCCEEDED" ──► itemCount == 0? YES → was the query construction-shaped? fix and re-run│ │ NO│ └──► read BUSINESS_PROFILE.* for contacts (NOT top level)│ check whether the analysis blocks are populated└──► status == "FAILED" ──► route to Error Handling Matrix above
💰 Cost Control & Pricing Transparency
Model: pay-per-result. Every firm returned in the dataset is billed at your account tier's rate. There is no separate charge for the analysis layer, and no monthly subscription.
| Apify plan | Rate per 1,000 results | Cost per firm |
|---|---|---|
| Free (no discount) | $10.00 | $0.010 |
| Starter (bronze) | $9.33 | $0.0093 |
| Scale (silver) | $8.67 | $0.0087 |
| Business (gold) | $8.00 | $0.0080 |
Worked examples at the Free-tier rate:
| Firms returned | Cost (Free tier) | Cost (Business tier) |
|---|---|---|
| 5 firms (the default run) | $0.05 | $0.04 |
| 20 firms (free-plan cap) | $0.20 | $0.16 |
| 50 firms | $0.50 | $0.40 |
| 100 firms (per-run ceiling) | $1.00 | $0.80 |
| 1,000 firms (10 runs) | $10.00 | $8.00 |
Apify platform usage is billed on top, at your plan's rates — compute units ($0.20/CU on Free down to $0.13/CU on Business) and residential proxies ($8.00/GB on Free, $7.00/GB on Business) are the two that matter here. Deep audits raise compute-unit consumption sharply, so maxAuditPagesPerSite and maxEmailPagesPerSite affect your platform bill even though they don't change the per-result price.
Cost levers: keep maxResults tight — it is the only thing that moves the per-result charge; lower the two page-depth settings to cut compute units; and set maxTotalChargeUsd on the run endpoint as a hard per-execution ceiling.
ℹ️ The analysis layer costs nothing extra. Advanced Web Analysis is part of the per-result price on every plan, free plans included (within the 20-firm cap). It is skipped only when
maxTotalChargeUsdis set too low to cover a configured pay-per-event analysis charge, and the run still returns contacts and emails. What the analysis does change is run time and compute-unit usage — turn it off withadvanceWebAnalysis: falsewhen you only need contacts.
🔍 Companion machine-readable files
| File | Purpose |
|---|---|
.actor/actor.json | Identity, version, default run options (4096 MB, 3600s timeout), input/output/dataset wiring |
.actor/input_schema.json | Authoritative typed input contract — custom query, 6-value business type, location, maxResults (1–100), the analysis toggle, deliveryMode enum, plus the Console-hidden language, batchSize, maxEmailPagesPerSite and maxAuditPagesPerSite |
.actor/dataset_schema.json | Declares the six nested output blocks and the Console table view |
.actor/output_schema.json | Declares where results are stored |
my_actor/main.py | Query validation, Playwright listing extraction, concurrent email crawl + website audit, charge gating, intelligence generation, MCP delivery |
my_actor/connector.py | MCP connector session handling and {placeholder} rendering |
Note: this Actor has no pay_per_event.json. The Store listing is priced pay-per-result, which is why the analysis layer behaves as described under Cost Control.
If any table on this page disagrees with the schema files, the schema files win.
🛠️ Troubleshooting
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | SUCCEEDED but nothing scraped | searchQueries was not recognised as a construction search | Add a construction term, or clear it and use a preset subcategory. |
| 2 | Only 5 results | maxResults defaults to 5 | Set it explicitly — up to 100. |
| 3 | Results are General contractors in London you never asked for | subcategory and location silently defaulted | Always pass both explicitly from API / MCP / SDK calls. |
| 4 | Input rejected for maxResults above 100 | The per-run ceiling is 100 | Send 100 or less; partition bigger jobs across runs. |
| 5 | Results stop at 20 | Free Apify plan cap | Upgrade the plan; the log prints Reached free-tier limit. |
| 6 | Analysis blocks are empty | The analysis didn’t run | Confirm advanceWebAnalysis is true, and check the log for max charge limit. |
| 7 | Log: Advance Web Analysis is included at no extra charge | Normal on this per-result listing | Informational only — the analysis is running. |
| 8 | Log: event 'advanced-web-analysis' is not configured | Pay-per-event pricing is on but the event is undefined | Informational only — the analysis runs without a separate charge. |
| 9 | Log: max charge limit does not allow the premium event | maxTotalChargeUsd too low | Raise the cap and re-run if you need the analysis. |
| 10 | item["companyEmail"] is undefined | Contacts are nested under BUSINESS_PROFILE | Read item["BUSINESS_PROFILE"]["companyEmail"]. |
| 11 | companyEmail is "NA" | No email discoverable within the crawled pages | Read emailStatus first; fall back to phone. From an API/SDK/MCP call you can raise maxEmailPagesPerSite up to 25. |
| 12 | emailStatus: "no_website" | The firm has no website — nothing to crawl or audit | Expected. Analysis blocks will be thin. |
| 13 | emailStatus: "failed" | The firm's site timed out or refused the connection | Retry later; a single slow site does not fail the run. |
| 14 | Can't select two business types | subcategory is a single string | Run once per type and merge on BUSINESS_PROFILE.googleMapsUrl. |
| 15 | No licence / bonding fields | Not part of Google Maps listings | Pair with a licensing board, or GOV.UK Business Scraper for UK entities. |
| 16 | Emails bounce when you send outreach | Scraped emails are not verified | Run them through an email-verification service first. |
| 17 | googleRating won't compare numerically | It is a string ("4.7") | Cast to float before comparing. |
| 18 | Run is very slow | Up to 25 email pages + 25 audit pages per firm | Turn the analysis off. From an API/SDK/MCP call you can also lower both depths and raise batchSize. |
| 19 | Pages time out after raising batchSize | Too many parallel pages for the current network | Lower batchSize back toward the default of 4. |
| 20 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Use async: POST /runs → poll → fetch dataset. Recommended above ~20 firms. |
| 21 | Blank or empty listing fields | Google served an empty page to the container's IP | Re-run. This Actor has no proxy input — there is nothing to reconfigure. |
| 22 | Log: Saved outputs to <dir> | The Actor also writes JSON/CSV inside the container for local development | Ignore it on the platform — the dataset is the real output. |
| 23 | Connector message has blank fields | You used sibling-style placeholders | Use {name}, {email}, {rating}, {reviews}, {mapsUrl} — see the placeholder table. |
| 24 | Hundreds of connector calls / connector rate-limited | deliveryMode defaults to perLead | Switch to summary or chunked. |
| 25 | Slack / Airtable / Notion received nothing | Delivery needs both mcpConnector and mcpTool | Set both. The dataset is still written in full. |
| 26 | Log: APIFY_MCP_PROXY_URL is not set | You ran locally; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 27 | HTTP 401 / 403 | Missing, expired, or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations. |
| 28 | HTTP 429 | Apify account concurrency / rate limits — not Google blocking | Retry with exponential backoff (2s → 4s → 8s). |
Diagnostic checklist before opening an issue
- Baseline run.
subcategory=General contractor,location= a large city,maxResults=5,advanceWebAnalysis: false,deliveryMode: "none". - Results returned? If yes, the scraper is fine — re-add the analysis and volume one step at a time.
- Zero items? Read the log: an unrecognised
searchQueriesvalue ends the run before any scraping. - Analysis missing? Confirm
advanceWebAnalysisistrue, then search the log formax charge limit— a spend cap too low to cover a configured analysis charge is the only thing that disables it. - Check where you're reading fields from — contacts live under
BUSINESS_PROFILE.
If the issue survives all five steps, open an Issues ticket on the Actor page (or email support) with the run ID, the exact input JSON, and what you expected.
❓ FAQ
Setup & access
Do I need a Google account, API key, or Google Cloud project?
No. The Actor reads public Google Maps listings and public website content. You need an Apify account and API token only.
Is this the official Google Places API?
No. This is an independent Actor, not affiliated with, endorsed by, or sponsored by Google.
Can I configure a proxy?
No — this Actor has no proxy input and uses a direct connection. If you need proxy control, the any-industry sibling and Hospitality Actor expose a proxyConfiguration field.
Can I plug it into Claude, Cursor, or a LangChain agent?
Yes — it is a native MCP tool:
claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/construction-lead-intelligence-scraper"
Search & volume
Can I search a business type that isn't construction?
No. A custom query that doesn't read as construction ends the run without scraping. Use the any-industry Actor for anything else.
Can I scrape two business types in one run?
No — subcategory takes a single value. Run once per type and merge the datasets.
Can I paste a Google Maps URL?
Yes. A Google Maps place or location URL in searchQueries is accepted directly.
How many firms can one run return?
Up to 100 — the same ceiling as every sibling Actor — and 20 on free Apify plans. Note the default is only 5, so set it explicitly.
Output & data
Why is companyEmail not at the top level?
This Actor nests the contact fields inside BUSINESS_PROFILE, unlike its siblings. Read item["BUSINESS_PROFILE"]["companyEmail"].
Why are the analysis blocks empty?
Because Advanced Web Analysis didn’t run — either you set advanceWebAnalysis: false, or your maxTotalChargeUsd was too low to cover a configured analysis charge (the log says max charge limit does not allow the premium event). The blocks are still emitted, just unpopulated.
Are the emails verified?
No. They are scraped from public website content. Run them through an email-verification service before any real outreach.
Do I get tenders, permits or licence status?
No. Google Maps listings don't carry them. Pair the output with a licensing board or planning-portal source.
Do I get owner names or personal emails?
No — firm-level public addresses only (info@, office@, contact@ and similar).
Pricing
How is this billed?
Pay-per-result: every firm in the dataset costs $0.010 on the Free tier, falling to $0.0080 on Business ($10.00 → $8.00 per 1,000). Apify platform usage — compute units and residential proxies — is billed separately at your plan's rates.
Does turning the analysis on cost more?
Not per result. It doesn’t change the per-result price; it makes the run longer and the payload bigger, which raises compute-unit usage. Note that on paid plans the analysis layer does not currently run at all — see Cost Control.
How do I make a run as cheap as possible?
Keep maxResults tight — it is the only lever on the per-result charge. To cut platform usage as well, set advanceWebAnalysis: false; from an API/SDK/MCP call you can also lower maxEmailPagesPerSite and maxAuditPagesPerSite.
What happens if my spend cap is too low?
The Actor logs a warning, skips the analysis, and still returns contacts and emails. It never aborts the run over the premium layer.
🔗 Related Actors
Same data family
This Actor is the construction vertical member of the family — the only one with a nested output shape and a single-value business type. Its siblings cover other verticals, any industry, and a cheap listings-only mode.
| Actor | Best for | Why pick it over this one |
|---|---|---|
| Google Maps Business Leads & Sales Intelligence | Any industry, free-text queries, several unrelated business types in one run | Your target isn't construction, you need several types per run, or you want flat output fields and socialMedia (from $6.50 / 1,000) |
| Google Maps Scraper | Plain Google Maps listings — name, address, phone, website, rating, reviews, hours, coordinates, images | You don't need emails or sales intelligence, and want the lowest cost per result (from $0.80 / 1,000) |
| Google Maps Healthcare Leads & Sales Intelligence | Healthcare — 55 preset medical categories, multi-select | Clinics, dental, diagnostics, wellness instead of construction (from $4.00 / 1,000) |
| Google Maps Hospitality Scraper | Hotels, resorts, restaurants, cafes — 33 mapped types | A hospitality patch instead of construction ($9.09 / 1,000) |
| Finance Google Maps Lead Intelligence | 28 finance business types, deepest per-site audit | Accounting, tax, insurance, broking, lending (from $10.00 / 1,000) |
Pick by intent: contractors/architects/designers/property (this Actor) · any other industry → any-industry · listings only, cheapest → Google Maps Scraper · clinics & dental → Healthcare · hotels & restaurants → Hospitality · accounting & insurance → Finance.
ℹ️ Pricing, output shape and per-run limits differ between siblings (they are separate Store listings on separate tiers). This Actor's nested
BUSINESS_PROFILEoutput and short delivery placeholders are unique to it — check before pointing an existing pipeline here.
Enrichment & downstream pipeline
| Actor | Use it for |
|---|---|
| GOV.UK Business Scraper | Match UK contractor and developer names against Companies House records, with PSC / beneficial-ownership enrichment |
| Advanced Website Crawling Actor | Crawl a firm's whole site from BUSINESS_PROFILE.website for clean HTML/Markdown/text context beyond the audited pages |
Suggested pipeline patterns
🎯 Trades agency prospecting engine
One run per preset type in a city with advanceWebAnalysis: true → filter WEBSITE_HEALTH_SCORECARD.finalGrade ≤ C → deliveryMode: "perLead" into Airtable → work the queue from SERVICE_RECOMMENDATIONS.
💸 Fast contact harvest, then targeted analysis
Broad advanceWebAnalysis: false runs to build the contact base quickly and cheaply in compute units → shortlist by rating/reviews → re-run just the shortlist with the analysis on and a deeper audit.
🏛️ UK contractor verification
This Actor for contacts → GOV.UK Business Scraper on BUSINESS_PROFILE.businessName → attach company number, incorporation date and PSC data before outreach.
📇 CRM enrichment loop
One run per city on a schedule → perLead delivery into HubSpot → dedupe on BUSINESS_PROFILE.googleMapsUrl → verify companyEmail before any sequence.
Browse all Actors by Techforce Global at scraper.techforce.global.
🔐 Compliance & Data Privacy
This is an independent Actor. It is not affiliated with, endorsed by, or sponsored by Google. Google Maps™ is a trademark of Google LLC; all trademarks are the property of their respective owners.
This Actor collects publicly available business listing data and publicly published website content for construction and property firms. It does not log into any account, bypass authentication, or access client, contract, or project data.
Three points matter before you use the output:
- Firm contact data is still personal data in many jurisdictions — a sole-trader builder's email or mobile number identifies a person. GDPR and CCPA can apply to it.
- Cold outreach is regulated. Before sending anything to a scraped address, satisfy the applicable anti-spam and electronic-marketing rules (GDPR Art. 6/21, ePrivacy/PECR, CAN-SPAM, CASL). Emails here are unverified and unconsented.
- Nothing here is licensing or vetting data. The output carries no licence, bonding, insurance or safety-record status. Do not present a scraped listing as evidence that a contractor is licensed, insured or qualified.
You are responsible for ensuring your use complies with Google's Terms of Service and all applicable law.
🆘 Support & Custom Pipeline Engineering
Need multi-type or multi-city scheduled refreshes, extra construction business types, licensing-register enrichment, email verification in-pipeline, or a full CRM / data-warehouse integration?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global — Specialists in High-Performance Web Scrapers and Agentic Workflows.
Made with ❤️ by Techforce Global Specialists in High-Performance Lead Generation Data Extraction and AI Automation.
🏷️ Structured data for search & AI discovery
SoftwareApplication describes the Actor; FAQPage wraps the FAQ above so those answers are
eligible for rich results, matching it question-for-question.
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Construction Lead Intelligence Scraper","alternateName": "Construction Lead Intelligence Scraper — Contractor, Architect & Real Estate Leads","applicationCategory": "BusinessApplication","applicationSubCategory": "Web Scraping / Construction & Property Lead Generation","operatingSystem": "Cloud (Apify platform)","description": "Scrapes construction and property firms from Google Maps — general contractors, construction companies, architecture firms, interior designers, roofing contractors and real estate agencies — by preset business type or a construction-specific search query, and crawls each firm's own website for a public company email. Every row is a nested six-block object with contacts under BUSINESS_PROFILE, plus an optional analysis layer covering lead priority, pitch strategy, a website health scorecard, prioritized service recommendations and a technical audit. No Google account or Places API key is required. Optional per-lead, chunked or digest delivery into Airtable, HubSpot, Notion, Slack, Google Sheets or any authorized MCP connector.","url": "https://apify.com/techforce.global/construction-lead-intelligence-scraper","author": {"@type": "Organization","name": "Techforce Global","url": "https://www.techforceglobal.com"},"provider": {"@type": "Organization","name": "Apify","url": "https://apify.com"},"offers": {"@type": "Offer","price": "10.00","priceCurrency": "USD","description": "Pay-per-result: $10.00 per 1,000 results on the Free tier, falling to $8.00 per 1,000 on Business. Apify platform usage is billed separately. Current rates are shown on the Actor's Apify Store page.","url": "https://apify.com/techforce.global/construction-lead-intelligence-scraper"},"featureList": ["No Google account, Places API key or billing project required","Six preset construction and property business types selected from a dropdown","Free-text construction search queries, validated against a construction vocabulary","Google Maps place and location URLs accepted directly as the search input","Non-construction queries end the run cleanly instead of scraping the wrong market","Company email discovered by crawling each firm's own website","Email status flag so unreachable and site-less firms are distinguishable","Configurable email-discovery depth of up to 25 pages per site","Name, category, address, phone, website, rating, review count and opening hours under BUSINESS_PROFILE","Optional analysis layer: lead priority, revenue opportunity, sales angle and recommended pitch","Pitch playbook: opening hook, services to pitch, pain points, outcomes and closing strategy","Graded website health scorecard with a full grade breakdown","Prioritized service recommendations with what to fix, why it matters and the expected outcome","Website technology audit: tech stack, SSL, robots.txt, sitemap, audited pages and key issues","Configurable audit depth of up to 25 pages per site","Configurable Google Maps interface language","Configurable parallel browser pages to trade speed against memory","Per-lead, chunked or digest MCP delivery into Airtable, HubSpot, Notion, Slack, Google Sheets or any authorized connector","Chunked delivery keeps long lead lists under connector size and timeout caps","Results pushed to the dataset batch by batch, so aborted runs keep partial data"],"isAccessibleForFree": false,"softwareHelp": {"@type": "CreativeWork","url": "https://apify.com/techforce.global/construction-lead-intelligence-scraper"}}
{"@context": "https://schema.org","@type": "FAQPage","mainEntity": [{"@type": "Question","name": "Do I need a Google account, API key, or Google Cloud project?","acceptedAnswer": {"@type": "Answer","text": "No. The Actor reads public Google Maps listings and public website content. You need an Apify account and API token only."}},{"@type": "Question","name": "Is this the official Google Places API?","acceptedAnswer": {"@type": "Answer","text": "No. This is an independent Actor, not affiliated with, endorsed by, or sponsored by Google."}},{"@type": "Question","name": "Can I configure a proxy?","acceptedAnswer": {"@type": "Answer","text": "No - this Actor has no proxy input and uses a direct connection. If you need proxy control, the any-industry sibling at techforce.global/google-maps-leads-sales-intelligence-tool and the Hospitality Actor at techforce.global/google-maps-hospitality-lead-scraper expose a proxyConfiguration field."}},{"@type": "Question","name": "Can I plug it into Claude, Cursor, or a LangChain agent?","acceptedAnswer": {"@type": "Answer","text": "Yes - it is a native MCP tool. Add it with: claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/construction-lead-intelligence-scraper\""}},{"@type": "Question","name": "Can I search a business type that isn't construction?","acceptedAnswer": {"@type": "Answer","text": "No. A custom query that doesn't read as construction ends the run without scraping. Use the any-industry Actor at techforce.global/google-maps-leads-sales-intelligence-tool for anything else."}},{"@type": "Question","name": "Can I scrape two business types in one run?","acceptedAnswer": {"@type": "Answer","text": "No - subcategory takes a single value. Run once per type and merge the datasets."}},{"@type": "Question","name": "Can I paste a Google Maps URL?","acceptedAnswer": {"@type": "Answer","text": "Yes. A Google Maps place or location URL in searchQueries is accepted directly."}},{"@type": "Question","name": "How many firms can one run return?","acceptedAnswer": {"@type": "Answer","text": "Up to 100 - the same ceiling as every sibling Actor - and 20 on free Apify plans. Note the default is only 5, so set it explicitly."}},{"@type": "Question","name": "Why is companyEmail not at the top level?","acceptedAnswer": {"@type": "Answer","text": "This Actor nests the contact fields inside BUSINESS_PROFILE, unlike its siblings. Read item[\"BUSINESS_PROFILE\"][\"companyEmail\"]."}},{"@type": "Question","name": "Why are the analysis blocks empty?","acceptedAnswer": {"@type": "Answer","text": "Because Advanced Web Analysis did not run - either you set advanceWebAnalysis to false, or your maxTotalChargeUsd was too low to cover a configured analysis charge (the log says max charge limit does not allow the premium event). The blocks are still emitted, just unpopulated."}},{"@type": "Question","name": "Are the emails verified?","acceptedAnswer": {"@type": "Answer","text": "No. They are scraped from public website content. Run them through an email-verification service before any real outreach."}},{"@type": "Question","name": "Do I get tenders, permits or licence status?","acceptedAnswer": {"@type": "Answer","text": "No. Google Maps listings don't carry them. Pair the output with a licensing board or planning-portal source."}},{"@type": "Question","name": "Do I get owner names or personal emails?","acceptedAnswer": {"@type": "Answer","text": "No - firm-level public addresses only, such as info@, office@ and contact@."}},{"@type": "Question","name": "How is this billed?","acceptedAnswer": {"@type": "Answer","text": "Pay-per-result: every firm in the dataset costs $0.010 on the Free tier, falling to $0.0080 on Business ($10.00 to $8.00 per 1,000). Apify platform usage - compute units and residential proxies - is billed separately at your plan's rates."}},{"@type": "Question","name": "Does turning the analysis on cost more?","acceptedAnswer": {"@type": "Answer","text": "Not per result. It doesn't change the per-result price; it makes the run longer and the payload bigger, which raises compute-unit usage. Note that on paid plans the analysis layer does not currently run at all - see Cost Control."}},{"@type": "Question","name": "How do I make a run as cheap as possible?","acceptedAnswer": {"@type": "Answer","text": "Keep maxResults tight - it is the only lever on the per-result charge. To cut platform usage as well, set advanceWebAnalysis to false; from an API/SDK/MCP call you can also lower maxEmailPagesPerSite and maxAuditPagesPerSite."}},{"@type": "Question","name": "What happens if my spend cap is too low?","acceptedAnswer": {"@type": "Answer","text": "The Actor logs a warning, skips the analysis, and still returns contacts and emails. It never aborts the run over the premium layer."}}]}