🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Google Maps B2B Lead Goldmine

by @nicemaths123

Extract, score, and export detailed local business leads from Google Maps by keyword and location with contact info, reviews, and personalized outreach messa...

πŸ’‘ Examples

Basic Lead Extraction by Keyword and City

import ApifyClient from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor("compass~crawler-google-places").call({ searchStringsArray: ["dentists in Miami, FL"], maxCrawledPlacesPerSearch: 50, language: "en", includeWebResults: false });

const { items } = await run.dataset().getData();

// Each item contains: // { title, phone, website, address, totalScore, reviewsCount, // categoryName, openingHours, email, location }

console.log(Found ${items.length} leads);


Multi-Location Parallel Search

const locations = [
  "dentists in Miami, FL",
  "dentists in Fort Lauderdale, FL",
  "dentists in West Palm Beach, FL",
  "dentists in Tampa, FL",
  "dentists in Orlando, FL"
];

const runs = await Promise.all( locations.map(search => client.actor("compass~crawler-google-places").call({ searchStringsArray: [search], maxCrawledPlacesPerSearch: 50, language: "en" }) ) );

const allLeads = []; for (const run of runs) { const { items } = await run.dataset().getData(); allLeads.push(...items); }

// Deduplicate by phone number const seen = new Set(); const unique = allLeads.filter(lead => { if (!lead.phone || seen.has(lead.phone)) return false; seen.add(lead.phone); return true; });

console.log(${unique.length} unique leads across ${locations.length} cities);


Lead Scoring Algorithm

function scoreLead(lead) {
  let score = 50;

// Review gap signal: few reviews = needs marketing help if (lead.reviewsCount < 10) score += 20; else if (lead.reviewsCount < 30) score += 10;

// Low rating signal: needs reputation management if (lead.totalScore && lead.totalScore < 4.0) score += 15; else if (lead.totalScore && lead.totalScore < 4.5) score += 5;

// No website = massive opportunity if (!lead.website || lead.website === '') score += 25;

// Has website but no email listed = hard to reach if (lead.website && !lead.email) score -= 5;

// Has phone = contactable if (lead.phone) score += 5;

// Category bonus for high-value niches const highValue = ['lawyer', 'dentist', 'doctor', 'real estate', 'contractor', 'plumber']; if (highValue.some(k => (lead.categoryName || '').toLowerCase().includes(k))) { score += 10; }

return Math.min(100, Math.max(0, score)); }

const scored = unique.map(lead => ({ ...lead, leadScore: scoreLead(lead) })).sort((a, b) => b.leadScore - a.leadScore);

console.log("Top 10 leads:"); scored.slice(0, 10).forEach((lead, i) => { console.log(${i + 1}. [${lead.leadScore}/100] ${lead.title} | ${lead.phone} | ${lead.website || 'NO WEBSITE'}); });


Deep Email Extraction from Business Websites

async function extractEmails(leads) {
  const withWebsites = leads.filter(l => l.website);

const run = await client.actor("apify/website-content-crawler").call({ startUrls: withWebsites.slice(0, 20).map(l => ({ url: l.website })), maxCrawlPages: 3, crawlerType: "cheerio" });

const { items } = await run.dataset().getData();

const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;

const enriched = items.map(page => { const emails = [...new Set((page.text || '').match(emailRegex) || [])]; return { url: page.url, emails }; });

return enriched; }


Generate Personalized Outreach per Lead

import axios from 'axios';

async function generateOutreach(lead) { const prompt = Write a short cold email (under 80 words) for this local business.

LEAD:

  • Business: ${lead.title}
  • Category: ${lead.categoryName}
  • Location: ${lead.address}
  • Rating: ${lead.totalScore}/5 (${lead.reviewsCount} reviews)
  • Website: ${lead.website || 'None'}
  • Lead Score: ${lead.leadScore}/100
  • RULES:

  • Reference something specific about their business
  • If they have few reviews, mention you can help them get more
  • If they have no website, mention you can build one
  • If their rating is below 4.5, mention reputation management
  • Keep it conversational, no corporate speak
  • End with a soft question, not a hard CTA
  • Include [YOUR_NAME] and [YOUR_COMPANY] placeholders
  • Return subject line and body only.;

    const { data } = await axios.post('https://api.anthropic.com/v1/messages', { model: "claude-sonnet-4-20250514", max_tokens: 250, messages: [{ role: "user", content: prompt }] }, { headers: { 'x-api-key': process.env.CLAUDE_API_KEY, 'anthropic-version': '2023-06-01' } });

    return data.content[0].text; }

    // Generate outreach for top 10 leads for (const lead of scored.slice(0, 10)) { lead.outreachEmail = await generateOutreach(lead); await new Promise(r => setTimeout(r, 500)); }


    Full Pipeline: Search, Score, Enrich, Outreach, Export

    import { writeFileSync } from 'fs';

    async function fullLeadPipeline(keyword, locations, maxPerLocation = 50) { console.log(Starting pipeline for: ${keyword});

    // STEP 1: Scrape all locations in parallel const searches = locations.map(loc => ${keyword} in ${loc}); const runs = await Promise.all( searches.map(s => client.actor("compass~crawler-google-places").call({ searchStringsArray: [s], maxCrawledPlacesPerSearch: maxPerLocation, language: "en" }) ) );

    let allLeads = []; for (const run of runs) { const { items } = await run.dataset().getData(); allLeads.push(...items); }

    // STEP 2: Deduplicate const seen = new Set(); const unique = allLeads.filter(l => { const key = l.phone || l.title; if (seen.has(key)) return false; seen.add(key); return true; });

    // STEP 3: Score const scored = unique.map(l => ({ ...l, leadScore: scoreLead(l) })) .sort((a, b) => b.leadScore - a.leadScore);

    // STEP 4: Generate outreach for top 20 for (const lead of scored.slice(0, 20)) { lead.outreachEmail = await generateOutreach(lead); await new Promise(r => setTimeout(r, 500)); }

    // STEP 5: Export to CSV const headers = ["title","phone","email","website","address","totalScore","reviewsCount","categoryName","leadScore","outreachEmail"]; const csv = [ headers.join(","), ...scored.map(l => headers.map(h => "${(l[h] || '').toString().replace(/"/g, '""')}").join(",")) ].join("\n");

    const filename = leads-${keyword.replace(/\s+/g, '_')}-${Date.now()}.csv; writeFileSync(filename, csv); console.log(Exported ${scored.length} scored leads to ${filename});

    return scored; }

    // Usage await fullLeadPipeline("plumbers", ["Miami, FL", "Fort Lauderdale, FL", "Tampa, FL"]);


    View on ClawHub
    TERMINAL
    clawhub install google-maps-extractor

    πŸ§ͺ Use this skill with your agent

    Most visitors already have an agent. Pick your environment, install or copy the workflow, then run the smoke-test prompt above.

    πŸ” Can't find the right skill?

    Search 60,000+ AI agent skills β€” free, no login needed.

    Search Skills β†’