Overview
SpiderMapsEnrich is a second-stage enrichment worker designed to extract detailed data from individual Google Maps place pages. It complements SpiderMaps by providing deep data extraction that isn’t available from bulk search results.
Two-Stage Architecture:
- SpiderMaps discovers businesses via search queries (100+ results per query)
- SpiderMapsEnrich enriches individual places with reviews, photos, and analysis
Why Use SpiderMapsEnrich?
SpiderMaps searches Google Maps and extracts basic business info from feed results. However, detailed data like reviews, photos, and popular times requires visiting each place page individually. SpiderMapsEnrich:
- Visits individual place pages to get full details
- Uses IP isolation to avoid rate limiting (different VPS from discovery)
- Extracts deep data including reviews, photos, and sentiment analysis
- Hosts photos on SpiderMedia for permanent storage
Data Comparison
| Field | SpiderMaps | SpiderMapsEnrich |
|---|
| Name, Address, Phone | Yes | Yes |
| Website, Rating | Yes | Yes |
| Review Count | Yes | Yes |
| Place ID / CID | Yes | Yes |
| Individual Reviews | No | Yes (up to 200) |
| Review Analysis | No | Yes |
| Negative Reviews | No | Yes |
| Owner Responses | No | Yes |
| Photo URLs | No | Yes (up to 50) |
| Review Tags | No | Yes |
| Menu Link | No | Yes |
| Street View URL | No | Yes |
| Popular Times | No | Yes |
| Related Places | No | Yes (snowball) |
Quick Start
1. Get a CID from SpiderMaps Results
SpiderMaps returns a cid field for each business. This is the most reliable identifier for enrichment.
// SpiderMaps result
{
"name": "Joe's Pizza",
"cid": "0x87c9f95545605943:0x6bad649f33559c94",
"place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
"rating": 4.5
}
2. Submit Enrichment Job
import requests
import time
API = "https://spideriq.ai/api/v1"
TOKEN = "<your_token>"
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
# Submit enrichment job
response = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": "0x87c9f95545605943:0x6bad649f33559c94",
"enrich_options": {
"reviews": {"enabled": True, "max_count": 50},
"photos": {"enabled": True, "max_count": 10},
"store_images": True
}
}
}
)
job = response.json()
print(f"Job submitted: {job['job_id']}")
# Poll for results
while True:
status = requests.get(
f"{API}/jobs/{job['job_id']}/status",
headers=headers
).json()
if status['status'] == 'completed':
break
elif status['status'] == 'failed':
print(f"Job failed: {status.get('error_message')}")
break
time.sleep(5)
# Get results
results = requests.get(
f"{API}/jobs/{job['job_id']}/results",
headers=headers
).json()
print(f"Business: {results['data']['data']['name']}")
print(f"Reviews extracted: {len(results['data']['reviews'])}")
print(f"Photos hosted: {len(results['data']['photos'])}")
Review Analysis
SpiderMapsEnrich automatically analyzes extracted reviews to provide actionable insights.
Rating Distribution
See how reviews break down by star rating:
{
"review_analysis": {
"rating_distribution": {
"5_star": 35,
"4_star": 8,
"3_star": 4,
"2_star": 2,
"1_star": 1
}
}
}
Negative Review Detection
Automatically identify and categorize negative reviews (1-2 stars):
{
"review_analysis": {
"negative_count": 3,
"negative_percentage": 6.0,
"negative_reviews": [
"Service was terribly slow...",
"Food came out cold and overpriced..."
],
"common_complaints": ["service", "food_quality", "value"]
}
}
Complaint Categories
| Category | Keywords Detected |
|---|
service | slow service, rude, ignored, unfriendly, poor service |
food_quality | cold food, stale, undercooked, bland, tasteless |
cleanliness | dirty, filthy, unclean, bugs, smell, disgusting |
value | overpriced, expensive, not worth, ripoff, small portions |
wait_time | long wait, slow, took forever, hour wait |
management | manager, owner, refused, no refund, unprofessional |
Owner Response Tracking
Track how businesses respond to negative feedback:
{
"review_analysis": {
"owner_response_rate_negative": 66.7
}
}
Competitor Research: Use negative_only: true to focus exclusively on competitor weaknesses for competitive analysis.
Photo Storage
Photos are automatically uploaded to SpiderMedia (per-client storage) and returned as permanent URLs.
Hosted URLs
{
"photos": [
"https://media.spideriq.ai/client-cli_xxx/gmaps/gmaps_enrich_0x87c9f95545605943_1.jpg",
"https://media.spideriq.ai/client-cli_xxx/gmaps/gmaps_enrich_0x87c9f95545605943_2.jpg"
],
"photos_original": [
"https://lh5.googleusercontent.com/p/...",
"https://lh5.googleusercontent.com/p/..."
]
}
SpiderMedia Integration: Each client has a dedicated storage bucket. Photos are organized in the gmaps/ folder with consistent naming for easy management.
Disable Storage
To skip photo hosting and get original Google URLs only:
{
"payload": {
"google_cid": "0x...",
"enrich_options": {
"photos": {"enabled": true, "max_count": 10},
"store_images": false
}
}
}
Two-Stage Workflow
Complete Pipeline: Discovery + Enrichment
import requests
import time
API = "https://spideriq.ai/api/v1"
TOKEN = "<your_token>"
headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
# Stage 1: Discover businesses with SpiderMaps
print("Stage 1: Discovering businesses...")
discovery_job = requests.post(
f"{API}/jobs/spiderMaps/submit",
headers=headers,
json={
"payload": {
"search_query": "pizza restaurant New York, NY",
"max_results": 20
}
}
).json()
# Wait for discovery
while True:
status = requests.get(f"{API}/jobs/{discovery_job['job_id']}/status", headers=headers).json()
if status['status'] in ['completed', 'failed']:
break
time.sleep(3)
discovery_results = requests.get(
f"{API}/jobs/{discovery_job['job_id']}/results",
headers=headers
).json()
businesses = discovery_results['data']['businesses']
print(f"Found {len(businesses)} businesses")
# Stage 2: Enrich top-rated businesses
print("\nStage 2: Enriching top businesses...")
top_businesses = sorted(businesses, key=lambda x: x.get('rating', 0), reverse=True)[:5]
enriched_data = []
for biz in top_businesses:
cid = biz.get('cid')
if not cid:
continue
print(f" Enriching: {biz['name']}...")
enrich_job = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": cid,
"original_data": biz, # Merge with discovery data
"enrich_options": {
"reviews": {"enabled": True, "max_count": 50},
"photos": {"enabled": True, "max_count": 5}
}
}
}
).json()
# Wait for enrichment
while True:
status = requests.get(f"{API}/jobs/{enrich_job['job_id']}/status", headers=headers).json()
if status['status'] in ['completed', 'failed']:
break
time.sleep(5)
if status['status'] == 'completed':
results = requests.get(f"{API}/jobs/{enrich_job['job_id']}/results", headers=headers).json()
enriched_data.append(results['data'])
analysis = results['data'].get('review_analysis', {})
print(f" Reviews: {analysis.get('total_extracted', 0)}")
print(f" Negative: {analysis.get('negative_percentage', 0):.1f}%")
print(f"\nEnriched {len(enriched_data)} businesses with detailed review data")
Snowball Expansion
Discover related places by enabling snowball mode. SpiderMapsEnrich extracts “People also search for” and similar sections.
# Enable snowball discovery
job = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": "0x87c9f95545605943:0x6bad649f33559c94",
"snowball": {
"enabled": True,
"max_depth": 2, # How many levels deep
"max_places_per_seed": 10 # Places per level
}
}
}
).json()
Snowball Diagram
Seed Place (depth 0)
+-- Related Place 1 (depth 1)
| +-- Related 1.1 (depth 2)
| +-- Related 1.2 (depth 2)
+-- Related Place 2 (depth 1)
+-- Related 2.1 (depth 2)
IP Isolation: Each snowball level automatically uses a different VPS to avoid rate limiting. The current VPS is added to exclude_vps for child jobs.
Review Options
Control how reviews are extracted and returned:
include_all | negative_only | Behavior |
|---|
true | false | Default - Returns all reviews + analysis |
true | true | Returns only 1-2 star reviews |
false | false | Empty reviews array, only analysis |
false | true | Negative reviews from analysis only |
Example: Negative Reviews Only
# Focus on competitor weaknesses
job = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": "0x...",
"enrich_options": {
"reviews": {
"enabled": True,
"max_count": 100,
"negative_only": True # Only 1-2 star reviews
}
}
}
}
).json()
IP Isolation
For large-scale enrichment, use IP isolation to avoid rate limiting:
# Discover from one VPS, enrich from another
discovery_vps = "llm3" # Known from metadata
enrich_job = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": cid,
"exclude_vps": [discovery_vps], # Don't use same VPS
"use_proxy": True # Use mobile proxy
}
}
).json()
Use Cases
1. Competitor Review Analysis
Extract and analyze competitor reviews to identify weaknesses:
competitors = [
"0x87c9f95545605943:0x6bad649f33559c94", # Competitor A
"0x47c708e4cc62845f:0x2d80199bcc9f805c", # Competitor B
]
for cid in competitors:
job = submit_enrich_job(cid, reviews_max=100, negative_only=True)
results = wait_for_results(job['job_id'])
complaints = results['data']['review_analysis']['common_complaints']
print(f"Competitor weaknesses: {', '.join(complaints)}")
2. Visual Content Collection
Build a gallery of business photos:
job = requests.post(
f"{API}/jobs/spiderMapsEnrich/submit",
headers=headers,
json={
"payload": {
"google_cid": cid,
"enrich_options": {
"reviews": {"enabled": False}, # Skip reviews
"photos": {"enabled": True, "max_count": 50},
"store_images": True # Host on SpiderMedia
}
}
}
).json()
3. Market Research Report
Combine discovery + enrichment for comprehensive market analysis:
# 1. Discover all pizza restaurants in area
# 2. Enrich top 20 by rating
# 3. Aggregate review analysis
report = {
"total_businesses": len(businesses),
"avg_rating": sum(b['rating'] for b in businesses) / len(businesses),
"common_complaints": aggregate_complaints(enriched_data),
"owner_response_rate": avg_response_rate(enriched_data)
}
Processing Time
| Configuration | Typical Time |
|---|
| Basic enrichment (no reviews) | 15-30 seconds |
| With 50 reviews | 30-60 seconds |
| With 200 reviews | 60-120 seconds |
| With proxy | Add 15-30 seconds |
Best Practices
Use CIDs over Place IDs: CIDs (hex format like 0x...:0x...) are more reliable and stable than Place IDs.
Chain with SpiderMaps: Use SpiderMaps for bulk discovery, then SpiderMapsEnrich for targeted deep dives on high-value results.
IP Isolation: For large batches, use exclude_vps to distribute requests across different servers.
Rate Limiting: Google Maps has rate limits. Process enrichment jobs in batches with delays between submissions.
Next Steps