Find Local Businesses Without Websites with Python: A Measured Workflow
A blank website field can be a useful prospecting signal, but it is not proof. The defensible workflow is: collect current business records, classify only complete records, deduplicate by stable identifier, manually verify valuable candidates, and contact a small relevant set in compliance with applicable rules.
We tested that workflow locally on July 2, 2026. Five Maps Quick calls returned 100 places. Ninety-six records were complete, four were core_only, and 16 complete records had no website: 16.7% of the classifiable sample. The rate ranged from 0% to 55% by niche, which is why broad “one in three businesses” claims are not a sound forecast for a specific campaign.
Find businesses without websites: measured sample
We selected five local service/category queries in midsize U.S. cities. This was a functional sample designed to test extraction and classification, not a representative survey of all businesses.
| Query | Returned | Complete | Core-only | No website among complete |
|---|---|---|---|---|
| plumber — Tulsa, OK | 20 | 19 | 1 | 0 (0%) |
| barber — Akron, OH | 20 | 19 | 1 | 4 (21.1%) |
| food truck — Fresno, CA | 20 | 20 | 0 | 11 (55%) |
| auto repair — Boise, ID | 20 | 19 | 1 | 1 (5.3%) |
| landscaper — Tulsa, OK | 20 | 19 | 1 | 0 (0%) |
| Total | 100 | 96 | 4 | 16 (16.7%) |
The useful finding is not the 16.7% headline. It is the variance: category selection changed the observed rate from zero to more than half. Test a small sample in the exact niche and city before projecting lead volume or campaign cost.
Classification rules that prevent false leads
The Maps response marks each business complete or core_only. A core-only record preserves ranked identity but may lack website, phone, hours, or other detail-derived fields. Therefore:
- classify “no website” only when
detail_status == "complete"and the normalized website value is blank; - put core-only records in a separate review queue;
- deduplicate by
place_idordata_idbefore counting; - store the query, location, and fetch date with every candidate; and
- manually verify before outreach.
These rules avoid the most damaging error: treating incomplete enrichment as evidence that a business has no site.
Find businesses without websites with tested Python
This script ran against the local gateway on July 2, 2026. It performs the five Quick calls above, writes only complete no-website candidates, and creates a separate CSV for core-only records. Set your own test key in the environment.
import csv
import os
import requests
API_KEY = os.environ["SERPENT_API_KEY"]
BASE_URL = os.getenv("SERPENT_BASE_URL", "http://localhost:3001")
SEARCHES = [
("plumber", "Tulsa, OK"),
("barber", "Akron, OH"),
("food truck", "Fresno, CA"),
("auto repair", "Boise, ID"),
("landscaper", "Tulsa, OK"),
]
FIELDS = [
"query", "location", "rank", "name", "category", "address",
"phone", "website", "rating", "review_count",
"place_id", "data_id", "detail_status",
]
def search(query, location):
response = requests.get(
f"{BASE_URL}/api/maps/search/quick",
headers={"X-API-Key": API_KEY},
params={"q": query, "location": location, "country": "us"},
timeout=90,
)
response.raise_for_status()
payload = response.json()
if not payload.get("success"):
raise RuntimeError(payload)
return payload["places"]
def flatten_place(place, query, location):
address = place.get("address") or {}
categories = place.get("categories") or []
return {
"query": query,
"location": location,
"rank": place.get("rank", ""),
"name": place.get("name", ""),
"category": ", ".join(categories),
"address": address.get("formatted", ""),
**{field: place.get(field, "") for field in FIELDS[6:]},
}
def write_csv(path, rows):
with open(path, "w", newline="", encoding="utf-8") as handle:
writer = csv.DictWriter(handle, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(rows)
complete_without_site = {}
needs_review = {}
for query, location in SEARCHES:
for place in search(query, location):
row = flatten_place(place, query, location)
key = place.get("place_id") or place.get("data_id")
if not key:
key = f"{row['name']}|{row['address']}"
if place.get("detail_status") != "complete":
needs_review[key] = row
elif not (place.get("website") or "").strip():
complete_without_site[key] = row
write_csv("no_website_candidates.csv", complete_without_site.values())
write_csv("incomplete_records.csv", needs_review.values())
print({
"candidates": len(complete_without_site),
"needs_review": len(needs_review),
})
The dated run found 16 candidates and four incomplete records before cross-query deduplication. The example deliberately does not search for emails, infer personal contact details, or send messages.
Manual verification before calling it a lead
Even a complete record can be stale or point to a booking/social profile rather than the business’s preferred website. For each high-value candidate:
- confirm the name, address, and phone describe the intended business;
- search the exact business name and city for an official domain;
- check whether the business uses a current social or marketplace profile as its primary presence;
- exclude closed, duplicate, or obviously irrelevant listings; and
- record when and how you verified it.
A phone number or address on a public business listing does not eliminate privacy, marketing, or platform obligations. Avoid collecting sensitive personal data, and do not enrich records simply because you can.
Responsible outreach
Do not automate outreach to every row. A useful first message should show that you checked the business, identify one concrete opportunity, explain why a website would help that business, and make opting out easy. Follow the laws that apply to the recipient and channel, including the FTC’s CAN-SPAM compliance guidance for commercial email in the United States and stricter consent rules where applicable.
Good qualification signals can include a complete business record, current operating status, a meaningful review history, and a service where owned web presence clearly helps customers. Poor signals include a core-only record, a business that operates entirely through a marketplace by choice, or a generic blast with no relevance.
Cost and coverage
Maps Quick costs $15/$7.50/$3.75 per 1,000 calls at Default/Growth/Scale and returns up to 20 places per call. Five calls cost $0.075 at Default before any free allowance. New accounts get 10 free lifetime calls shared across SERP search and Maps Quick.
Do not convert that into a guaranteed cost per lead. Some searches return fewer than 20 places, niches differ sharply, records can overlap, complete records may have sites, and manual verification removes false positives. In our small sample, five calls produced 16 pre-verification candidates; another niche mix could produce none.
For the full endpoint contract, see the Google Maps business data API page. The Maps Python export tutorial explains the general CSV workflow, and the Maps API comparison shows how to normalize per-call and per-place billing.
Limitations
The measured sample is small, limited to five U.S. query/location pairs, and dated July 2, 2026. It is not a population estimate. Listings and websites change. The Serpent Maps endpoints were validated locally and are not advertised here as production-available. Review Google Maps terms, privacy rules, marketing law, and your use case with appropriate counsel.
Build the classifier, then verify the people behind the rows
Start with completion-aware Maps records and a small niche sample. Treat the CSV as a research queue—not permission to send bulk outreach.
Review the Maps API ContractFAQ
How many Maps businesses have no website?
There is no safe universal percentage. In our dated five-query sample, 16 of 96 complete records had no website, or 16.7%. The niche-level share ranged from 0% to 55%.
How do I avoid false no-website leads?
Only classify records whose detail status is complete, then manually verify high-value prospects by checking the business name, address, social profiles, and any website listed on other current public sources.
Does Maps Quick return website and phone fields?
A complete Maps Quick record can include website and phone alongside name, category, address, coordinates, rating, review count, hours, and stable identifiers. Core-only records may omit detail-derived fields and must remain unclassified.
What does the lead-finder script cost to run?
Maps Quick costs $15, $7.50, or $3.75 per 1,000 calls at Default, Growth, or Scale, with up to 20 places per call. New accounts get 10 free lifetime calls shared across SERP search and Maps Quick.
Can I automatically contact every result?
No. Verify relevance, obey applicable marketing and privacy laws, honor opt-outs, avoid sensitive personal data, and send targeted messages based on a real business need rather than bulk spam.



