Houzz Scraper — Contractor Leads & Emails
Pricing
from $6.00 / 1,000 results
Houzz Scraper — Contractor Leads & Emails
Collect Houzz professionals and businesses by category and city: contractors, designers, architects and more. Get names, phones, emails, websites, addresses, ratings, reviews, services and social profiles as structured JSON streamed to your dataset in real time. Free accounts get a small sample.
Pricing
from $6.00 / 1,000 results
Rating
0.0
(0)
Developer
Emmanuel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Houzz Real-Time Scraper
Turn the Houzz professional directory into a clean, contactable lead list — contractors, designers, architects, remodelers, builders and every other trade Houzz lists — as flat, structured JSON that streams into your dataset while the run is still going.
Search many categories across many cities in a single run. Every row arrives with the business name, category, rating and review count, phone, website, full address and coordinates, services offered, service areas, badges, licence details and project count — plus, with Enable lead details turned on, the contact email addresses and social profiles published on the professional's own website.
Paid plan required for full data. This Actor is monetized. Free Apify accounts receive a small capped sample (2 results by default) so you can see the output shape before you buy. Paying accounts get the full, uncapped output and stream unlimited rows to the dataset. See Free accounts vs paid accounts.
Table of contents
- What it does
- Free accounts vs paid accounts
- Outcomes you get
- Who it is for
- Use cases
- Quick start
- Input
- Output
- Webhook setup
- MCP usage
- Integrations
- Performance & run length
- Pricing & charging
- Responsible use
- FAQ
- Troubleshooting
- Support
What it does
Houzz hosts a directory of home-improvement and design professionals organised by trade and by city. This Actor collects that directory into your own dataset, in your own account, as flat JSON that any spreadsheet, CRM or database can take.
The four features:
| Feature | What it collects |
|---|---|
| Search | Professionals and businesses for any category + location combination you supply. Enabled by default. |
| Enable lead details | Adds extra lead data to every row: the contact fields published on the professional's full record, plus public contact emails and social profiles listed on the professional's own website. Enabled by default. |
| Professional Details | Full structured records for Houzz links you already hold — a professional's link, or a directory listing link (which expands into every professional it holds). |
| Reviews | The reviews published on a professional's own record, exported as their own rows tagged featureType: "reviews". |
Everything streams. Each row is written to the dataset the moment it is ready, so memory stays low on very large runs and your output grows live.
Free accounts vs paid accounts
This Actor is monetized, and it is transparent about what that means:
| Account | What happens |
|---|---|
| Paid Apify plan (Bronze and above) | Full output, no caps, every enabled feature. The run logs Paying user — full output. once at the start. |
| Free Apify plan | The run is capped to 2 result rows per run (owner-configurable). It finishes quickly and cleanly, logs a clear upgrade notice, and writes the same paywall summary into the run output. |
Important details:
- Your free-tier run never fails and never looks broken. It exports its 2 rows, exits with the status message “Free tier limit reached — results were capped. Upgrade to a paid Apify plan for full, unlimited data.” and is fully visible in the run log.
- The exact state of the gate is in the run output, not only in the logs, under the
paywallkey:detected,isPaying,pricingTier,blocked,limited,freeTierMaxItems. - Every row is charged per successful result. Because every row is charged, the Actor also honours your maximum cost per run: when your spending limit is reached, collection stops immediately and the run finishes gracefully with
stoppedReason: "spending_limit". It never keeps collecting work you are not paying for. - The Actor owner can switch the free-account behaviour between limit (small sample, the default) and block (no rows at all) without any code change.
Outcomes you get
- A ready-to-contact lead list per city and trade. Business name, category, phone, website, email addresses, social profiles, address with coordinates.
- Market sizing in one run. How many contractors, designers or architects a category really holds in each city, and how they rank for reviews.
- Qualification signals built in. Rating, review count, badge list (
Best of Houzzwinner,Locally owned,Woman owned,Responds Quickly,Free consultation,Provides 3D Visualization, years in business), verified/licence flags, licence details, project count, cost estimate, and the featured review text. - Service coverage mapping. Each professional's own list of
servicesandserviceAreas, so you can see who actually works where you need them. - Repeatable output. Stable field names, one row per professional, deterministic feature tags — schedule it and the table keeps the same shape.
- Live delivery. Rows stream to the dataset and, optionally, to your own webhook as they are collected.
Who it is for
- Home-improvement lead sellers and marketing agencies building prospecting lists of remodelers, roofers, flooring and HVAC contractors by metro.
- Contractor-focused SaaS and CRM teams (field service software, estimating tools, review platforms) sourcing their first customers by trade and city.
- Materials and equipment brands (windows, cabinetry, countertops, tile, roofing, pools) mapping the installers and showrooms in a territory.
- B2B data providers enriching their own directory with services, service areas, licence details, badges and contact addresses.
- Recruiters for the trades and design industries finding firms that hire by location and speciality.
- Real-estate and proptech teams who need vetted, reviewed local renovation partners by postcode.
- Market researchers and analysts benchmarking local trade markets: review counts, rating distributions, badge wins, service breadth.
- Sales operations and RevOps feeding a CRM with a weekly refreshed, deduplicated professional list per territory.
- Franchise and dealership development teams finding operators in expansion cities.
- Insurance, warranty and inspection networks mapping qualified local professionals with verifiable details.
Use cases
Lead generation and cold outreach
- Build a city-by-city contractor list for a new territory launch.
- Pull every interior designer in five metros for a design-software outbound campaign.
- Collect architects with their websites and public contact addresses for a BIM tool trial push.
- Find recently badged
Best of Houzzwinners to target with a premium positioning offer. - Target
Woman ownedandLocally ownedbusinesses for a community-focused programme. - Seasonal campaigns — roofers and gutter specialists before storm season, pool builders before summer, HVAC before winter.
- Find high-review-count firms (a proxy for an established business with a marketing budget).
- Reach contractors who advertise
Free consultationorResponds Quickly.
Territory and market analysis
9. Size the same trade across 20 cities to prioritise expansion.
10. Measure competitive density: professionals per category per city.
11. Benchmark review counts and ratings against the local median.
12. Track who holds the newest Best of Houzz badges each year.
13. Map service areas to find white space where nobody covers a suburb.
14. Compare service breadth (number of listed services) across firms in a trade.
15. Identify multi-project, high-volume firms via project counts.
Enrichment and data quality 16. Enrich an existing contractor list with phone numbers and websites. 17. Fill in missing contact emails from publicly listed addresses. 18. Normalise messy CRM addresses against structured address fields and coordinates. 19. Re-verify stale records — refresh rating, review count, badges and status. 20. De-duplicate a list using the professional identifier and profile link.
CRM, marketing and operations 21. Stream new professionals straight into HubSpot, Salesforce or Pipedrive with a webhook. 22. Feed a Google Sheet or Airtable that a sales team works from daily. 23. Post high-value new leads to a Slack channel in real time. 24. Trigger a Zapier or Make automation that sends a personalised first-touch email. 25. Keep a BigQuery or Snowflake warehouse table of the local trade market. 26. Build a ranking / leaderboard of top-rated professionals per city for content marketing.
Product and research 27. Seed a marketplace with real professionals on day one. 28. Power a “find a pro near me” directory feature. 29. Train or evaluate a matching model on services, service areas and locations. 30. Monitor a category over time to detect market entry and exit.
Partnerships and recruiting 31. Find complementary businesses (interior designers + architects + builders) for a referral network. 32. Identify potential channel partners with a strong review presence. 33. Source trade firms hiring in a region for a recruiting marketplace. 34. Build a vendor shortlist for a renovation programme. 35. Find local suppliers and showrooms for a trade-network programme.
Quick start
- Open the Actor in Apify Console.
- Leave Search enabled and fill the prefilled row: a Category and a Location.
- Leave Enable lead details enabled so contacts are included.
- Click Start. Rows appear in the Output tab as they are collected.
- Download the dataset as JSON, CSV or Excel — or connect a webhook.
A two-minute starter run:
{"enableSearch": true,"enableLeadDetails": true,"searchTasks": [{ "query": "general-contractor", "location": "Austin, TX" }],"maxResultsPerTask": 10,"maxItems": 10}
Input
Every field is optional. Sensible defaults are prefilled in the Console, so a first run needs no changes at all.
Categories & locations
Search runs one collection per search task. Add a row for each category + location pair; add as many rows as you like and they are worked on together.
| Field | Type | Default | Description | Example |
|---|---|---|---|---|
enableSearch | boolean | true | Search the professional directory by category and location. | true |
searchTasks[].query | string | — | Required per row. The professional category. Common values: general-contractor, interior-designer, architect, kitchen-and-bath-remodeler, bathroom-remodeler, home-builder, design-build-firm, landscape-architect, landscape-contractor, roofing-contractor, flooring-contractor, hvac-contractor, plumber, electrician, painter, pool-builder, cabinet-maker, countertop-contractor, window-contractor, solar-energy-contractor, home-stager, home-inspector, real-estate-agent, cleaning-service, moving-company, pest-control. | "general-contractor" |
searchTasks[].location | string | empty | City or area for this category. Use "City, ST" for best precision. Leave empty for a nationwide search. | "Austin, TX" |
searchTasks[].maxResults | integer (≥1) | maxResultsPerTask | Maximum professionals for this one task. | 25 |
maxResultsPerTask | integer (≥1) | 10 | Default maximum professionals per task. A row's own maxResults overrides it. | 50 |
maxResultSetsPerTask | integer (1–50) | 8 | Depth limit per task. Each batch holds up to 15 professionals, so the default is roughly 120 per task. Raise it only for very large jobs — a run always stops as soon as maxItems is reached. | 12 |
Tips
- Run one row with a big
maxResultsfor deep coverage of a single trade, or many rows with a small one for breadth across cities. - Category text is flexible:
"kitchen and bath remodeler"and"kitchen-and-bath-remodeler"resolve to the same category. "New York, NY","new-york-ny"and"New-York--NY"are all understood.- Multi-word cities keep their precision —
"Los Angeles, CA"is treated as one location.
Lead details
| Field | Type | Default | Description |
|---|---|---|---|
enableLeadDetails | boolean | true | Collect extra lead details for every professional: the contact fields published on the professional's full record, plus public contact addresses and social profiles listed on the professional's own website. Adds a little extra time per professional. |
leadDetailsSiteLookups | integer (1–8) | 3 | How many extra contact sections to check on each professional's own website while looking for an email address (contact, about, team, quote). More lookups mean more chances to find an address and a slightly longer run. Set to 1 for the fastest runs. |
No filtering — ever. Every professional found is exported, whether or not lead details could be found for them. Rows with no email simply come back with "email": null and "emails": []. This is deliberate: it keeps the output complete and the run length predictable, which is what makes per-result pricing possible. Use the leadDetailsComplete and emails columns to sort in your own tooling afterwards.
Professional details
| Field | Type | Default | Description |
|---|---|---|---|
enableProfileDetails | boolean | false | Collect full structured records for Houzz links you already hold. |
profileUrls | array of string | [] | Professional profile links or directory listing links. A listing link expands into every professional it holds. |
Rows from this feature are tagged featureType: "provider_details".
Reviews
| Field | Type | Default | Description |
|---|---|---|---|
enableReviews | boolean | false | Export the reviews published on a professional's own record as their own rows. |
reviewProfileUrls | array of string | [] | Professional links to read reviews from. Falls back to profileUrls when left empty. |
Review rows are tagged featureType: "reviews" and carry reviewId, proName, authorName, text and projectDescription (either Featured review or Most recent review).
Limits & output
| Field | Type | Default | Description |
|---|---|---|---|
maxItems | integer (≥1) | 10 | Global cap on total rows for the whole run, across every task and feature. Raise it for large jobs (1000, 10000, …). Free Apify accounts are capped to a small sample per run regardless of this value. |
webhookUrl | string | "" | Optional. Every row is always saved to the dataset — this webhook is an additional real-time push. See Webhook setup. |
webhookFormat | json | slack | "json" | json sends the full record object; slack sends a Slack-friendly message payload. |
Webhooks
| Field | Type | Default | Description |
|---|---|---|---|
webhookUrl | string | "" | Each new row is POSTed to this URL as soon as it is collected. Works with a CRM webhook, a Slack incoming webhook, Zapier, Make, n8n and Google Sheets. |
webhookFormat | json | slack | "json" | Payload shape. |
Delivery is best-effort by design: a webhook failure never stops the run and never blocks the dataset write. Failed deliveries are counted and reported in the run log and in the summary.
Connection
| Field | Type | Default | Description |
|---|---|---|---|
proxyConfiguration | proxy object | US residential | A US residential connection is enabled by default for reliable collection. Change it here only if you need a different country or your own proxy URLs. |
The Actor owner may also configure an owner-side connection pool (an Apify environment variable, never visible in the input or in any output). When it is present it takes priority; otherwise the proxy chosen in this dropdown is used.
Full input reference
{"enableSearch": true,"searchTasks": [{ "query": "general-contractor", "location": "Austin, TX", "maxResults": 10 },{ "query": "interior-designer", "location": "Dallas, TX", "maxResults": 10 }],"maxResultsPerTask": 10,"maxResultSetsPerTask": 8,"enableLeadDetails": true,"leadDetailsSiteLookups": 3,"enableProfileDetails": false,"profileUrls": [],"enableReviews": false,"reviewProfileUrls": [],"maxItems": 10,"webhookUrl": "","webhookFormat": "json","proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "US"}}
Output
Rows land in the run's default dataset. Every row carries featureType so you can filter:
"search"— from category + location search."provider_details"— from the Houzz links you supplied."reviews"— review rows.
Three dataset views ship with the Actor: Overview, Leads (contactable rows) and Reviews.
Professional rows
| Field | Type | Description |
|---|---|---|
featureType | string | search or provider_details. |
name | string | null | Business or professional name. |
proId | string | null | Professional identifier on the source directory. Stable across runs — the best de-duplication key. |
profileUrl | string | null | Link to the professional's own record. |
category | string | null | Primary category, e.g. General Contractors. |
contactName | string | null | Named contact person when the record publishes one. |
phone | string | null | Primary phone number, formatted as published. |
phones | string[] | All distinct phone numbers found, best first (max 3). |
website | string | null | The professional's own website. |
email | string | null | First contact address found — convenience copy of emails[0]. |
emails | string[] | Public contact addresses found when Enable lead details is on. Empty when none were published. |
address | object | null | { street, city, state, zipCode, country }. |
location | string | null | Flattened address for one-column viewing, e.g. 100 Congress Ave, Austin, TX, 78701. |
city | string | null | City. |
state | string | null | State or region. |
zipCode | string | null | Postal code. |
country | string | null | Country. |
latitude | number | null | Latitude. |
longitude | number | null | Longitude. |
rating | number | null | Average rating, 0–5. |
reviewCount | integer | null | Number of reviews. |
description | string | null | The professional's own description / about text. |
thumbnail | string | null | Profile image URL. |
images | string[] | Additional image URLs when published. |
socials | object | null | { facebook, instagram, twitter, linkedin, youtube, pinterest, tiktok }. |
linkedinUrl | string | null | Convenience copy of socials.linkedin. |
facebookUrl | string | null | Convenience copy of socials.facebook. |
instagramUrl | string | null | Convenience copy of socials.instagram. |
twitterUrl | string | null | Convenience copy of socials.twitter (X / Twitter). |
badges | string[] | Earned badges, e.g. Best of Houzz 2026 - Client Satisfaction, Locally owned, Woman owned, Responds Quickly, Provides 3D Visualization, 25 Ideabook Saves. |
standouts | string[] | Highlighted profile badges. |
merits | string[] | Merit badges. |
services | string[] | Services the professional offers, e.g. Kitchen Remodeling, Home Additions. |
serviceAreas | string[] | Areas the professional serves. |
licenseNumber | string | null | Licence number, when published. |
licenseInfo | object | null | { number, type, issuedBy, businessName, status }. |
verified | boolean | null | Whether the professional is verified on the source directory. |
licenseVerified | boolean | null | Whether the licence is verified. |
costEstimate | string | null | Published cost estimate for their work. |
awards | string[] | Awards listed on the record. |
projectCount | integer | null | Number of projects on the professional's record. |
status | string | null | active or inactive. |
mostRecentReview | object | null | { text, author } of the most recent review. |
featuredReview | object | null | { text, author } of the featured review. |
leadDetailsComplete | boolean | null | true once the lead-details pass finished for this row. Rows are never filtered on this. |
searchQuery | string | null | The category that produced this row. |
searchLocation | string | null | The location that produced this row. |
searchTaskIndex | integer | null | 0-based index of the search task that produced this row. |
searchTaskLabel | string | null | Human-readable task label, e.g. general-contractor | Austin, TX. |
position | integer | null | Position in the results for that task. |
scrapedAt | string | ISO-8601 timestamp of collection. |
Review rows
| Field | Type | Description |
|---|---|---|
featureType | string | Always reviews. |
reviewId | string | null | Stable identifier — featured_<proId> or recent_<proId>. |
proId | string | null | Professional identifier. |
proName | string | null | Professional name. |
profileUrl | string | null | Link to the professional's record. |
authorName | string | null | Reviewer display name. |
rating | number | null | Star rating when published with the review. |
text | string | null | Review text. |
publishedAt | string | null | Publication date when published. |
projectDescription | string | null | Featured review or Most recent review. |
ownerResponse | string | null | Owner's reply when published. |
scrapedAt | string | ISO-8601 timestamp of collection. |
Run summary
The same fields are written to the run's key-value store under the OUTPUT key and shown in the run's Output tab.
{"totalPushed": 240,"spendingLimitReached": false,"stoppedReason": "max_items","paywall": {"detected": true,"isPaying": true,"pricingTier": "BRONZE","blocked": false,"limited": false,"freeTierMaxItems": null},"errors": []}
| Field | Type | Description |
|---|---|---|
totalPushed | integer | Number of rows written to the dataset in this run. |
spendingLimitReached | boolean | true when your maximum cost per run was reached and collection stopped early. |
stoppedReason | string | completed, max_items, spending_limit, free_tier_limit or free_tier_blocked. |
paywall.detected | boolean | Whether the platform reported your account status. false for local runs. |
paywall.isPaying | boolean | Whether this run was treated as a paid account. |
paywall.pricingTier | string | null | Your Apify pricing tier, e.g. BRONZE, GOLD, FREE. |
paywall.blocked | boolean | true when a free account was blocked instead of limited. |
paywall.limited | boolean | true when free-tier caps were applied. |
paywall.freeTierMaxItems | integer | null | The free-tier cap in force, when one applied. |
errors | string[] | Fixed, user-facing problem descriptions. Never contains internal or connection details. |
Example rows
A professional row (trimmed):
{"featureType": "search","name": "Keith Wing Custom Builders","proId": "1171058","profileUrl": "https://www.houzz.com/professionals/general-contractors/keith-wing-custom-builders-pfvwus-pf~1171058","category": "General Contractors","contactName": "Keith Wing Custom Builders, LLC","phone": "(830) 266-9464","phones": ["(830) 266-9464"],"website": "https://keithwing.com/","email": "keith@keithwing.com","emails": ["keith@keithwing.com", "sales@keithwing.com"],"address": {"street": null,"city": "New Braunfels","state": "TX","zipCode": "78132","country": "US"},"location": "New Braunfels, TX, 78132","city": "New Braunfels","state": "TX","zipCode": "78132","country": "US","latitude": 29.7565,"longitude": -98.1983,"rating": 4.9,"reviewCount": 33,"description": "Since 2014, Keith Wing Custom Builders has been designing and building...","thumbnail": "https://st.hzcdn.com/simgs/895335fe06b3c811_0-7858/_.jpg","socials": {"facebook": "https://www.facebook.com/keithwingcustombuilders/","linkedin": "https://www.linkedin.com/in/keith-wing-2b845984"},"badges": ["Best of Houzz 2026 - Client Satisfaction", "Best of Houzz 2025 - Client Satisfaction", "Recommended on Houzz"],"services": ["Custom Homes", "Home Building"],"serviceAreas": ["New Braunfels", "San Antonio", "Boerne", "Bulverde"],"licenseNumber": null,"verified": null,"costEstimate": null,"awards": [],"projectCount": 54,"status": "active","mostRecentReview": { "text": "Great experience from start to finish.", "author": "Homeowner" },"featuredReview": { "text": "They built our dream home.", "author": "Sarah M." },"leadDetailsComplete": true,"searchQuery": "general-contractor","searchLocation": "Austin, TX","searchTaskIndex": 0,"searchTaskLabel": "general-contractor | Austin, TX","position": 4,"scrapedAt": "2026-09-24T16:20:51.877Z"}
A review row:
{"featureType": "reviews","reviewId": "featured_1171058","proId": "1171058","proName": "Keith Wing Custom Builders","profileUrl": "https://www.houzz.com/professionals/general-contractors/keith-wing-custom-builders-pfvwus-pf~1171058","authorName": "Sarah M.","rating": null,"text": "They built our dream home.","publishedAt": null,"projectDescription": "Featured review","ownerResponse": null,"scrapedAt": "2026-09-24T16:20:51.877Z"}
Webhook setup
A webhook lets you push each row to your own system as it is collected, instead of waiting for the run to finish and downloading a file. The dataset always receives every row too — the webhook is an additional copy, so a webhook outage never costs you data.
To enable it:
- Put your receiving URL in Webhook URL.
- Choose Webhook format:
- JSON (full record) — the complete row object, identical to the dataset row.
- Slack message — a short, human-readable message payload.
- Start the run.
JSON payload — exactly the row documented in Output, posted with Content-Type: application/json:
{"featureType": "search","name": "DNA Builds","proId": "7429615","category": "General Contractors","phone": "(737) 355-7635","email": "contact@dnabuilds.com","emails": ["contact@dnabuilds.com"],"website": "http://www.dnabuilds.com","location": "209 Ottaviano Way, Austin, TX, 78757","city": "Austin","state": "TX","zipCode": "78757","country": "US","rating": 5,"reviewCount": 11,"searchTaskLabel": "general-contractor | Austin, TX","scrapedAt": "2026-09-24T16:20:51.878Z"}
Slack payload:
{"text": ":house: *Bes Builder*\n*Category:* General Contractors • *Rating:* 5 (4 reviews)\n*Phone:* (512) 856-9873\n3301 Northland Dr, Ste 315, Austin, TX, 78731\n<https://www.houzz.com/professionals/...|View profile>"}
What a Slack incoming webhook expects. Paste the Slack incoming-webhook URL as the Webhook URL and select Slack message. Each row posts as one message.
Delivery behaviour
- One POST per row, sent immediately after the row is written to the dataset.
- A 15-second timeout per delivery, with one automatic retry.
- A non-success response is logged as a delivery failure and counted in the run summary — the run continues.
- Payloads contain only the row's own output fields. They never contain connection details, credentials, or internal processing data.
Helpful patterns
- Signature/verification: in the receiving system, allow POSTs from Apify's IP range, or use a secret path segment in the URL.
- Ordering: rows are delivered as they finish; several can be in flight at once, so sort by
scrapedAtorpositionin your tooling. - Idempotency: key on
proId(orreviewIdfor review rows) — it is stable across runs.
MCP usage
This Actor can be driven from an MCP client (Claude, Cursor, and any other MCP-speaking assistant) through Apify's MCP server, so you can ask for a lead list in plain language and get the dataset back.
Server URL
https://mcp.apify.com/?actors=houzz-real-time-scraper
Claude Desktop / Cursor configuration
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?actors=houzz-real-time-scraper","headers": {"Authorization": "Bearer <YOUR_APIFY_API_TOKEN>"}}}}
Example prompts
- “Use the Houzz Real-Time Scraper to find general contractors in Austin, TX and give me the top 20 by rating.”
- “Get every interior designer in Dallas, TX with their website and contact email.”
- “Build me a list of roofing contractors across Houston, Dallas and San Antonio, 25 each.”
- “Find kitchen and bath remodelers in Miami, FL that have at least 20 reviews.”
- “Which architects in Seattle, WA won a Best of Houzz badge and what services do they list?”
Notes
- Free Apify accounts receive the capped sample through MCP exactly as in the Console.
- A large job costs the same per row as it does anywhere else, and still honours your maximum cost per run.
- Pass
maxItemsin the call when you want a hard ceiling on a single job.
Integrations
The dataset is a normal Apify dataset, so anything that reads one works:
| Destination | How |
|---|---|
| Google Sheets | Apify's Google Sheets integration, or a webhook to a Sheet-bound Apps Script. |
| Airtable | Airtable automation on a webhook URL, or the Apify Airtable integration. |
| HubSpot / Salesforce / Pipedrive | Webhook to your CRM URL, or the Apify integrations. |
| Zapier / Make / n8n | Use Webhook URL; each row triggers the automation immediately. |
| Slack / Discord / Teams | Incoming webhook with Webhook format: Slack message. |
| BigQuery / Snowflake / Postgres | Export the dataset, or load it with the Apify API. |
| S3 / Google Cloud Storage | Configure a dataset export to your bucket. |
| Your own backend | Read the dataset with the Apify API, or receive rows on a webhook. |
Performance & run length
- Rows stream out as they are collected, so memory stays light and very large runs are practical at the default 512 MB.
- Bounded parallelism is used throughout. Several search tasks, several result batches per task, and several lead-details lookups all run together, which is why a full run finishes in minutes rather than hours.
- Run length is proportional to
maxItemsand the lead-details setting, and it is predictable — which is what makes per-result pricing fair. - A whole-run time budget for the lead-details phase (owner-configurable, 15 minutes by default) guarantees the run finishes. When the budget is spent, every remaining professional is still exported — just with the details already collected. The run logs a clear warning when that happens.
- Tuning knobs:
maxItems(hard ceiling),maxResultSetsPerTask(depth per task),leadDetailsSiteLookups(1is fastest), andmaxResultsPerTask. - The Actor's own run timeout is set to 10,000 seconds, far beyond any normal run, so long jobs are never cut off by a timeout.
Pricing & charging
- Billing is per successful result row written to the dataset.
- The Actor honours the maximum cost you set for a single run. When that ceiling is reached, it stops immediately, finishes gracefully, and reports
stoppedReason: "spending_limit"— it never continues working you are not paying for. - You can preview a run's cost before starting by setting a low
maxItems. - Free accounts are capped to a small sample (2 rows by default) and are told how to upgrade.
Responsible use
- The Actor collects business contact information that is published for the purpose of being contacted, and it is intended for legitimate B2B outreach, market research and enrichment.
- You are responsible for complying with the laws and regulations that apply to you — including anti-spam and data-protection rules such as GDPR, CAN-SPAM and PECR — and with the terms of any site you interact with.
- Use sensible volumes and honour opt-out requests. Add a clear unsubscribe route to any outreach you send.
- Keep your own scraping reasonable: prefer a few larger scheduled runs over constant small ones.
FAQ
Is this Actor free? No. It is monetized and charged per result row. Free Apify accounts receive a capped sample of 2 rows by default and are shown a clear upgrade notice — that is a deliberate product limit, not an error. Paying accounts get the full, uncapped output.
How do I get the full, unlimited data?
Upgrade to any paid Apify plan. The Actor detects your plan automatically at the start of every run; paid runs log Paying user — full output. once and run with no caps.
How many results can I get in one run?
As many as you like, subject to maxItems. The default per-task depth is roughly 120 professionals; raise maxResultSetsPerTask and maxResultsPerTask for deeper coverage.
Why does the run stop at 2 results on my free account? That is the free-tier cap. See Free accounts vs paid accounts.
Do results without an email get dropped?
Never. Every professional found is exported. Rows with no findable contact details simply have email: null, emails: []. Filter in your own tooling if you want to.
How long does it take?
Usually minutes. It scales with how many tasks you define and how much maxItems you ask for. The lead-details phase has a guaranteed time budget so a run always finishes.
Can I run several categories and cities at once? Yes — that is the intended pattern. Add one Search task row per category + location pair.
Can I collect more than 15 professionals for a category?
Yes. 15 is a single batch. maxResultSetsPerTask controls how deep a task goes; the default of 8 reaches roughly 120 professionals per task.
Which city format should I use?
City, ST is most precise, e.g. "Austin, TX". "austin-tx" and "Austin--TX" also work. Leave the location empty for a nationwide search.
How do I avoid duplicate rows?
Key on proId. It is stable across runs and is the strongest de-duplication key. profileUrl works too.
Can I schedule it? Yes — use Apify Schedules. A weekly run per territory keeps a CRM or Sheet fresh. Pair it with a webhook for continuous updates.
Does it work with a CRM? Yes. Use Webhook URL with JSON format, or one of the Apify CRM integrations.
Can I get reviews?
Yes — enable Reviews and supply professional links (or reuse your search links). Reviews are exported as separate rows tagged featureType: "reviews".
What happens if my proxy is rejected? The run logs a clear, fixed message and either retries with a different connection or finishes with whatever it collected. You will never see an internal technical error, a URL or an address in the log or the run output.
Can I see exactly what the paywall did?
Yes. Every run writes a paywall object (detected, isPaying, pricingTier, blocked, limited, freeTierMaxItems) into the run output, alongside totalPushed and stoppedReason.
Can I run it from code? Yes — use the Apify API, the Apify client, or MCP. See MCP usage.
Is the output the same every time? Yes. Field names and types are stable, so a table built from it stays valid across runs.
Troubleshooting
| Symptom | Meaning / fix |
|---|---|
| You only got 2 rows | You are on a free Apify plan and hit the free-tier sample cap. Upgrade for full output. |
stoppedReason: "spending_limit" | Your maximum cost for this run was reached. Raise the limit, or split the job into smaller runs. |
paywall.blocked is true and there are no rows | The Actor is configured to block free accounts entirely rather than limit them. Upgrade to run it. |
Zero rows with stoppedReason: "completed" | No professionals matched that category and location, or a data source declined the run. Check the category spelling, try a broader location, or try again later. |
| Rows have no emails | Nothing public was published for those professionals, or Enable lead details is off. Raise leadDetailsSiteLookups to check more contact sections. |
| Webhook rows did not arrive | Check the receiving URL and that it answers with an HTTP success status. The dataset is always complete regardless. Delivery failures are counted in the run log. |
| The run is slower than expected | Lower leadDetailsSiteLookups to 1, or disable Enable lead details. Turn off Reviews if you do not need review rows. |
| Fewer rows than requested | maxItems is the hard global ceiling, and per-task maxResults/maxResultSetsPerTask cap each task independently. |
Support
Open an issue on the Actor's GitHub repository with the run ID, your input (secrets removed) and what you expected. Please include the run log — if anything went wrong, the log already contains a plain-English, safe description of it.
Changelog
1.0.0
- Initial release: category + location search, opt-in lead details, professional details and reviews.
- Streaming output — every row is written to the dataset as soon as it is collected.
- Free accounts limited to a small sample with a clear upgrade path; paying accounts uncapped.
- Per-result charging that respects your maximum cost per run.
- Webhook delivery in JSON or Slack format, exposed in the input UI.
- Run timeout set to 10,000 seconds.