Google Maps contains structured business data that is valuable for lead generation, market research, and competitive analysis. Scraping Maps directly is difficult due to JavaScript rendering and bot detection. The Scavio API returns local business data as part of SERP results when you include location-based queries. This tutorial extracts business listings, contact information, ratings, and addresses from local search results at $0.005 per query.
Prerequisites
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
- A target business category and location
Walkthrough
Step 1: Search for local businesses
Use location-specific queries to trigger local pack and Maps results in the SERP. The API returns structured local business data.
import os, requests, json, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
URL = 'https://api.scavio.dev/api/v1/search'
def search_local_businesses(category: str, location: str, num: int = 10) -> dict:
resp = requests.post(URL, headers=H,
json={'query': f'{category} in {location}', 'country_code': 'us', 'num_results': num})
data = resp.json()
# Extract local pack results (Maps listings)
local_pack = data.get('local_results', data.get('local_pack', []))
# Also check organic results for business pages
organic = data.get('organic_results', [])
return {
'local_pack': local_pack,
'organic': organic,
'knowledge_graph': data.get('knowledge_graph', {}),
}
results = search_local_businesses('plumbers', 'Austin TX')
print(f'Local pack: {len(results["local_pack"])} businesses')
print(f'Organic: {len(results["organic"])} results')Step 2: Extract and normalize business data
Parse the local results to extract business name, address, phone, rating, and review count into a normalized format.
def extract_business_data(search_results: dict) -> list:
businesses = []
# From local pack
for biz in search_results.get('local_pack', []):
businesses.append({
'name': biz.get('title', biz.get('name', '')),
'address': biz.get('address', ''),
'phone': biz.get('phone', ''),
'rating': biz.get('rating', ''),
'reviews': biz.get('reviews', biz.get('reviews_count', 0)),
'type': biz.get('type', ''),
'website': biz.get('website', biz.get('link', '')),
'source': 'local_pack',
})
# From organic results (business websites)
for r in search_results.get('organic', []):
if any(signal in r.get('link', '').lower() for signal in ['yelp.com', 'yellowpages', 'bbb.org']):
continue # Skip directories
businesses.append({
'name': r.get('title', ''),
'website': r.get('link', ''),
'snippet': r.get('snippet', ''),
'source': 'organic',
})
return businesses
businesses = extract_business_data(results)
print(f'Extracted {len(businesses)} businesses')
for b in businesses[:5]:
print(f' {b["name"][:40]} | {b.get("rating", "N/A")} stars | {b.get("phone", "N/A")}')Step 3: Batch search across multiple categories and locations
Scale the extraction across multiple business categories and locations. Export results to CSV for use in CRM or outreach tools.
import csv
def batch_local_search(searches: list, output_file: str = 'local_businesses.csv') -> list:
all_businesses = []
for category, location in searches:
results = search_local_businesses(category, location)
businesses = extract_business_data(results)
for b in businesses:
b['category'] = category
b['location'] = location
all_businesses.extend(businesses)
print(f'{category} in {location}: {len(businesses)} businesses')
time.sleep(0.3)
# Export to CSV
if all_businesses:
keys = ['name', 'category', 'location', 'address', 'phone', 'rating', 'reviews', 'website']
with open(output_file, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=keys, extrasaction='ignore')
writer.writeheader()
writer.writerows(all_businesses)
print(f'\nTotal: {len(all_businesses)} businesses exported to {output_file}')
print(f'Cost: ${len(searches) * 0.005:.3f}')
return all_businesses
searches = [
('plumbers', 'Austin TX'),
('electricians', 'Austin TX'),
('HVAC contractors', 'Austin TX'),
]
batch_local_search(searches)Python Example
import os, requests, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
def local_businesses(category, location):
resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'query': f'{category} in {location}', 'country_code': 'us', 'num_results': 10})
data = resp.json()
local = data.get('local_results', data.get('local_pack', []))
print(f'{category} in {location}: {len(local)} local results')
for b in local[:5]:
name = b.get('title', b.get('name', ''))
rating = b.get('rating', 'N/A')
print(f' {name[:35]} | {rating} stars')
for cat in ['plumbers', 'electricians']:
local_businesses(cat, 'Austin TX')
time.sleep(0.3)JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function localBusinesses(category, location) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `${category} in ${location}`, country_code: 'us', num_results: 10 })
});
const data = await resp.json();
const local = data.local_results || data.local_pack || [];
console.log(`${category} in ${location}: ${local.length} results`);
local.slice(0, 5).forEach(b => {
console.log(` ${(b.title || b.name || '').slice(0, 35)} | ${b.rating || 'N/A'} stars`);
});
}
(async () => {
await localBusinesses('plumbers', 'Austin TX');
await localBusinesses('electricians', 'Austin TX');
})();Expected Output
Local pack: 8 businesses
Organic: 10 results
Extracted 12 businesses
ABC Plumbing & Drain Service | 4.8 stars | (512) 555-0123
Austin Reliable Plumbers | 4.6 stars | (512) 555-0456
Radiant Plumbing & Air Conditioning | 4.9 stars | (512) 555-0789
plumbers in Austin TX: 8 businesses
electricians in Austin TX: 7 businesses
HVAC contractors in Austin TX: 6 businesses
Total: 21 businesses exported to local_businesses.csv
Cost: $0.015