DEV Community

TORIX
TORIX

Posted on

Build a local B2B lead list in 20 lines of Python (Google Maps + website contacts)

Sales teams pay a lot for lists like "all dentists in Austin with a phone, website and email". You can build one yourself in a few minutes with two hosted scrapers and the Apify Python client. You pay only for the rows you get back (about $3 per 1,000), and nothing for empty sites.

The pipeline

  1. Google Maps Scraper returns businesses for a search term and a location: name, phone, website, rating, review count.
  2. Website Contact Details Extractor visits each website and returns emails (including Cloudflare-obfuscated ones), extra phones, LinkedIn, Instagram, X and the company name.

Both run over plain HTTP, without a headless browser, so a few hundred businesses take seconds.

Code

pip install apify-client
export APIFY_TOKEN=...   # free account: console.apify.com → Settings → API & Integrations
Enter fullscreen mode Exit fullscreen mode
import csv, os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

# 1) Businesses from Google Maps
run = client.actor("torix33/google-maps-scraper").call(run_input={
    "searchTerms": ["dentist"],
    "locations": ["Austin, TX"],
    "maxPlacesPerSearch": 50,
    "minRating": 4.0,
})
places = list(client.dataset(run["defaultDatasetId"]).iterate_items())
sites = sorted({p["website"] for p in places if p.get("website")})

# 2) Contacts from each website
run = client.actor("torix33/website-contact-details-extractor").call(run_input={
    "websites": sites,
    "maxPagesPerSite": 6,
})
contacts = {c["domain"]: c for c in client.dataset(run["defaultDatasetId"]).iterate_items()}

# 3) Merge and save
with open("leads.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["name", "phone", "website", "rating", "reviews", "emails", "linkedin"])
    for p in places:
        domain = (p.get("website") or "").split("//")[-1].split("/")[0].removeprefix("www.")
        c = contacts.get(domain, {})
        w.writerow([p["title"], p.get("phone"), p.get("website"), p.get("rating"),
                    p.get("reviewsCount"), "; ".join(c.get("emails") or []), c.get("linkedin")])
print(len(places), "leads →", "leads.csv")
Enter fullscreen mode Exit fullscreen mode

Prefer a single call? Google Maps Scraper has a scrapeContacts option that runs step 2 for you.

Qualify the list

To keep only businesses on a given stack (for example, WordPress sites without a booking widget), run the same sites list through Website Tech Stack Detector. It returns CMS, analytics, CRM, payments and chat widgets per domain.

Using it from an AI agent

Every actor here is also an MCP tool. Add https://mcp.apify.com/?tools=torix33/google-maps-scraper,torix33/website-contact-details-extractor to Claude Desktop, Cursor or any MCP client and ask: "Find 30 well-rated dentists in Austin and get their emails."

Notes

  • Only public business data is collected. Check your local rules (GDPR, CAN-SPAM) before you email anyone.
  • Every actor has a maxResults cost cap, so you can't overspend.

Links: Google Maps Scraper · Website Contact Details Extractor · Tech Stack Detector

Top comments (0)