Facebook B2b Lead Scraper
Pricing
$19.99/month + usage
Facebook B2b Lead Scraper
Extract targeted business prospects from Facebook using the Facebook B2B Lead Scraper. Collect company pages, descriptions, emails, websites, phone numbers, and follower metrics automatically. Ideal for B2B outreach, lead generation, and market research.
Pricing
$19.99/month + usage
Rating
4.0
(1)
Developer
Scraper Engine
Maintained by CommunityActor stats
1
Bookmarked
29
Total users
0
Monthly active users
11 days ago
Last modified
Categories
Share
Facebook B2B Lead Scraper ๐ข
Find companies on Facebook and get their real business email.
You give it industry keywords (marketing agency, consulting firm, freight forwarder). It finds
public Facebook business Pages that match, opens each Page's public About tab, and reads the
contact details the business itself published there โ company name, category, website, and the email
address it uses for enquiries.
The result is a corporate contact list: info@, sales@, contact@, hello@ on the company's own
domain โ not personal profiles, not Marketplace listings, not group chatter.
{"network": "facebook.com","keyword": "consulting firm","title": "KEP Consulting Firm (@kepconsultingfirm) - About","description": "KEP Consulting Firm, Pretoria. 164 likes. We are an audit and consultancy firm offering services to the public and private sectors.","url": "https://www.facebook.com/kepconsultingfirm","email": "info@kepconsulting.co.za","companyName": "KEP Consulting Firm","category": "Financial service","website": "http://www.kepconsulting.co.za/","emailSource": "facebook_page_about"}
๐ Where the email actually comes from
This section is deliberately blunt, because "where did this address come from?" is the only question
that matters when you are about to email a stranger. Every row carries an emailSource field
naming its origin. There are exactly three, in priority order:
emailSource | What it means | Typical share |
|---|---|---|
facebook_page_about | The business published this address in the Email field of its public Facebook Page About tab. Highest confidence โ the company put it there to be contacted on. | Most rows |
company_website:<host> | The Page listed a website but no email, so the actor fetched that company's own site (home, /contact, /contact-us, /about, /about-us) and took the best corporate mailbox found, preferring mailto: links. | Some rows |
google_search_snippet | Neither surface yielded an address, but one was visible in the search-result snippet for that Page. Lowest confidence โ kept only as a last resort. | Rare |
Nothing else is ever emitted. The actor never guesses an address from a pattern
(firstname.lastname@company.com), never invents sample rows, and never writes a placeholder into a
field. If a source yields nothing, the run log says so and the row is dropped.
What is NOT reachable (tested, reported honestly)
These were all tested live before this actor was built. They do not work and are not used:
- Unauthenticated Graph API โ
graph.facebook.com/<page>?fields=emailsreturns{"error":{"message":"(#200) Provide valid app ID"}}(HTTP 403). No app token, no data. - Page oEmbed โ
facebook.com/plugins/page/oembed.json/returns HTTP 404. Retired. - The Page plugin iframe โ
facebook.com/plugins/page.phpreturns HTTP 200 but carries only the name, cover photo and a like button. No contact fields. mbasic.facebook.comโ still resolves, but with a plain client it serves the "Log in or sign up to view" wall. It is not a usable bypass and is not relied on.- Google's result links โ Google now wraps every organic result as
/goto?url=CAES<encrypted>. The payload is encrypted server-side; following it returns HTTP 400. It is not decodable, and this actor does not pretend to decode it (see the fix below). - Google's
<cite>breadcrumb โ useless for Facebook specifically: Facebook results render their cite line as"40+ comments ยท 2 years ago", not as a URL.
What IS reachable
https://www.facebook.com/<page>/about returns HTTP 200 with the full logged-out markup โ
og: metadata plus a profile_fields payload containing Email, Website, Phone, Address, Category and
opening hours โ provided the request goes through a residential proxy and carries a complete
browser navigation header set. The identical URL with a minimal user-agent + accept header pair
returns HTTP 400 and a 1.5 KB stub. That header detail is the whole ballgame, and it is why this
actor sends the full Chrome header set on every Facebook request.
๐ ๏ธ What was broken, and what fixed it
This actor previously returned zero rows on every run. Two independent causes, both fixed.
1. Every search result was silently discarded
The old code walked the anchors on Google's results page and called _unwrap_google_url() on each
href. Google has since replaced direct result links with an opaque wrapper:
href="/goto?url=CAESfgHrOzAVYCzaRlwZ4cm6ikcGdtCqPD3psODc011WCUlw..."
_unwrap_google_url() returned that literal relative string, which then failed the
url.startswith(("http://", "https://")) guard, so every single result was skipped. Measured
live on this actor's own query: 19 anchors, 0 direct, 19 wrapped โ 0 rows.
The fix. The same page still embeds the real destination in an inline JSON data island a few hundred bytes after each wrapper:
["https://www.facebook.com/ProknowaraConsulting/about/","Proknowara Consulting Firm ...","... Consulting agency in Dhaka ... marketing@proknowara.com ...", 1, "en", ...]
google_blob_results() scans for ["http and lets json.JSONDecoder.raw_decode consume the array.
This reads the data payload rather than the rendered DOM, so it is immune to Google's rotating
obfuscated CSS class names. Three tiers are tried in order โ JSON island, classic anchor walk
(for direct and legacy /url?q= layouts), then a raw-text URL sweep โ and _unwrap_google_url() now
returns "" for a /goto? wrapper so callers fall through cleanly. Locale subdomains
(it-it.facebook.com/...) are accepted and normalised back to www.facebook.com.
2. The email was never fetched, only hoped for
The old pipeline regex-scanned the search-result snippet and nothing else. An email is almost never in a snippet, so even with cause 1 fixed the yield would have stayed near zero.
The fix. Discovery and extraction are now separate stages. Search finds the Page; the email comes from actually fetching that Page's About tab, and from the company's own website when the Page lists one but no address.
๐ข Why this actor and not the other Facebook scrapers
There are sibling actors for general Facebook search, for Pages, and for personal profiles. This one is the B2B member of the family, and it is built differently on purpose.
It searches for companies, not for emails. The sibling actors query
site:facebook.com "@gmail.com" "<keyword>" โ which only ever surfaces pages where an address is
already visible in Google's snippet. In practice that means group threads and job ads: consumer and
community content. This actor instead targets the Page About tab directly:
site:facebook.com/*/about "<keyword>"
Measured live on consulting firm: 10/10 results were company Page /about URLs, versus 6/10
group threads for the old query โ and 9 of those 10 Pages published a real corporate email.
It rejects the consumer surfaces outright. Group threads, Marketplace listings, personal timelines, events, reels, photo and post permalinks are all filtered out by path. Those carry personal and classified-ad contacts, which is what the sibling actors are for.
It ranks for commercial relevance. Candidates are scored and the best contact wins:
| Score | Address |
|---|---|
| 100 | Role mailbox on the company's own domain โ info@acme.co.uk |
| 90 | Any address on the company's own domain โ jane@acme.co.uk |
| 70 | Role mailbox on another corporate domain |
| 55 | Any other corporate domain |
| 35 | Role mailbox on free webmail โ sales.acme@gmail.com |
| 10 | Plain free webmail โ joe@gmail.com |
It returns firmographics, not just an address. companyName, category and website come out
alongside the email, so a row is usable in a CRM without a second enrichment pass.
โ ๏ธ One deliberate default change: emailDomains now starts empty
The input schema used to pre-fill emailDomains with ["@gmail.com"]. For a B2B tool that default
is actively harmful: genuine business contacts overwhelmingly sit on the company's own domain, so
pre-filtering to Gmail discards nearly every real lead the actor finds. In the verified sample above,
9 of 10 addresses would have been thrown away.
The filter semantics are unchanged โ emailDomains still keeps only the domains you list. Only
the prefill changed, from ["@gmail.com"] to empty (= keep every domain). Type @gmail.com back
in whenever you specifically want sole traders on free webmail.
๐งฉ Input
| Field | Type | Default | Description |
|---|---|---|---|
keywords | array | ["consulting firm"] | Required. Industry/trade keywords. Trade terms beat generic words. |
platform | string | Facebook | Network to mine. Facebook only for now. |
location | string | "" | Optional city/region/country filter. Narrow values cut results sharply. |
emailDomains | array | [] | Keep only these email domains. Empty = all domains (recommended for B2B). |
maxEmails | integer | 20 | Leads per keyword (1โ5000). |
engine | string | legacy | Processing engine. |
enrichFromWebsite | boolean | true | Advanced. Also fetch the company's own site when the Page lists no email. |
requireBusinessEmail | boolean | false | Advanced. Drop free-webmail leads; keep only corporate domains. |
maxSearchPages | integer | 6 | Advanced. Search-result pages to walk per keyword (1โ20). |
proxyConfiguration | object | RESIDENTIAL | Network. Proxy for Facebook and website fetching. |
{"keywords": ["consulting firm", "marketing agency"],"location": "London","emailDomains": [],"maxEmails": 20,"requireBusinessEmail": true,"platform": "Facebook","engine": "legacy"}
About the proxy
Two proxies are used, and they are not interchangeable:
- Google search always runs through the Apify GOOGLE_SERP group. That group can only reach
Google's own hosts โ it cannot fetch
facebook.comat all โ so it is not configurable here. - Facebook Pages and company websites are fetched through
proxyConfiguration, defaulting to RESIDENTIAL. This is what makes Facebook serve its logged-out Page markup.
Setting proxyConfiguration to a datacenter group will degrade Facebook fetching. Leave the default
unless you know why you are changing it.
๐ค Output
Every field carries a real value on every row. A lead missing any field is dropped, not padded with a blank โ an empty column is worse than one fewer row, and the run log names what was skipped.
| Field | Description |
|---|---|
network | Facebook host the result matched (facebook.com, fb.com, โฆ) |
keyword | Keyword that surfaced this company |
title | Page title as indexed |
description | The Page's public description |
url | Canonical Facebook Page URL |
email | The business contact address |
companyName | Business name, cleaned of | City and (@slug) noise |
category | Facebook business category โ Marketing Agency, Business Consultant, โฆ |
website | The company's own website |
emailSource | Where the email came from โ see the table at the top |
๐ฏ Use cases
- Outbound sales prospecting โ build a list of agencies, consultancies or contractors in a city,
each with a monitored
info@/sales@inbox. - Partner and supplier sourcing โ find companies in a trade category with a working contact route.
- CRM enrichment โ match an existing account list against Facebook Pages to fill in category, website and a contact address.
- Market mapping โ enumerate every business Page in a category and location to size a segment.
โ๏ธ Legal and ethical use
This actor reads only publicly available information: pages Facebook serves to a logged-out visitor, and companies' own public websites. It does not log in, does not bypass any authentication, and does not touch private profiles or non-public fields.
The addresses it returns are ones businesses published for the purpose of being contacted. That does not exempt you from the rules that apply to using them. You are responsible for complying with GDPR, CAN-SPAM, PECR and the equivalent laws in your market โ including having a lawful basis for contact, identifying yourself, and honouring opt-outs. Use a reasonable send volume and do not resell scraped contact data as a list product.
Support
- Bug reports: open a ticket in the repository Issues section
- Custom features: dev.scraperengine@gmail.com
FAQ
Where exactly does the email come from?
The emailSource field on every row says so explicitly. See the table at the top of this README.
Why did I get fewer rows than maxEmails?
Not every business Page publishes an email. The actor drops incomplete leads rather than padding the
dataset. Broaden the keyword, clear the location filter, or raise maxSearchPages.
Can it scrape personal profiles? No โ by design. Personal timelines, group posts and Marketplace listings are filtered out. Use the sibling profile/page/general Facebook actors for those.
Can it get emails hidden behind Facebook's login? No, and it does not claim to. Only what Facebook serves publicly is read.
Why is emailDomains empty by default now?
Because pre-filtering to @gmail.com discarded roughly 9 out of every 10 genuine B2B leads. The
filter itself works exactly as before; only the starting value changed.
Does it use the Facebook API?
No. The unauthenticated Graph API returns (#200) Provide valid app ID and is unusable without an
app token. Public page markup is used instead.