API
Agent-first structured business data from public Google Maps listings
One request
curl -s https://mapcensus.com/v1/scrape \
-H 'authorization: Bearer YOUR_KEY' \
-H 'content-type: application/json' \
-d '{"query":"coffee shops","location":"Austin, TX","limit":10}'
Paying
Two ways. An API key spends a credit balance. Or send no key at all: the response is
402 with a machine-readable price for that request, and a client that speaks the
x402 payment flow signs an authorization for it and retries. That payment buys that request and
nothing more - no balance is kept for a caller with no account. It is charged the quoted price
when it returns at least one row. A request that returns no rows is not charged; the payment can be presented again to retry that same request, up to 3 attempts in all, within 6 minutes of the first. It buys an answer in the same response:
POST /v1/jobs needs an account, and a request that outlives its response is
stopped, not charged. To pay once for many calls, add credit to an account with
POST /v1/credits, which needs one.
Credits are atomic units of the settlement asset: 1,000,000 credits = $1. Billing is per
record, added up from the lines that apply - at the free rate a listing row is
$4.00 per thousand and a detailed place
$6.00 per thousand. Three cheaper rates are opened by a
monthly plan, and rows stay pay-as-you-go on every one of them - a plan buys the rate and never
comes with credit. A cached answer costs 10% of a
fresh one. The whole card, every unit at every rate:
/pricing for a person, /v1/pricing for a
program. GET /v1/account reports which rate a key is on, and when it renews.
Depth
listing reads the results feed: name, category, rating, review count. Fast and
cheap, because one page load yields many rows. detail opens each place - hours,
phone, website, address parts, price band, photos, booking links, the menu, the busyness curve,
tickets, room rates, the owner's posts, the verdict on the area, and the places shown alongside.
One page load per place, so it costs proportionally more.
Fields
| Group | Holds |
|---|---|
identity | Business name, category list, and the listing’s stable identifiers. |
location | Full address broken into parts, plus latitude, longitude and plus code. |
rating | Average rating, total review count, and the one-to-five star distribution. |
contact | Public phone number and website. |
hours | Opening hours per day, and whether a place has closed temporarily or for good. |
busyness | The published hour-by-hour busyness curve, and how busy the place is right now. |
price | Price band and, where published, an estimated spend per person. |
menu | The published menu where there is one: sections, dish names, prices and descriptions. |
media | The listing's cover photo, and the names of the photo tabs it groups the rest under. |
actions | Reservation, ordering and menu links the listing offers. |
reviews | Individual reviews with rating, text, date and any per-category scores the reviewer gave, plus the aggregate topic tags. |
attributes | Amenities and descriptors exactly as the listing publishes them, grouped into the sections the source groups them into and each carrying whether the place offers it. |
tickets | Admission and experience tickets on sale, each with a price in USD, its seller and a booking link. |
hotel | For a place that takes room bookings: star rating, the dates quoted, every rate offered, and the nearby hotels shown beside it. |
posts | Updates the owner has published, with the event date and time any of them carries. What the owner says, which is not the same as what is currently true. |
area | The published verdict on the surrounding area: an overall visitor score, and the transit, sightseeing and airport scores behind it. |
web | Pages elsewhere on the web that cite this place, each with its provider and the snippet it was shown under. |
related | The places shown alongside this one, with rating and review count - the competitive set as the source itself draws it. |
Ask for fewer and the response is smaller and faster. The default is
identity, location, rating, contact, hours - every group that carries no separate charge.
Naming any others adds them; "fields": "all" asks for the lot.
Every field lists what is inside each group, column by
column, with what it costs.
One group returns personal data. reviews carries the display name a
reviewer published alongside what they wrote, so asking for it makes you a controller of that
data in your own right - with your own transparency and rights obligations, separately from
ours. The terms set this out and the
privacy notice describes what we drop before it
reaches you. If the question is about a business rather than about the people who reviewed it,
rating gives you the score and the count without any of this.
Size and shape
POST /v1/scrape answers inside the request, up to
60 results. Beyond that use POST /v1/jobs, poll
GET /v1/jobs/{id}, then fetch GET /v1/jobs/{id}/results. A job and
its results are kept for 7 days, then deleted - and an
Idempotency-Key is remembered for as long as its job is, so a key sent again after
that starts a new request.
Ask for ?format=ndjson for anything large: it streams a row at a time, so
neither side has to hold the whole result. csv and json are also
available.
A row comes in one of two shapes. The default, ?shape=flat, is a single
snake_case level - place_id, review_count - which is what a
spreadsheet wants and what CSV can express. ?shape=nested returns the record as
the crawler itself holds it, with the sections and spelling it uses - identifiers.placeId,
rating.reviewsCount - so a pipeline already reading those records needs no
translation table. It is the shape on disk, so ?format=ndjson&shape=nested&fields=all
is served with no parsing at all, and it is the cheapest way to take delivery of a large
result. There is no nested CSV, and asking for one is an error rather than a flat file you
did not ask for.
Speed and cost
Set max_age_seconds to the oldest answer you would accept. The same question
already answered inside that window returns in milliseconds from cache and is billed at the
cache rate. When freshness does not matter, this is the single biggest lever on both latency
and price.
The cache is keyed on what you asked about, not on how much of it you wanted:
limit, fields and max_age_seconds do not split it. So a
request for ten rows is served from an answer someone already crawled fifty of, at the cache
rate - while a request for fifty is not served from an answer of ten, because that
would be a short answer pretending to be a complete one.
Limits
| Requests per minute (per key) | 120 |
| Requests per minute (unauthenticated, per address) | 60 |
| Concurrent jobs per account | 3 |
| Max results per job | 5,000 |
| Max reviews per place | 200 |
Errors
Every error is the same shape: a stable error code, a message for a
human, and a request_id to quote. Branch on the code; the wording may change.
{
"error": "insufficient_credits",
"message": "…",
"request_id": "a1b2c3d4e5"
}