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, phone, address, website, rating and review count).

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. City and region names are accepted in the usual languages ("Bruxelles", "Brussel" and "Brussels" resolve to the same place), and associated or delegated French communes ("Lomme") resolve to their own location. A name shared by several cities means the most populous one; a name shared by a large city and the area around it means the city ("Antwerp", "New York"), and the whole area is requested by its title ("New York State"). Corsica is written "2A", "2B" or "Corse". The overseas departments (Guadeloupe, Martinique, Guyane, La Réunion, Mayotte) are requested by name or code ("Martinique", "974"), or by one of their communes or postal codes; "France" alone does not include them. A commune there that shares its name with a mainland one is written with its department ("Saint-Denis 974", "Saint-Denis La Réunion") or its postal code ("97400").
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.
exclude_foreign_phones bool no true (default): drops listings whose phone number carries another country's calling code. Numbers without a country code and listings with no number are kept. See Foreign phone numbers.

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). The same dataset is available in three formats through the download endpoint.

Column Type Description
nom string Establishment name as displayed on Google Maps.
telephone string Phone number as listed.
adresse string Street address (number and street) as shown in the Maps list.
ville string City, taken from Google's own structured place data (exact, including Paris, Lyon and 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.
site_web string Public website URL, when listed.
lien_google_maps string Google Maps URL of the listing.
note number Average star rating, from 0 to 5. Empty when the listing has none.
nb_avis integer Number of public reviews. Empty when unknown.
query string Query that produced the row.
date_scraping string Date and time 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 carries the city.

The file has no category, opening-hours or directory-flag column. Websites that are really a directory listing rather than the business's own site are recognised by dead_check (status = aggregator) and can be dropped with the exclude_aggregators rule of filter.

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"] }.

Foreign phone numbers

A search area that touches a border also returns businesses from the other side: a scrape of Lille (France) brings back salons in Tournai (Belgium), with numbers in +32.

exclude_foreign_phones, on by default. A listing is dropped when its phone number explicitly carries the calling code of another country than the one you selected. Everything else is kept:

Listing Result
Number written without a country code (03 20 53 00 86, 06 26 55 99 69) Kept: that is how numbers of the selected country are written.
Number with the selected country's calling code Kept.
Number with another country's calling code (+32 69 23 26 64 in a France scrape) Dropped.
No phone number Kept.
Unreadable number or unknown calling code Kept.

It works the same way in every country. Calling codes shared by several countries (+1 for the United States, Canada and the Caribbean, +7 for Russia and Kazakhstan, +44 for the United Kingdom and the Channel Islands) are resolved from the number range, not from the code alone. Numbers from a country's overseas territories count as that country's (Guadeloupe or Réunion for France, Puerto Rico for the United States).

Dropped listings do not count toward stop_at, and the job log ends with the number of listings dropped this way. Send "exclude_foreign_phones": false (or untick Exclude foreign phone numbers in the advanced settings) to keep them.

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. When a close match exists, the error names it (suggestion).
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.
Maximum duration reached A job runs for at most 24 hours. It then stops with its partial CSV preserved and a follow-up job is queued automatically for the remaining grid points, billed only for the remainder. Nothing to do: follow the new job, linked by source_job_id.
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