1. Unit Economics: Direct API Pipeline vs Bundled Lead Credits
Using your own PlacesData API key inside Clay or n8n eliminates 5x–10x platform credit markups on local SMB prospecting.
| Pipeline Stage | Endpoint | Credits | PAYG Base Cost (1K Leads) |
|---|---|---|---|
| 1. Discover 1,000 targeted local SMBs | GET /v1/places/search?has_website=true | 5,000 Credits | $0.50 |
| 2. Crawl official domains for emails & socials | GET /v1/leads/enrich?domain=... | 20 Credits / hit (0 if empty) | $1.20 (at 60% hit rate) |
| Total All-In Enriched Pipeline | Search + Contact Enrichment | ~17,000 Credits | $1.70 per 1,000 leads |
2. End-to-End Python & Node.js Lead Enrichment Pipeline
Fetch local businesses in any target metro, filter by review velocity or rating, and enrich every business domain concurrently.
Search Local Businesses + Enrich Emails & LinkedIn URLs
Zero credits are deducted for domains that do not publish an email or social link:
curl "https://api.placesdataapi.com/v1/places/search?q=dental+clinics+in+Scottsdale+AZ&has_website=true&limit=25" \
-H "x-api-key: pd_live_your_api_key"import requests
API_KEY = "pd_live_your_api_key"
HEADERS = {"x-api-key": API_KEY}
places = requests.get(
"https://api.placesdataapi.com/v1/places/search",
headers=HEADERS,
params={"q": "dental clinics in Scottsdale AZ", "has_website": "true", "limit": 25},
timeout=30,
).json()["data"]
qualified_leads = []
for p in places:
if not p.get("domain"):
continue
contacts = requests.get(
"https://api.placesdataapi.com/v1/leads/enrich",
headers=HEADERS,
params={"domain": p["domain"]},
timeout=20,
).json().get("data", {})
if contacts.get("emails"):
qualified_leads.append({
"name": p["name"],
"phone": p["phone"],
"domain": p["domain"],
"emails": contacts["emails"],
"linkedin": contacts.get("social_links", {}).get("linkedin"),
})
print(f"Enriched {len(qualified_leads)} ready-to-send B2B leads")const headers = { "x-api-key": "pd_live_your_api_key" };
const searchRes = await fetch(
"https://api.placesdataapi.com/v1/places/search?q=dental+clinics+in+Scottsdale+AZ&has_website=true&limit=25",
{ headers }
);
const { data: places } = await searchRes.json();
const leads = [];
for (const p of places) {
if (!p.domain) continue;
const enrichRes = await fetch(
`https://api.placesdataapi.com/v1/leads/enrich?domain=${encodeURIComponent(p.domain)}`,
{ headers }
);
const { data: contacts } = await enrichRes.json();
if (contacts?.emails?.length) {
leads.push({ name: p.name, phone: p.phone, emails: contacts.emails, socials: contacts.social_links });
}
}
console.log(leads);{
"code": 0,
"provider": "https://placesdataapi.com",
"status": "ok",
"data": {
"domain": "austindentalspa.com",
"emails": ["info@austindentalspa.com", "appointments@austindentalspa.com"],
"social_links": {
"linkedin": "https://www.linkedin.com/company/austin-dental-spa",
"instagram": "https://www.instagram.com/austindentalspa"
}
},
"meta": { "contacts_found": true, "credits_charged": 20 }
}Interactive Engineering Workbench · GET /v1/places/search
Verify real-time latency, credit math & JSON schema
Frequently Asked Questions
- How do I configure PlacesData API in Clay's HTTP API integration?
- Add an HTTP API column with Method = GET, URL = https://api.placesdataapi.com/v1/leads/enrich?domain={{domain}}, and Header x-api-key: pd_live_your_api_key. Enable Clay's conditional run formula so it only runs when {{domain}} is non-empty.
- What happens if my credits run out mid-workflow?
- The gateway returns HTTP 402 with JSON fields credits_remaining, credits_required, and topup_url: https://placesdataapi.com/dashboard/topup so your workflow can pause cleanly without losing progress.