Construction Lead Intelligence Scraper
Pricing
from $8.00 / 1,000 results
Construction Lead Intelligence Scraper
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
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
Construction Lead Intelligence Scraper — Contractor, Architect & Real Estate Leads with Emails, Website Grades & 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) |
⚠️ Verify before publishing: the ~60-results-per-text-query ceiling and the absence of an email field are long-standing Places API behaviours, but re-confirm both against Google's current Places API docs before relying on this row.
🤖 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."known_issue: "The code gates Advanced Web Analysis behind pay-per-EVENT pricing, so on this pay-per-RESULT listing paid runs currently return contacts only - see the Pricing section."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"
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 — up to 25 pages per site if you raise
maxEmailPagesPerSite(default 4). 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. If the analysis cannot be billed as a pay-per-event charge, the Actor logs a warning, disables the analysis, and still returns contacts — it never aborts the run over it. ⚠️ On the current pay-per-result listing this means paid runs return contacts only; see Cost Control.
- 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 can silently switch itself off. The code requires pay-per-event pricing plus a configured
advanced-web-analysisevent plus a permissivemaxTotalChargeUsd. This listing is priced pay-per-result, so for paying users the first check fails and the run continues on contacts only, with a warning in the log. Free-tier runs skip the check and do get the analysis. ReadadvancedWebAnalysisin the output to know which mode you actually got. - 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. maxEmailPagesPerSiteandmaxAuditPagesPerSiteboth go up to 25. They are the two biggest run-time levers; defaults are 4 and 6.- 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 requires the 'advanced-web-analysis' event configured under pay-per-EVENT pricing"on_violation: "analysis is disabled with a warning; contacts and emails are still returned"live_effect: "this listing is pay-per-RESULT, so paid runs currently hit this path"- rule: "free Apify plans get the analysis but are capped at 20 companies"note: "the free-tier branch returns before the pricing check"- 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 | en |
batchSize | integer | ⬜ No | 4 | 1–20 parallel browser pages | 8 |
maxEmailPagesPerSite | integer | ⬜ No | 4 | 1–25 pages crawled for email/social | 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) | 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:
{"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_NOT_PRICED | Log not running with pay-per-event pricing | The listing is pay-per-result, but the code requires pay-per-event — the expected path on paid runs today | Not a run failure. Consume contacts; the Actor owner must relax the gate (see Pricing). |
ANALYSIS_EVENT_MISSING | Log event 'advanced-web-analysis' is not configured | The event isn't defined in Apify Console | Not a run failure. Consume contacts; ask the owner to add the event. |
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 | The analysis did not run (any of the three reasons above, or advanceWebAnalysis: false) | Check advancedWebAnalysis / the 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. |
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: lower both, raise batchSize, or turn the analysis off. |
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. |
⚠️ Verify before publishing: the HTTP status rows reflect standard Apify API behavior; every log-line and field condition above is taken from this Actor's source. Re-confirm the
408/429rows against current platform behavior if you depend on them for automated retry logic.
🗣️ 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} |
| "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│ └─ KNOWN ISSUE: the code needs pay-per-EVENT pricing, but this listing is│ pay-per-RESULT, so paid runs return contacts only (free-tier runs do get it)└── NO → advanceWebAnalysis: false (same per-result price, faster run, smaller payload)▼8. Tune depth vs speed: 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.
🐞 Known issue — the analysis is gated on the wrong pricing model. my_actor/main.py requires
pricing_info.is_pay_per_eventbefore enabling the premium layer, but this listing is configured pay-per-result. The practical effect today: free-tier runs get the full analysis (that branch returns before the pricing check) while paying customers get contacts only, withnot running with pay-per-event pricingin the log — the opposite of the intended behaviour. Two ways out: (a) drop the pay-per-event gate so the analysis is simply part of the per-result price, or (b) switch the Store listing to pay-per-event with anadvanced-web-analysisevent. Until one of those is done, treat the analysis blocks as free-tier-only.
🔍 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), email/audit depths, batchSize, the analysis toggle, deliveryMode enum |
.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 configured pay-per-result, while the code still gates the analysis layer on pay-per-event pricing — see the Known issue 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 | Check the log for the three billing reasons, and that advanceWebAnalysis is true. |
| 7 | Log: not running with pay-per-event pricing | Expected on this listing — it is priced pay-per-result while the code requires pay-per-event | Owner action: remove the gate, or switch the listing to PPE. Contacts are still returned. |
| 8 | Log: event 'advanced-web-analysis' is not configured | The billable event is missing in Console | Owner action: add the event. Contacts still returned. |
| 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; raise maxEmailPagesPerSite (up to 25); fall back to phone. |
| 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 | Lower both depths, raise batchSize, or turn the analysis off. |
| 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? Search the log for
pay-per-event,not configured, ormax charge limit— those three lines explain every silent disable. - 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 the Actor disabled it for a billing reason (see the three log lines in Troubleshooting). 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. See the Known issue below for why paid runs currently don't get the analysis at all.
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 and lower maxEmailPagesPerSite / 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 apify.com/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
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Construction Lead Intelligence Scraper — Contractor, Architect & Real Estate Leads","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes construction and property firms from Google Maps by preset business type or a construction search query, finds the company email on each firm's website, and optionally adds lead priority, pitch strategy, a website performance grade, prioritized improvement recommendations and a technical audit — with optional delivery to 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": "Per 1,000 results on the Free tier, falling to $8.00 per 1,000 on Business"}}