Google Maps Popular Times Is Missing. What Should Your App Do?
When a place returns popular_times: {}, you have no hourly observation to chart. It does not mean nobody visited. Check the place identity and detail status, preserve the empty value, and decide whether the business listing itself supports a follow-up. Keep any hours you do collect in the place’s local time.
I run Serpent API, whose Maps fields are discussed here. The October 1 source review checked Google’s published availability explanation and each provider’s documented fields; it did not measure Popular Times coverage across providers.
Short answer: A Google Maps Popular Times API response can be empty because popularity data is not available for that place in the public result, or because its detail record is incomplete. Serpent keeps popular_times as an object: {} means no hourly values were returned. Read detail_status before deciding what the absence means.
Why Google Maps Popular Times data can be empty
Google says its Popular Times feature draws on aggregated visits from people who opted into its location data collection, and shows the feature when there is enough information. It is not a census of everyone entering a business. A listing can have a name, reviews, opening hours and a map pin while showing no Popular Times chart.
That gives you two separate questions: did our request return a complete place record, and did that record contain popularity values? Serpent's Maps response contract makes the distinction explicit: popular_times is always an object, and the documented empty value is {}. A missing chart does not tell you how many people visited, whether the business is open, or whether the field will appear later.
A user report in Outscraper's community describes the same kind of missing-field frustration with another Maps data product. It is one user's experience, not evidence of Serpent's coverage or a published availability rate. We do not have a defensible overall coverage percentage for this field.
What the chart measures—and what it never measures
Google's own explanation says the usual Popular Times graph is based on aggregated, anonymized data from opted-in Location History users. It appears only when Google has enough visits to show it. The graph describes a place's typical busyness over recent months. A separate live overlay, when shown, describes activity now relative to the usual level. Wait-time and visit-duration estimates are other products of visit patterns; none is a turnstile count.
Most importantly for analysis, Google says an hour's popularity is relative to that same place's typical peak for the week. Two cafes can both peak at a displayed score of 100 while serving very different numbers of people. The score is useful for choosing a quieter hour within one location. It is not a defensible way to rank two businesses by total visitors or revenue. Our second chart below illustrates this normalization with invented bars so the distinction is visible; it is not API data.
| Question you want to answer | Can Popular Times help? | What else you need |
|---|---|---|
| Which hour is typically quieter at this branch? | Yes, when this branch has a chart and you respect its local day. | Opening hours and the place's time zone. |
| How many people entered between 4 and 5 PM? | No. Google does not publish an absolute headcount in the chart. | Your own visitor counter or first-party transactions. |
| Which of two branches has more total demand? | No. Each branch is normalized to its own peak. | Comparable first-party counts over the same period. |
| Is this shop busy right now? | Only if a live overlay is actually present; typical bars alone are historical patterns. | A current observation, with its timestamp and source marked. |
Do not silently replace a missing chart with opening hours, reviews, or a neighboring branch's chart. Those answer different questions. If you show an estimated busy hour in a product, label exactly which field produced it, when it was fetched, and whether it is typical or live.
Read the status before reading an empty object
The Serpent Google Maps API can return a complete place record or a core record whose enrichment did not finish within the request. Keep both: the core identity is useful even when some detail is unknown. On a search response, the meta.partial and delivery blocks give request-level context; detail_status tells you about an individual place.
| Place record | popular_times | Safe interpretation | Next step |
|---|---|---|---|
complete | Populated object | Popularity values were returned for this place in this response. | Keep raw values and the place's time zone; review the shape before charting. |
complete | {} | No popularity values were available in this response. Demand remains unknown. | Skip the hourly chart, or check the public listing if the field is essential. |
core_only or null | {} | Detail may be incomplete. You cannot decide whether the listing exposes Popular Times. | Allow one bounded follow-up detail request if the business case warrants it. |
This is the same discipline we recommend for contact-field lead pipelines and the no-website workflow: a blank field in a partial record is unknown, not proof of absence. You can also read our place-details field audit for the wider set of fields that can vary by business.
How the main Popular Times data options compare
We run Serpent, so here is the market view with the billing and field distinctions exposed. This is a comparison of the documented options for one identified place, not a claim that all six providers return the same coverage or quality. We checked the linked official pages on October 1, 2026. Empty-field behavior is especially important: an empty value can come from the public listing or an incomplete read, and we have not run a cross-provider coverage study.
| Option | Documented Popular Times path | Billing unit and practical fit | Important limit |
|---|---|---|---|
| Serpent Maps Place | One identified /api/maps/place response; popular_times stays an object and detail_status distinguishes complete from incomplete detail. | $0.0015 per Place call at the published Default rate; suitable when you want an explicit missing-data state and a selective lookup. | {} is still possible after a complete lookup; no coverage percentage is promised. |
| Google Places API (New) | Its published Place Data Fields list does not include a Popular Times field, although Maps may display the chart to users. | Official Places billing varies by requested field mask and SKU; use it for documented place fields and the official platform contract. | A field you can see in the Maps interface is not necessarily in the Places API. |
| SerpApi Google Maps | Its own worked example gets popular_times.graph_results from Place Results after identifying a place in a search. | Monthly search credits; useful if your app already uses its Maps result schema. | Search-list and Place Results are separate steps in its example; check your plan's allowance. |
| DataForSEO Business Data | The documented Google My Business Info live result contains a popular_times object, with day-level structure. | Task-based API pricing shown through its official pricing calculator; useful when already running its business-data pipeline. | Its task schema and charge need mapping; a search-list row is not automatically a usable hourly chart. |
| Outscraper | Its field dictionary lists popular_times for individual place searches, including live status when available. | Per-result and task options; fits bulk exports and no-code workflows as well as API use. | Its own documentation says the field works for individual searches; its community confirms a listing can still lack it. |
| Apify Compass Google Maps Scraper | Its maintained Actor page lists a Popular Times histogram and live occupancy, and accepts a direct Place ID or URL. | Advertises from $1.50 per 1,000 scraped places; direct Place ID or URL input is a paid additional-details add-on, so the headline record rate alone is not a quote for this task. | It produces an exportable dataset. Confirm the add-on charge and actual hourly values for the places you need. |
Serpent's practical edge for this task is the small per-place Default charge and a documented {} plus detail_status interpretation in the same response. SerpApi publishes a clear graph_results example; Google Places is the right official path for fields it actually lists; DataForSEO, Outscraper, and Apify Compass can suit teams already using their wider business-data or export workflows. Before choosing, submit the same small, known set of places to each candidate you can trial, then compare returned hourly values, absence, response time, billing, and terms. A price table alone cannot establish field coverage.

A complete Google Maps Popular Times API handling example
Use a place ID you already found through a Maps search workflow. The following Python 3.9+ script makes one /api/maps/place request, checks the parsed response, and reports an explicit status. It uses only the standard library. Set SERPENT_API_KEY and SERPENT_PLACE_ID in your environment before running it. The example has not been verified against a live parsed response.
# Python 3.9+; illustrative request, not a measured response.
import json
import os
import sys
from datetime import datetime
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
key = os.environ["SERPENT_API_KEY"]
place_id = os.environ["SERPENT_PLACE_ID"]
url = "https://apiserpent.com/api/maps/place?" + urlencode({"place_id": place_id})
request = Request(url, headers={"X-API-Key": key})
try:
with urlopen(request, timeout=60) as response:
body = json.load(response)
except (HTTPError, URLError, TimeoutError, ValueError) as exc:
sys.exit(f"Place request failed: {exc}")
if body.get("success") is not True or not isinstance(body.get("place"), dict):
sys.exit("No parsed place record was returned")
place = body["place"]
if not (place.get("place_id") or place.get("name")):
sys.exit("The response has no identifiable place")
detail_status = place.get("detail_status")
popular = place.get("popular_times")
if not isinstance(popular, dict):
sys.exit("Unexpected popular_times shape; check the current API contract")
if detail_status != "complete":
status = "detail_incomplete_or_unknown"
elif popular:
status = "values_returned"
else:
status = "unavailable_in_this_response"
timezone_name = place.get("timezone")
observed_local = None
if timezone_name:
try:
observed_local = datetime.now(ZoneInfo(timezone_name)).isoformat()
except ZoneInfoNotFoundError:
pass
print(json.dumps({
"place_id": place.get("place_id"),
"name": place.get("name"),
"detail_status": detail_status,
"popular_times_status": status,
"popular_times": popular,
"timezone": timezone_name,
"observed_at_local": observed_local,
}, indent=2))
For a complete record without popularity data, the illustrative output is:
{
"place_id": "ChIJExamplePlaceId",
"name": "Example Cafe",
"detail_status": "complete",
"popular_times_status": "unavailable_in_this_response",
"popular_times": {},
"timezone": "America/New_York",
"observed_at_local": "2026-10-01T10:00:00-04:00"
}
The example output is a shape illustration, not a measured live result. The timestamp will reflect your run time. The status labels are produced by the example script; they are not extra API fields.
Skip, retry, or check the listing yourself?
Start with the decision your product needs to make. If Popular Times is merely a nice chart, keep the place record and show “data unavailable” instead of guessing at a quiet hour. If an hourly chart is essential, use the following bounded workflow.
- Check
detail_status. If it iscore_onlyornull, a single follow-up place detail lookup is reasonable. Keep the original record while you check. - Check the public Maps listing. If your follow-up record is complete but still has
{}, see whether Google shows a Popular Times chart for that exact place. Match the place ID and address to avoid checking a branch with a similar name. - Stop at a budget. Repeated paid calls do not create source data. Record when you checked, then retry on your normal refresh cycle only if the listing changes or the use case justifies it.
- Use a different signal when needed. Reviews, opening hours, and your own first-party visits answer different questions. None is a substitute for measured hourly foot traffic.
At the published Default rate of $1.50 per 1,000 Maps Place calls, one follow-up call costs $0.0015, 100 cost $0.15, and 1,000 cost $1.50 before any free calls or tier discount. Those figures are simple call-count arithmetic using the public price on October 1, 2026. They do not predict how often Popular Times will be returned. Track cost per usable popularity record from your own responses, rather than multiplying a per-call price by an assumed coverage rate.
Budget the follow-ups before you collect a city
A cheap individual call can become an expensive workflow if you refresh everything repeatedly or mistake discovery for detail. Suppose you already hold 1,000 verified place IDs and check each once. At the published Default Place rate, the planned Place charge is $1.50. If 200 of those records are incomplete and you allow exactly one follow-up each, another 200 calls add $0.30. The $1.80 total is an arithmetic scenario, not a measured availability rate: 200 is an invented planning assumption. Discovery, reviews, more fields, different price tiers, taxes, and any extra calls belong in your own budget.
At a paid Default account's published standard bracket, the account-wide allocation is up to 10,000 requests a day, with 1,000 per hour and 100 per minute. A 1,200-call task therefore fits inside one day, but it passes the 1,000/hour ceiling, so it needs at least two hourly windows (at 100 per minute, about 12 minutes of sending) even though the $1.80 usage math looks tiny. Do not assume a cheap unit rate means all requests can finish immediately; read your own /api/status limits and balance before queuing. If you need both discovery and Place calls, count both against the schedule. The selective-enrichment cost guide shows how that extra stage changes the total.
Keep three counters in the job log: places requested, complete detail records, and complete records with usable popularity values. The last one is the denominator for cost per usable Popular Times record. If it is zero, report the metric as unavailable rather than dividing by zero. That is more informative than a claim such as “$0.0015 per busy-hour chart,” because the charge is per call while the chart is conditional.

Keep hourly data in the place's local time
When a place does return popularity values, store the raw popular_times object alongside timezone, which is documented as an IANA time-zone name such as America/New_York. Do not label a Friday evening value with your server's Friday if the business is across the date line. The script records a local observation timestamp so you can see when you fetched the place in its own zone.
Before charting values across cities, inspect the returned day and hour structure and test your conversion at a daylight-saving change. A missing timezone is null under the documented place schema; in that case, preserve the raw values and defer cross-region comparisons until you can establish the zone. The local rank-tracking guide gives more context on why location settings matter when combining local data.
Do not flatten a week before you understand its shape
Different providers name days, hours, and live overlays differently. SerpApi's worked example reads day arrays under graph_results; DataForSEO describes popular_times_by_days; Outscraper has its own schema. Our public Serpent contract promises the field is an object, but it does not promise a universal hour-key layout for every place. The code above deliberately preserves the raw object before transformation. Write and test a provider-specific adapter only after you inspect representative parsed results.
When you do normalize it, keep the provider, raw day label, raw hour label, value, place timezone, fetch timestamp, and whether a value is typical or live. Test midnight crossings and daylight-saving transitions with explicit cases. A restaurant open until 2 AM may belong to an operational “Friday night” report while its local clock has moved to Saturday; the grouping rule is a business choice you must state. Do not silently shift the source's day labels into UTC and then call the result local Friday.
A small audit before you trust a Popular Times dashboard
We would test a sample before buying a large collection. Choose places with a visible public chart, places without one, and places in at least two time zones. Record each exact place ID and public listing URL. For each provider under consideration, log whether the request completed, whether the identity matched, whether hourly values were returned, the field shape, local timezone, and the actual billable unit. This gives you a task-specific coverage picture; it does not establish a universal provider success rate.
- Pick known positives and negatives. Include a few listings where you can currently see a chart, and a few where you cannot. Save a screenshot and check date for your own audit. Google can change the display later.
- Inspect parsed data. A 200 response with a place name is not enough. Confirm the exact place identity, completion state, and whether the popularity object contains day/hour values.
- Compare like for like. Use the same IDs and locale settings, and separate typical values from a live overlay. Do not treat another branch of a chain as a match.
- Record the missing reason you can actually know. “Complete record, no values” and “detail incomplete” are different. Neither tells you an absolute visitor count.
- Review cost per usable record. Include discovery, detail, and bounded repeats. Recheck published prices and your live limit bracket before expanding the roster.
If a public listing visibly shows a chart but the API returns no values, preserve the place ID, time, location, raw response and a screenshot for support or debugging. Do not label the difference a provider defect from one case: the listing and API may have been read at different moments or under different locale settings. If the mismatch repeats on the same place, it becomes a concrete case to investigate.
What to record in your own data model
We suggest keeping the raw object, the detail status, the zone, the fetch time and one of the three script statuses. That small set prevents two common mistakes: replacing unknown with zero, and silently comparing hours from different local days.
| Store | Why | When missing |
|---|---|---|
place_id and name | Identify the exact business checked. | Do not treat an unidentifiable record as an answer. |
detail_status | Separate an incomplete record from a complete one. | Keep popularity status unknown. |
Raw popular_times | Preserve the source shape for later parsing. | Store {}, not zero visits. |
timezone and fetch time | Make later local-time comparisons possible. | Do not assign a server time zone to the place. |
| Typical versus live label | Prevents a historical pattern from being presented as a real-time count. | Leave the label unknown rather than guessing from one number. |
| Provider and schema version | Makes later parsing changes and cross-provider audits traceable. | Keep the raw response for investigation. |
This article is about interpreting one field. If you need to choose between list discovery, selected place lookups and deeper search, use our Maps enrichment cost guide. You can inspect the response shape in the API playground before wiring it into a production report.
FAQ
Why is popular_times empty in a Google Maps API response?
Google does not show Popular Times for every place. In Serpent, an unavailable popularity field is {}. If detail_status is core_only or unknown, treat the missing detail as unknown rather than concluding the listing has no popularity data.
Does {} mean the business has no visitors?
No. It means no popularity values were returned in this response. It says nothing about actual demand or whether the business is open.
Should I retry when it is empty?
A bounded follow-up can help when the place record is incomplete. For a complete record with {}, repeated calls do not guarantee any new data. Verify the public listing when the field is essential and keep a retry budget.
How do I compare hours across time zones?
Keep the place's IANA time zone with the raw popularity object. Check the structure and test day changes before converting to another zone. If the zone is missing, delay a cross-region hourly comparison.
Can I compare two businesses by a popularity score of 100?
No. Google describes the usual graph as relative to each business's own weekly peak. Two peaks of 100 do not establish equal foot traffic or sales. Compare hours within one place, and use comparable first-party counts for cross-business volume.
Does the official Google Places API return Popular Times?
Google's published Places API (New) field list, checked October 1, 2026, does not list a Popular Times field. The public Maps interface may show a chart that is not available through that official API. Check the current field list before designing your integration.
How many API calls should I budget for 1,000 places?
If you already have 1,000 verified place IDs, one Serpent Place request each means 1,000 calls; any discovery and follow-ups add calls. At the published Default Place rate, those 1,000 calls are $1.50 in planned usage, and the standard Default allocation (up to 10,000 requests/day, 1,000/hour, 100/minute) covers them in one day, but the 1,000/hour ceiling leaves no room for discovery or retries in the same hour, so spread the job across a few hours.
Inspect the Fields Before You Build the Chart
Start with a known place, read its detail status and keep an empty popularity object as unavailable data. The Maps API overview and public API reference show the field contract.
Open the API PlaygroundSources and method
We checked Google's Popular Times explanation and official Places field list, Serpent's public response documentation and published pricing, and the linked SerpApi, DataForSEO, Outscraper, and Apify Compass product documentation on October 1, 2026. The provider table is a documented-capability comparison, not a measured availability study. The code's example output is illustrative; verify parsed output for your own place sample before relying on it.
The Outscraper community discussion is included solely as an attributed user experience. It cannot establish Serpent field coverage or Google-wide prevalence.




