How to Build a B2B Lead List from Yellow Pages (No Code)
The standard B2B lead-list problem looks like this: a sales team needs 500 fresh businesses in a city + category, the data broker quote came back at four figures with two-year-old data, and the alternative is paying an offshore VA to copy-paste from Yellow Pages for a week. Neither option is great. This guide walks through the third path — pulling the list yourself in an afternoon, without writing code, and exporting a clean CSV your SDRs can dial today.
Why This Matters for Sales and Ops Teams
The pain shows up in three places. Data brokers like ZoomInfo and Apollo charge per seat and per export and bias toward enterprise titles, which means the local-services categories you actually need are thin. Bought lists from Fiverr come with no provenance — half the numbers are dead and the addresses are years stale. And the manual workflow (open Yellow Pages, search, click each listing, paste fields into a spreadsheet, repeat) costs roughly 90 seconds per lead, which means 500 leads is 12 hours of work no one wants to do.
Yellow Pages is the quiet workhorse for this. The directory has reliable coverage of every US city, listings are kept reasonably current by the businesses themselves, and the data is fully public. The catch is that the site does not publish an API, so you either scrape it or click-collect by hand.
What Yellow Pages Actually Contains (Be Honest About This)
Before you build a workflow, know exactly what comes out the other end. Yellow Pages search results give you the following fields per listing:
| Field | Example value | Notes |
|---|---|---|
name | Joe's Plumbing & Drain | Trading name as listed |
url | https://www.yellowpages.com/los-angeles-ca/mip/joes-plumbing-12345 | The detail page |
phone | (323) 555-0142 | Primary line, US format |
address | 1234 W Pico Blvd, Los Angeles, CA 90015 | Full single-line address |
street | 1234 W Pico Blvd | Parsed component |
city | Los Angeles | Parsed component |
state | CA | Two-letter |
postal_code | 90015 | |
country | US | |
opening_hours | ["Mon-Fri 8:00-18:00", "Sat 9:00-14:00"] | Array of strings |
If you click into the detail page, you also get rating, review count, latitude/longitude, an image URL, and the most recent reviews.
What Yellow Pages does not give you: business website (occasionally present, but unreliable), owner or decision-maker name, personal email addresses, employee count, or revenue. If your SDRs need a CEO email, plan on chaining a separate enrichment step (Hunter, Apollo, Clearbit) keyed on the website domain — and accept that you will only enrich the subset of leads where Yellow Pages happens to return a website.
This honesty matters because half the failed lead-gen projects this template fixes were sold to a sales VP who expected emails out of Yellow Pages and got phones instead.
The No-Code Workflow
This is the path for non-developers. End-to-end it takes about 20 minutes for a single city + category and produces a CSV ready for dialer import.
Step 1 — Build the search URL in the browser. Go to yellowpages.com, search for your target — for example "Printing Services" with the location "Los Angeles, CA". The browser URL bar will now show something like:
https://www.yellowpages.com/search?search_terms=Printing+Services&geo_location_terms=Los+Angeles%2C+CA
Copy that URL exactly. The search_terms and geo_location_terms parameters are what Yellow Pages uses internally, and the encoded comma (%2C) in the geo string is intentional. Tweak the search in the UI until the result count and filters match what you want before you copy the URL — once you submit a scrape job, you pay (in time and credits) for whatever pages you requested.
Step 2 — Submit the scrape from the LogPose dashboard. In the LogPose app, open the Business tab and pick Yellow Pages → Search. Paste the URL into the URL field, set pages to the number of result pages you want (Yellow Pages shows about 30 listings per page, so pages: 5 gives you roughly 150 leads), and submit. The job ID appears in the Jobs panel; most 5-page searches complete in 60–90 seconds.
Step 3 — Download the CSV. When the job moves to completed, click Download → CSV. The file has one row per listing with the columns described in the table above. Here is what the first few rows look like:
name,phone,address
Joe's Plumbing & Drain,(323) 555-0142,1234 W Pico Blvd Los Angeles CA 90015
Westside Print Co,(310) 555-0177,4567 Sawtelle Blvd Los Angeles CA 90066
Downtown Copy & Print,(213) 555-0188,890 S Spring St Los Angeles CA 90014
Step 4 — Clean and dedupe in Sheets. Paste the CSV into a new Google Sheet. Add a helper column =REGEXREPLACE(B2,"[^0-9]","") to normalize the phone numbers. Then Data → Remove duplicates, keyed on the normalized phone column. A 5-page export usually collapses by 10–20% — Yellow Pages lists the same business under multiple sub-categories.
Step 5 — Repeat for adjacent geos and categories. Run the same flow for Long Beach, CA, Pasadena, CA, and any other relevant cities. Append each export to the same sheet, re-run the dedupe, and you have a regional lead list in under an hour.
The API Path (When You Outgrow the Dashboard)
The dashboard flow works fine for one-off runs. Once you want to refresh the list weekly or pull 50 cities at once, the API path is faster. Every Yellow Pages job is asynchronous — you submit, get a job_id, poll until done, then fetch the result.
The simplest path is curl:
# Submit
curl -G "https://api.logposervices.com/api/v1/ecommerce/yellowpages/search" \
-H "X-API-Key: lp_xxxxxxx" \
--data-urlencode "url=https://www.yellowpages.com/search?search_terms=Printing+Services&geo_location_terms=Los+Angeles%2C+CA" \
--data-urlencode "pages=5"
# → {"job_id": "yp_8f3a..."}
# Poll (or use ?wait=true)
curl -H "X-API-Key: lp_xxxxxxx" \
"https://api.logposervices.com/api/v1/jobs/yp_8f3a?wait=true&timeout=60"
# Fetch result
curl -H "X-API-Key: lp_xxxxxxx" \
https://api.logposervices.com/api/v1/jobs/yp_8f3a/result
For Python the reusable helper looks like this:
import os, time, requests
API_KEY = os.environ["LOGPOSE_API_KEY"]
BASE = "https://api.logposervices.com/api/v1"
HEADERS = {"X-API-Key": API_KEY}
def submit_and_wait(path: str, params: dict, timeout_s: int = 120) -> dict:
r = requests.get(f"{BASE}/{path}", params=params, headers=HEADERS, timeout=30)
r.raise_for_status()
job_id = r.json()["job_id"]
deadline = time.time() + timeout_s
while time.time() < deadline:
s = requests.get(f"{BASE}/jobs/{job_id}", headers=HEADERS, timeout=15).json()
if s["status"] == "completed":
break
if s["status"] == "failed":
raise RuntimeError(s.get("error", "unknown failure"))
time.sleep(2)
else:
raise TimeoutError(f"job {job_id} did not finish in {timeout_s}s")
return requests.get(f"{BASE}/jobs/{job_id}/result", headers=HEADERS, timeout=15).json()
if __name__ == "__main__":
data = submit_and_wait(
"ecommerce/yellowpages/search",
{
"url": "https://www.yellowpages.com/search?search_terms=Printing+Services&geo_location_terms=Los+Angeles%2C+CA",
"pages": 5,
},
)
print(f"{len(data['listings'])} listings")
Cleaning the Output for Sales Use
A raw 5-page export is usually 130–150 rows. To turn it into something an SDR can dial without complaining, do three things in pandas (or Sheets if you prefer):
import pandas as pd
df = pd.DataFrame(data["listings"])
# 1. Normalize the phone number to E.164
df["phone_e164"] = "+1" + df["phone"].str.replace(r"[^0-9]", "", regex=True).str[-10:]
# 2. Dedupe by phone
df = df.drop_duplicates(subset="phone_e164", keep="first")
# 3. Drop listings with no phone (rare but they exist)
df = df[df["phone_e164"].str.len() == 12]
# 4. Sort by city so SDRs can split the list by territory
df = df.sort_values(["city", "name"])
df.to_csv("la_printers_leads.csv", index=False)
For Yellow Pages business-detail scrapes, you also get review_count and rating. Filtering for review_count >= 5 is a cheap proxy for "this business actually exists and is operating" — the long tail of zero-review listings includes a meaningful number of closed locations.
Scaling Beyond a Single City or Category
The single-search workflow caps out around 3,000 listings per category-city pair. For wider coverage you have two options.
Run multiple searches with the bulk endpoint. Submit a list of URL + page-count pairs in one request, and LogPose schedules them across the proxy pool with deduplication on its end:
import requests, os
requests.post(
"https://api.logposervices.com/api/v1/ecommerce/yellowpages/search/bulk",
headers={"X-API-Key": os.environ["LOGPOSE_API_KEY"]},
json={
"targets": [
{"url": "https://www.yellowpages.com/search?search_terms=Plumbers&geo_location_terms=Los+Angeles%2C+CA", "pages": 10},
{"url": "https://www.yellowpages.com/search?search_terms=Plumbers&geo_location_terms=San+Diego%2C+CA", "pages": 10},
{"url": "https://www.yellowpages.com/search?search_terms=Plumbers&geo_location_terms=Sacramento%2C+CA", "pages": 10},
],
},
).raise_for_status()
Schedule a recurring refresh. Once you have a working query set, run it weekly on a cron and diff against the prior week — anything new is a fresh lead. For the diff pattern specifically, see the companion post How to monitor Yellow Pages for new businesses in your category.
If you want a richer field set (website + category + lat/lng + hours), Google Maps is the better source. The trade-off is that Yellow Pages has better coverage of the long tail of single-location US businesses, while Google Maps skews toward businesses that actively maintain their listing. Most lead programs end up scraping both and merging by phone — see How to scrape Google Maps for local business leads.
Legality and Ethics
Yellow Pages business listings are public, and US courts have repeatedly ruled that scraping public web data is not a CFAA violation. The compliance work is downstream: cold-emailing under GDPR requires a legitimate interest assessment, CAN-SPAM requires an unsubscribe link and a physical address in every message, and the FTC enforces accuracy of from-headers. Calling published business landlines is broadly fine in the US, but state DNC rules vary and the federal business-to-business exemption does not cover every state. None of this is legal advice — get a one-hour consult with a marketing lawyer before launching a high-volume outbound program.
Common Mistakes
- Forgetting pagination. A default
pages=1only gives you 30 listings. Setpagesbased on the result count Yellow Pages shows you in the UI. - Pasting the URL with stripped escapes. The
%2Cbetween city and state matters. If you decode the URL by hand, the query breaks. - Skipping dedupe. Yellow Pages cross-lists businesses under multiple sub-categories. Expect 10–20% duplicates across a wide search.
- Assuming the address is parsed perfectly. The
street/city/state/postal_codefields work for 95% of listings; the remaining 5% have edge cases (PO boxes, suite numbers in odd positions). Don't build downstream logic that crashes on missing components. - Ignoring the Cloudflare 100-second edge timeout.
api.logposervices.comis fronted by Cloudflare, so a long single job that takes 100+ seconds appears to your client as a 524 even though the job continues server-side. Always poll for status; never expect a long synchronous response.
Get Started
- Sign up at logposervices.com and generate an API key under Tool → API Keys.
- In the dashboard, open Business → Yellow Pages → Search, paste a YP search URL, and submit a 5-page job.
- Download the CSV, dedupe by phone in Sheets, and ship it to your SDRs.
Related reading: How to scrape Google Maps for local business leads, How to monitor Yellow Pages for new businesses, and the broader web scraping API guide for when to roll your own vs use a managed service.
External: Yellow Pages search, CAN-SPAM Act compliance guide, ICO guidance on B2B marketing under GDPR.