FR
Copied
Modules

Scrap (Google Maps)

Source module that extracts Google Maps listings for a set of search queries across one or more geographic zones, in any of ~199 countries.

Purpose

Source module: runs each query over every grid point covering the requested zones and returns a flat CSV of Google Maps establishments (name, contact, location, rating).

Inputs

Field Type Required Description
queries string[] (1–20) yes Search terms run against Google Maps. Each query is trimmed and capped at 200 chars.
country string (ISO3) no Target country as an ISO-3166 alpha-3 code (e.g. FRA, USA, DEU). Defaults to "FRA". Determines both the GPS grid and how zones are resolved.
zones string[] (1–50) yes Geographic zones expressed within country: a state/region (adm1), a county/department (adm2), a city with an optional radius ("Austin 30km"), or the country name itself to scrape the whole country. For France, INSEE codes, department codes and postal codes remain valid. Each zone is resolved to a grid of points server-side.
include_reviews bool no Kept for backward compatibility. Does not chain a reviews job — use the reviews module instead. Defaults to false.
max_per_phone int (0–10) no Maximum listings kept per phone number. 0 (default) = no limit; 1 = a single contact per number. Listings with no number are never dropped. The same listing never appears twice regardless. See Duplicates.

Request body:

{
  "queries": ["plombier", "chauffagiste"],
  "country": "FRA",
  "zones": ["75", "92"],
  "include_reviews": false
}

Targeting another country instead — zones are expressed in that country:

{
  "queries": ["plumber", "hvac"],
  "country": "USA",
  "zones": ["Texas", "Austin 30km"]
}

Effective Google Maps requests = len(queries) × grid_points(zones). Rejected at submit if cost exceeds the per-job EF ceiling.

Outputs

Result file: UTF-8 CSV, semicolon delimiter, BOM (Excel-safe). Same dataset available in three formats via download endpoint.

Column Type Description
nom string Establishment name as displayed on Google Maps.
site_web string Public website URL if listed.
telephone string Phone number as listed.
adresse string Street address (number + street) as shown in the Maps list.
ville string City, taken from Google's own structured place data (exact, including Paris/Lyon/Marseille arrondissements and multi-postcode cities). Empty for listings Google has no address for.
code_postal string Postal code, from the same Google source as ville.
rating float Average star rating (0.0–5.0).
reviews_count int Number of public reviews.
category string Primary Google Maps category.
lien_google_maps string Canonical Google Maps URL for the listing.
aggregator_flag bool True if the listing looks like a directory/aggregator rather than an end business.
query string Source query that produced the row.
lat, lon float Grid point at which the row was collected.

ville and code_postal come from the structured place data Google ships with each result, not from reverse-geocoding — so they match Google exactly. The Maps list view only renders the street, which is why adresse alone never carried the city.

Optional columns

Three extra columns are off by default and enabled per job via extra_columns (a list). The default output is street + ville + code_postal.

Option in extra_columns Adds column(s) Description
gps lat, lon Exact latitude/longitude of the business (Google's own coordinates — not a grid approximation).
departement departement Department/county name (adm2). In France, derived from code_postal; in any other country, derived from the business GPS via world reverse-geocoding.
region region Region/state name (adm1). In France, derived from code_postal; in any other country, derived from the business GPS via world reverse-geocoding.

Example request body: { "queries": ["plumber"], "zones": ["Paris 10km"], "extra_columns": ["gps", "departement", "region"] }.

Duplicates

One listing = one row. Always, with no setting to turn it off. A business can sit within reach of several GPS grid points and match several of your queries, and Google's listing URL is not a stable identity — it also encodes the search context, so two captures of the same place come back with different URLs. Rows are therefore deduplicated on the 0x…:0x… feature id Google carries inside that URL (falling back to normalised name + address when it is absent). This is a correctness guarantee, not a preference.

max_per_phone — the one thing you can tune. Different listings sometimes share a phone number: a shared switchboard, an owner running several outlets, or a network of near-identical local listings pointing at one call centre. For a chain those are genuinely distinct establishments; for prospecting they are one contact. That trade-off is yours to make:

Value Behaviour
0 (default) No limit — every listing is kept, whatever number it shows.
1 One listing per phone number; the first one found wins.
2, 3, … (max 10) Up to N listings per number.

Numbers are compared in E.164 form, so +33 1 87 58 88 84 and 01 87 58 88 84 are the same number; the job's country sets the region used to parse national formats. Listings with no usable number are never dropped by this setting.

The quota applies across the whole job, queries and zones included, and is preserved when a cancelled job is resumed. Deduplication never changes which grid points get scraped, so it has no effect on EF cost or runtime.

Formats: csv (original), json, xlsx. Selected via ?format= on the download endpoint.

Lifecycle

Standard job lifecycle: see Jobs & lifecycle. While running, the SSE status event carries a query_stats payload of shape { "<query>": { "tiles": int, "with_results": int } }, updated in real time to expose per-query hit ratio.

Pipeline

Field Value
needs null (source module — no input CSV required)
produces poi_list

Typical downstream modules chained against a scrap output:

Endpoints

Dedicated endpoint:

POST /api/jobs
Content-Type: application/json

{
  "queries": ["plombier"],
  "zones": ["75"],
  "include_reviews": false
}

Generic job endpoint (equivalent — same payload, job_type inferred from shape):

POST /api/jobs
Content-Type: application/json

{
  "job_type": "scrap",
  "queries": ["plombier"],
  "zones": ["75"]
}

Both responses return the created JobPublic object including id, status, grid_points_count, ef_cost and output_filename.

Download:

GET /api/jobs/{job_id}/download?format=csv|json|xlsx

Limits

Platform-wide quotas: see /docs/concepts/limits. Module-specific caps:

Limit Value
Maximum queries per job 20
Maximum zones per job 50
Maximum query length 200 chars
Maximum cost per job 5 EF by default (per user, adjustable up to 50)
Email verification Required on the account before a scrap job can be created.

Errors

Scenario HTTP Resolution
Unrecognised zone string 400 Inspect the errors array in the response body. Check the zone is spelled as it exists in the selected country (a state/region, county/department, "City 30km", or the country name). For France, INSEE/department/postal codes and "France" also work.
No grid points resolved 400 The zone set is empty after resolution — broaden the zone selection.
EF quota exceeded 400 Reduce the number of queries or shrink the zones until estimated EF is within your per-job ceiling (5 by default, adjustable up to 50 in Settings).
Email not verified 403 Verify the account email before creating a scrap job.
No worker available The job stays in pending until the shared multi-proxy pool is free. Only one multi-proxy job runs at a time platform-wide.
Job failed mid-run A partial CSV is preserved. A POST /api/jobs/{id}/resume creates a follow-up job that skips already-processed grid points and is billed only for the remainder.
Download expired 410 Result files have a retention window — re-run the job or chain from a fresh source.

Queries refused by Google Maps surface in dead_queries on the job object.

Data sources & attribution

Worldwide zone resolution and reverse-geocoding are built on open datasets:

Worldwide administrative boundaries © geoBoundaries (CC BY 4.0) · Cities © GeoNames (CC BY 4.0).

What's next