Skip to content
All posts

How to find restaurants without a website in any city

Leads and local businesses5 min read
How to find restaurants without a website in any city: the post's first code sample

If you build websites for small businesses, restaurants are some of the best prospects: they depend on being found, their menu and hours change, and many still rely on a Facebook page. This guide shows how to find restaurants without a website in any city, keep only the ones with a phone number you can call, and get the list as JSON or CSV.

Why restaurants, and why "no website"

A restaurant with no site of its own loses the customers who search for a menu, opening hours or a booking link. A web agency can show it a clear before and after, and the sale does not need a long explanation. The hard part is building the list: a restaurant with no website is exactly the one that does not show up in search results.

OpenStreetMap maps restaurants with their address, phone, hours and, when there is one, their website. POST /v1/places queries that data for one category in one place and filters it, so "phone yes, website no" is two fields in the request.

The request

Send "category": "restaurant", a location (a city name, ideally with the country or state) and the two filters. website, phone and email each take "any", "yes" or "no"; anything you leave out means "any".

curl
curl https://scrape.land/v1/places \
  -H "X-Api-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category": "restaurant",
       "location": "Porto, Portugal",
       "website": "no",
       "phone": "yes"}'

If you want an exact area rather than a city's boundary, send "bbox": [south, west, north, east] in place of location. One search covers about a large metro area; ask for something much bigger and you get a 400 asking for a smaller region.

What came back for Porto

This is a real response from an account on the Free plan, trimmed to four of the ten results:

JSON
{
  "attribution": "© OpenStreetMap contributors, ODbL (openstreetmap.org/copyright)",
  "bbox": [41.1383507, -8.691289, 41.1859353, -8.5520792],
  "category": "restaurant",
  "count": 10,
  "matched": 116,
  "preview": true,
  "preview_note": "Free plan: the first 10 matches. Every paid plan returns up to 200 per call.",
  "results": [
    {
      "name": "A Capoeira",
      "category": "restaurant",
      "address": "63 Esplanada do Castelo",
      "lat": 41.149753,
      "lon": -8.674453,
      "phone": "+351226181589",
      "opening_hours": "Mo-Su 12:30-14:30, 20:00-23:00",
      "osm_url": "https://www.openstreetmap.org/node/5915520403"
    },
    {
      "name": "A Case do Evaristo",
      "category": "restaurant",
      "address": "535 Rua Fernandes Tomás",
      "lat": 41.14951,
      "lon": -8.604942,
      "profile": "http://www.facebook.com/acasadoevaristo",
      "phone": "+351222011398",
      "osm_url": "https://www.openstreetmap.org/node/4376038633"
    },
    {
      "name": "A Cozinha do Martinho",
      "category": "restaurant",
      "address": "2598-2606 Rua de Costa Cabral, 4200-219",
      "city": "Porto",
      "lat": 41.178439,
      "lon": -8.584224,
      "phone": "+351 224 961 859",
      "opening_hours": "Mo-Sa 12:00-15:00, 19:00-22:00",
      "osm_url": "https://www.openstreetmap.org/node/4710940318"
    },
    {
      "name": "Adega São Pedro",
      "category": "restaurant",
      "address": "10 Rua do Esteiro de Campanhã",
      "lat": 41.144741,
      "lon": -8.576718,
      "profile": "https://www.facebook.com/adegasaopedro/",
      "phone": "+351225305009",
      "opening_hours": "Tu-Sa 10:00-22:00; Su 11:00-15:00",
      "osm_url": "https://www.openstreetmap.org/node/13311108686"
    }
    … 6 more
  ],
  "units": 1
}

A few things to read from it:

What "profile" means

Two of the four restaurants above have a profile field with a Facebook page. That is deliberate. When the only "website" a business lists is a social media page, a booking or delivery portal, a directory listing or a link-in-bio page, the API does not count it as a website. It moves the link to profile and leaves website empty, so the business still matches "website": "no".

For an agency, those are often the warmest leads. A restaurant that bothered to set up a Facebook page cares about being found online; it just does not own a site. You can open the profile to see how active they are before you get in touch.

If you want only the businesses with no online presence at all, drop the rows that have a profile after you receive them.

Save it as a CSV

Python
import csv
import requests

r = requests.post(
    "https://scrape.land/v1/places",
    headers={"X-Api-Key": "YOUR_KEY"},
    json={"category": "restaurant", "location": "Porto, Portugal",
          "website": "no", "phone": "yes"},
    timeout=60,
)
r.raise_for_status()
body = r.json()

cols = ["name", "phone", "address", "city", "profile", "opening_hours", "osm_url"]
with open("porto-restaurants-no-website.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=cols, extrasaction="ignore")
    w.writeheader()
    w.writerows(body["results"])

with_profile = sum(1 for p in body["results"] if p.get("profile"))
print(body["count"], "rows,", body["matched"], "matched,", with_profile, "with a social profile")

Swap the city and run it again for the next one. Each run is a separate search, and a repeat of a recent search is answered from cache.

Other categories that work the same way

The same filters work for cafés, bars, hairdressers, beauty salons, bakeries, florists, plumbers, electricians, car repair shops and more. GET /v1/capabilities with your key lists every category under entitlements.places.categories. If you want to look at the map first, the dashboard's Local businesses tool runs the same search with a map and a CSV button.

Before you pitch

Use only the public business details in the list. Check the rules on unsolicited calls and emails where you and the restaurant are (in the EU that means GDPR and ePrivacy), keep it to one relevant offer, and stop when someone says no. The data is © OpenStreetMap contributors under the ODbL, and the attribution string should travel with it if you share it.

What it costs

1 request unit per 10 rows returned, at least 1 per search. The Porto search above cost 1 unit. A full 200-row result costs 20 units. If the map data service is busy you get a 502 and are not billed. See pricing.

Next steps

To run many cities and categories in one go, see how to export local business leads to CSV with Python. The broader version of this guide, for any category, is how to find local businesses without a website. All request options are in the docs.

Start free with 1,000 requests Read the docs