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:
  1. SpiderMaps discovers businesses via search queries (100+ results per query)
  2. 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

FieldSpiderMapsSpiderMapsEnrich
Name, Address, PhoneYesYes
Website, RatingYesYes
Review CountYesYes
Place ID / CIDYesYes
Individual ReviewsNoYes (up to 200)
Review AnalysisNoYes
Negative ReviewsNoYes
Owner ResponsesNoYes
Photo URLsNoYes (up to 50)
Review TagsNoYes
Menu LinkNoYes
Street View URLNoYes
Popular TimesNoYes
Related PlacesNoYes (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

CategoryKeywords Detected
serviceslow service, rude, ignored, unfriendly, poor service
food_qualitycold food, stale, undercooked, bland, tasteless
cleanlinessdirty, filthy, unclean, bugs, smell, disgusting
valueoverpriced, expensive, not worth, ripoff, small portions
wait_timelong wait, slow, took forever, hour wait
managementmanager, 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_allnegative_onlyBehavior
truefalseDefault - Returns all reviews + analysis
truetrueReturns only 1-2 star reviews
falsefalseEmpty reviews array, only analysis
falsetrueNegative 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

ConfigurationTypical Time
Basic enrichment (no reviews)15-30 seconds
With 50 reviews30-60 seconds
With 200 reviews60-120 seconds
With proxyAdd 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