How to Scrape Yelp Business Listings in 2026
How to Scrape Yelp Business Listings in 2026
Yelp is a moderately hard scraping target — Cloudflare Turnstile on most pages, aggressive ToS enforcement on bulk extraction, and an official Fusion API at $0-95/mo that solves 80% of asker use cases.
If you want business names, addresses, phone numbers, ratings, review counts: use the Fusion API. If you want full review text or "businesses near coordinates" at scale: scraping is the tougher path.
The honest verdict first
| Use case | Recommendation |
|---|---|
| Business search by category + location | Yelp Fusion API (free tier ~5k req/day) |
| Business details (hours, phone, address) | Yelp Fusion API |
| Bulk review text + content | Scraping (with anti-bot stack) — but consider whether you actually need it |
| Lead-gen for B2B sales | Apollo / Lusha / Hunter — Yelp data is not what you want |
| One-off list of "100 dental clinics in Boston" | Yelp Fusion API or DIY with curl_cffi |
| Compete with Yelp | Don't, legally |
The Yelp Fusion API (the right answer)
Free tier: 5,000 requests/day. That's enough for most personal/research use. Sign up at yelp.com/developers.
import requests
def search_yelp(term: str, location: str, key: str, limit: int = 50) -> list[dict]:
r = requests.get("https://api.yelp.com/v3/businesses/search", params={
"term": term, "location": location, "limit": limit
}, headers={"Authorization": f"Bearer {key}"})
return r.json().get("businesses", [])
for biz in search_yelp("dental", "Boston, MA", key="YOUR_KEY"):
print(biz["name"], biz["rating"], biz["phone"], biz["location"]["address1"])
What you get per business: name, rating (1-5), review_count, phone, address (split into address1/2/3, city, state, zip), location lat/lon, categories, price tier, image URL, url.
What you don't get: full review text, business owner contact info, hours-of-operation across multi-locations.
Pricing: free up to 5k/day. Beyond that, Yelp doesn't publicly sell paid tiers — you contact their partner team. For most freelancing use cases, the free tier is plenty.
The DIY scraping path (low volume only)
If you absolutely need data the API doesn't expose, or you can't get an API key, the DIY path:
import time
from random import uniform
from curl_cffi import requests as cf
from bs4 import BeautifulSoup
def yelp_search(term: str, location: str, n: int = 30) -> list[dict]:
base = "https://www.yelp.com/search"
params = {"find_desc": term, "find_loc": location}
r = cf.get(base, params=params, impersonate="chrome131", timeout=20)
if "challenges.cloudflare.com" in r.text or r.status_code != 200:
raise RuntimeError(f"Cloudflare challenge or block: {r.status_code}")
soup = BeautifulSoup(r.text, "html.parser")
results = []
for el in soup.select("[data-testid='serp-ia-card']")[:n]:
name_el = el.select_one("a[href*='/biz/']")
rating_el = el.select_one("[role='img'][aria-label*='rating']")
if not name_el:
continue
name = name_el.get_text(strip=True).split(".", 1)[-1].strip() # strip leading numeric
url = "https://www.yelp.com" + name_el["href"].split("?")[0]
rating = rating_el["aria-label"] if rating_el else None
results.append({"name": name, "url": url, "rating": rating})
return results
# Example
for r in yelp_search("dental", "Boston, MA", n=30):
print(r["name"], "-", r["rating"])
time.sleep(uniform(3.0, 6.0))
Then per-business detail scrape (after a delay):
def yelp_business(url: str) -> dict:
r = cf.get(url, impersonate="chrome131", timeout=20)
soup = BeautifulSoup(r.text, "html.parser")
return {
"phone": next((p.get_text(strip=True) for p in soup.select("p")
if p.get_text(strip=True).startswith("(")), None),
"address": " ".join(s.get_text(strip=True) for s in
soup.select("address p, address span")),
# CSS selectors drift — see "Self-healing extractors" tutorial for the resilient version
}
Limitations:
- Yelp's CSS classes change frequently. Expect 1-2 days/quarter maintenance.
- Cloudflare blocks at moderate volumes (>50 requests/min).
- Anonymous scraping returns less data than logged-in scraping (which adds a ToS contract layer).
- Review text is partially loaded by JS — needs Playwright for full extraction.
What lead-gen freelancers actually want
The most common Yelp-scraping briefs I see on Upwork are "build me a list of [profession] businesses in [city]." Three honest paths:
- Yelp Fusion API (free tier) for the basic search. Yields ~5,000 records/day. Sufficient for almost any one-off list.
- Google Maps via Places API ($200 free credit/month from Google). Different data shape, often complementary to Yelp. The places-search endpoint works for "type=dentist&radius=10km" queries.
- Apollo / Lusha for the B2B contact-data version. If your goal is "find me email addresses for these business owners," Apollo will out-perform anything you scrape from Yelp.
Combining all three (Yelp + Google Places + Apollo enrichment) gives you a more complete dataset than scraping Yelp alone.
Legal & ToS
Yelp's ToS forbids automated access without their API key. They've actively pursued companies redistributing scraped Yelp data; the most cited case is Yelp v. ReviewVoIP, which Yelp won.
Practical rules:
- Use the API for anything you'll deploy or sell
- Don't republish full Yelp reviews — they're user-content, copyright-claimable
- Don't compete with Yelp using Yelp data
- Personal/internal use is the safest territory
What I tell freelance clients who ask
For "build me a list of 500 dental clinics in 10 cities" briefs:
"Yelp has a free API for exactly this — Fusion API, free tier handles 5k requests/day. I'd charge $200 fixed-price to write the script, which uses the API legally and ships in 24h. The scraping alternative would be 3-5x the work, brittle, and ToS-risky. Want the API version?"
Most clients take the API version. The few who don't are clients I usually decline.
What to read next
- How to Scrape Google Search Results — companion target for lead-gen briefs
- Web Scraping Tools Comparison — managed vs DIY math
- Web Scraping Legal & Ethics — ToS landscape
For a Yelp-shaped lead-gen brief, send to info@luba.media. I'll quote fixed-price within 24h.
Hire me to build this for your site
I quote fixed-price and ship in 7-10 days. Send a brief to info@luba.media.
Send a brief