LinkedIn Phone Number Scraper: Lead Scoring avatar

LinkedIn Phone Number Scraper: Lead Scoring

Pricing

$19.99/month + usage

Go to Apify Store
LinkedIn Phone Number Scraper: Lead Scoring

LinkedIn Phone Number Scraper: Lead Scoring

LinkedIn Lead & Contact Finder (Google SERP) extracts publicly listed phone numbers from LinkedIn profiles and linked pages. Build targeted contact lists by role, industry, or company. Ideal for sales teams running outbound campaigns.

Pricing

$19.99/month + usage

Rating

0.0

(0)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

1

Bookmarked

81

Total users

14

Monthly active users

12 days ago

Last modified

Share

LinkedIn Phone Number Scraper — Lead Scores, Grades and E.164 Numbers

LinkedIn Phone Number Scraper: Lead Scoring finds publicly indexed phone numbers on linkedin.com and returns each one as a scored lead: phone_number normalised to E.164, the url it came from, the result title and description, plus an Actor-computed leadScore (0-100), a leadGrade (A–F), a derived seniorityLevel and a best-effort company. Thirteen flat JSON keys per row, no parsing required. Give it keywords and a country, press Start, and scored rows land in the dataset as each results page is parsed.

⚠️ This Actor reads Google's index, not LinkedIn pages. Despite the name it never sends a request to LinkedIn. It runs site:linkedin.com "<dial code>" "<keyword>" queries through Apify's GOOGLE_SERP proxy and pulls phone numbers out of the result titles and snippets Google has indexed. That means no login and no cookie — but also no LinkedIn profile fields: no structured job title, no employer record, no connection count. The seniority and company values are inferred from the snippet text, not read from LinkedIn.

What is LinkedIn Phone Number Scraper: Lead Scoring?

LinkedIn Phone Number Scraper: Lead Scoring is an Apify Actor that turns a keyword and a country into a ranked list of publicly indexed linkedin.com pages that show a phone number in visible text. For each keyword it pages through Google search results, keeps only links to linkedin.com, scans each result block for a phone-shaped string, normalises it against your country's dial code, then scores the result as an outreach lead from its own title, snippet and phone validity.

No LinkedIn account, login, session cookie or API token is required, because LinkedIn is never contacted — the only credential involved is your Apify token. The Actor selects Apify's GOOGLE_SERP proxy for you.

It is built for sales and lead-generation teams that want a prioritised call list rather than a raw dump, recruiters sourcing by role, and developers piping scored contact records into a CRM or an AI pipeline.

What LinkedIn contact data is publicly available to scrape?

A Google result row for a linkedin.com page carries a title, a URL and a snippet — nothing more. Everything else about the person or company behind that URL sits on the LinkedIn page itself, which this Actor never opens.

Data CategoryOn the Google result (returned here)Needs a LinkedIn page fetch or login
Phone number written in indexed page text✅ Returned as phone_number, normalised to E.164
The linkedin.com profile, company or post URL✅ Returned as url
Google's result title, usually Name - Role - Company | LinkedIn✅ Returned as title
Snippet text surrounding the number✅ Returned as description
Role seniority✅ Inferred as seniorityLevel from title and snippet wordingLinkedIn's own structured job title — not returned
Employer✅ Best-effort company parsed out of the snippetLinkedIn's structured employer record — not returned
Email addresses as their own fieldNot parsed — only phone numbers are extracted
Contact-info panel, connections, InMailLogin and, in most cases, a connection
Numbers on LinkedIn pages Google has not indexedOut of reach — coverage is Google's index

LinkedIn Phone Number Scraper: Lead Scoring only returns publicly visible data — what any visitor sees on a Google results page. Nothing behind a login wall.

What data can I extract with LinkedIn Phone Number Scraper: Lead Scoring?

Every row carries the same thirteen keys: eight describing the search result and the number found in it, and five computed by the Actor from those eight.

Field NameDescription
platformPlatform label the search ran against. With the schema's only allowed value this is always Linkedin.com
keywordThe exact entry from your keywords list that produced this row
titleText of the Google result's <h3> heading, whitespace-normalised
descriptionThe Google snippet for that result, or "" when no snippet element matched
urlThe linkedin.com URL, unwrapped from Google's /url?q= redirect when present
phone_numberThe number normalised to + followed by digits only, e.g. +447585467400
countryCountry name with the dial code stripped off, e.g. United Kingdom
dial_codeDial code used to build the query, normalise the number and filter it, e.g. +44
leadScoreActor-computed outreach score from 0 to 100 — see the scoring section below
leadGradeLetter band derived from leadScore: A, B, C, D or F
seniorityLevelSeniority tier matched in the title and snippet: Founder/Owner, C-Suite, VP, Director, Manager, Senior IC or Unknown
companyBest-effort company name parsed from the title or snippet, truncated to 80 characters, or null when nothing company-like is present
titleMatchtrue when any of your titleKeywords appears in the title or snippet, otherwise false

Result identity and source fields

platform, keyword, title, description and url describe where the number came from. keyword is the partition key you will use most — one run covers many keywords and rows are not otherwise separated. url is the link a human needs to verify a lead before it is worked, and it points at whatever LinkedIn page Google indexed, which may be a personal profile (/in/…), a company page (/company/…) or a post.

description is the highest-information text field in the row: it is the snippet Google showed, so it usually contains the number in its original written form along with the surrounding words. It falls back to "" rather than null when the snippet element does not match.

phone_number, country and dial_code are the normalised half of the source data. country and dial_code come from splitting your single country input — the schema stores it as one combined string like United Kingdom (+44), which is split into United Kingdom as the name and +44 as the dial code.

The dial code does real work in three places: it becomes a quoted term in the Google query, it is prepended to numbers written in national trunk form, and a candidate is discarded unless the normalised result starts with it. Normalisation is deliberately conservative — text is NFKC-normalised, non-breaking spaces converted and zero-width characters (U+200B, U+200C, U+200D, U+FEFF) stripped; a leading 00 becomes +; a duplicated country code (4444…) is collapsed; a trunk zero written straight after the country code, as in +44 (0)7585 467400, is dropped; and anything whose digit count falls outside 10–14 is rejected outright, which is what keeps profile ids and concatenated junk out of the column.

Lead scoring fields

leadScore, leadGrade, seniorityLevel, company and titleMatch are computed by this Actor, not values LinkedIn or Google publish. They are derived entirely from the same row's title, description, phone_number and dial_code, plus your titleKeywords list. Change the fetched text and the score changes.

company is the only key in the row that can be null. The extractor tries three patterns in order: an at <Capitalised phrase> phrase in the title or snippet; the last non-LinkedIn segment of a dash-separated LinkedIn title when the title has at least three segments; and finally any capitalised phrase ending in a company token such as Ltd, Limited, Inc, LLC, GmbH, PLC, Group, Agency, Solutions, Technologies, Consulting, Ventures, Studio, Media, Marketing, Labs, Partners, Corp, Company or Enterprises. When none of the three matches, the field is null rather than a guess. Because pattern one takes a run of capitalised words, a snippet that continues into another capitalised word after the company name can over-capture — always treat company as a hint to verify, not a verified employer record.

🤖 Add-on: Need additional LinkedIn data?

A scored number and a URL is a lead, not a profile. LinkedIn Profile & Company Enrichment Scraper takes the profile behind a url and returns the profile and employer detail you need to qualify a high-grade lead before anyone calls it. LinkedIn Company URL - Mass Profile Finder With Firmographics works the company side when company comes back populated and you want firmographics behind it. And LinkedIn Profile Phone Number Scraper By Seniority & Title Filter is the sibling to reach for when you would rather filter results by seniority up front than score and sort them afterwards.

How the lead score is computed

leadScore is the sum of four signals, plus one optional boost, clamped to the range 0–100. Every input is read from the row itself.

SignalPointsExact condition
Valid phone30phone_number starts with +, has 10–14 digits, and its digits start with the country's dial digits
Seniority tier0–35Highest matching tier in the combined title and description text — see the tier table
Company present20company is non-empty after extraction
Description richness0–15Character length of description: ≥200 → 15, ≥120 → 12, ≥60 → 8, ≥20 → 4, otherwise 0
Title-keyword boost+10Any entry in your titleKeywords appears in the title or description; also sets titleMatch to true

Written out: leadScore = min(100, phonePoints + seniorityPoints + companyPoints + descriptionPoints + (10 if titleMatch else 0)).

Seniority tiers are checked in this order and the first match wins. Matching is whole-word and case-insensitive over the title and description joined together.

seniorityLevelPointsMatched terms
Founder/Owner35founder, co-founder, cofounder, co founder, owner, co-owner, proprietor
C-Suite35ceo, cfo, coo, cto, cmo, cio, ciso, chief, president
VP30vp, vice president, svp, evp
Director26director, head of, head
Manager18manager, principal, lead
Senior IC10senior, specialist, consultant, expert
Unknown0nothing above matched

leadGrade is a straight band on the final score: A ≥ 85, B ≥ 70, C ≥ 55, D ≥ 40, otherwise F.

Three things to know before you sort on it.

The score is absolute, not relative to the run. No run-level average, median or percentile enters the formula — the same title, snippet, number and titleKeywords list always produce the same score. That makes leadScore comparable across runs, keywords and countries, so datasets from separate runs merge and sort together. The one thing that shifts a score between runs is your own titleKeywords list, since the +10 boost depends on it; keep the list identical if you want strictly comparable numbers.

The four base signals can already reach 100. A valid phone, a founder or C-suite match, a named company and a snippet of 200 characters or more sum to exactly 100, so the +10 boost only moves rows that are not already maxed out. titleMatch is still worth reading as a flag in its own right.

Matching is text matching, and text lies. The tiers key on words in Google's snippet, so head inside "head of department" and lead inside "operations lead" or "lead generation" both register — the second as the Manager tier. chief and president behave the same way. titleKeywords matching is looser still: it is a plain lowercase substring test, so lead matches leader and leads. A seniorityLevel is a signal from indexed text, not a verified job title, and no accuracy figure is claimed for it.

Limits you should know before your first run

Four behaviours are worth knowing up front rather than discovering mid-run.

maxPhoneNumbers is per keyword, not per run. Ten keywords at maxPhoneNumbers: 200 is a 2,000-row target. Each keyword pages through Google ten results at a time until its own limit is met or it hits three consecutive pages that yield nothing.

minLeadScore drops rows before they are pushed. A row scoring below the threshold is never written to the dataset and never charged; the run logs the dropped count. Dropped rows also do not count toward maxPhoneNumbers, so raising the threshold tightens quality without shrinking the row target — but it does mean more Google pages fetched for the same number of rows.

Eleven country options collapse to +1. The dial-code parser keeps only the country-code portion before the hyphen, so Jamaica (+1-876), Bahamas (+1-242), Barbados (+1-246), Dominica (+1-767), Dominican Republic (+1-809), Grenada (+1-473), Antigua And Barbuda (+1-268), Saint Kitts And Nevis (+1-869), Saint Lucia (+1-758), Saint Vincent And The Grenadines (+1-784) and Trinidad And Tobago (+1-868) all query and filter on +1 — the whole North American Numbering Plan, not that island. Vatican City (+39-06) behaves the same way and filters on +39. The country field on the row still records the country you picked, so filter the results yourself if you need one specific area code. Countries that genuinely share a code behave the same by nature: Kazakhstan and Russia both filter on +7.

Search localisation is a hint, not a guarantee. The country you pick is mapped to an ISO code and passed to Google as gl and to the proxy as its exit country, which biases the index Google searches. It does not restrict results to that country — the dial-code filter on the number does that. One mapping is off: Congo (Democratic Republic) (+243) resolves to the CG exit country rather than CD, so its SERP is localised to the neighbouring Republic of the Congo. The dial code, the filter and the country value on the row are unaffected.

Why not build this yourself?

LinkedIn's own APIs are partner-gated and expose no contact lookup, so there is no official endpoint to compare against — the alternative to this Actor is writing the Google-SERP plumbing yourself, and that plumbing is where the time goes.

The proxy is the whole ballgame. Apify's GOOGLE_SERP group intercepts the plain HTTP request, runs the search itself and hands back results HTML. The residential group instead serves a JavaScript-gated shell with no results in it, which parses as "zero phone numbers found" rather than as an error — a silent, expensive failure mode.

Block detection is a trap, not a substring match. The obvious implementation greps the body for sorry or captcha. A normal Google results page legitimately contains both words in footer links and reCAPTCHA references, so that heuristic flags every healthy page as blocked and returns nothing on every run. This Actor keys instead on four phrases that appear only on the interstitial, on the HTTP status, and on whether Google's own result-wrapper markup is present at all.

Then there is the long tail. Rotating user-agent and accept-language pools, jittered delays, a retry budget, /url?q= and /safety/go redirect unwrapping, a phone regex whose output has to survive zero-width padding, 00 prefixes, trunk zeros and duplicated country codes, and a scoring layer that has to degrade gracefully when Google returns a title but no snippet. All of it is maintained here.

How to use LinkedIn Phone Number Scraper: Lead Scoring

The Actor runs on Apify. Start it from the Apify Console or call it through the Apify API — nothing else to sign up for.

  1. Open LinkedIn Phone Number Scraper: Lead Scoring on Apify and click Try for free
  2. Add one or more entries to Keywords / Usernames / URLs (keywords) — a job title, industry or business type works best, because the term has to appear in Google's indexed text
  3. Pick a Country (country) from the dropdown — this sets the quoted dial code in the query, the filter applied to every number found, and the search locale
  4. Optionally set Minimum Lead Score (minLeadScore) to drop weak rows, and Title Keywords (boost) (titleKeywords) to promote the roles you actually want
  5. Click Start, then sort the dataset by leadScore and export as JSON, CSV or Excel, or read it through the Apify API

keywords and country are the two required inputs, and they fail differently. An empty or missing keywords list logs No keywords provided. Please fill the 'keywords' array with at least one value. and the run finishes immediately — no rows, nothing charged. A missing country does not stop the run: the code falls back to United Kingdom (+44), so an API caller who omits the field quietly gets UK numbers under a UK search locale. The Console blocks that case before the run starts; the fallback only bites callers building the input JSON themselves.

How to scale to bulk lead extraction

keywords is a list, so bulk is the normal mode — add as many terms as you like and each is searched in turn within one run. Keywords are processed sequentially, not in parallel, and rows are pushed as they are found rather than at the end.

An entry can also be a LinkedIn profile or company URL. When it matches linkedin.com/in/…, /pub/… or /company/…, the Actor pulls the slug, turns it into a name — jane-doe-9057562b becomes jane doe, with the trailing vanity id stripped — and tries up to three progressively broader queries, stopping at the first one that yields a row.

One run covers one country, because country is a single string. To cover several regions, run the Actor once per country with the same keywords list. Because country, dial_code and an absolute leadScore are stamped on every row, the datasets merge into one sortable multi-region table.

What can you do with LinkedIn phone lead data?

  • 📞 A sales development rep building a call list runs five job-title keywords with minLeadScore at 70, then works the dataset sorted by leadScore descending, opening url to verify each lead before the number is ever dialled.
  • 🎯 A demand-gen manager targeting decision-makers sets titleKeywords to ["founder", "vp sales"] and filters on titleMatch and seniorityLevel to separate genuine buying-authority rows from Manager and Senior IC noise.
  • 🧹 A RevOps engineer enriching a CRM matches incoming phone_number values against existing records, routes by dial_code, and writes leadScore and leadGrade onto the record as a prioritisation field with url kept as the provenance link.
  • 🔍 A market researcher measuring how a profession publishes contact details compares row counts and the leadGrade distribution for the same keywords across different country values, reading description to see whether numbers are switchboards or direct lines.
  • 🤖 An AI engineer building a lead-qualification agent indexes description, title and company into a vector store with leadScore, seniorityLevel and keyword as metadata filters, so the agent can answer "which indexed LinkedIn pages in this niche publish a UK number for a founder" against live rows rather than a stale export.

Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

How does LinkedIn Phone Number Scraper: Lead Scoring handle rate limits and blocking?

Every request goes out through Apify's GOOGLE_SERP proxy group, which is what performs the search — you never create a proxy account or rotate an IP yourself. The proxy is asked for your chosen country as its exit location.

Each results page gets up to three attempts. Before every attempt the Actor sleeps a random 1–2 seconds and rebuilds its headers, drawing a user-agent from a pool of four current desktop browser strings and an accept-language from three. After a failed or blocked attempt it waits a further random 3–6 seconds before retrying. Requests use a 60-second total timeout, with 30 seconds each for connect and socket read.

A response counts as blocked on any status other than 200, on four literal interstitial phrases such as our systems have detected unusual traffic, or on a body that contains Google's JavaScript-gate marker while showing none of Google's result markup.

There is no CAPTCHA solving in this Actor, and none is claimed. When all three attempts fail, that page counts as one empty page for the keyword; after three consecutive empty or failed pages the Actor stops that keyword, keeps the rows already collected, and moves on. A run never dies because one keyword got blocked. If the GOOGLE_SERP proxy cannot be initialised at all, or you turn useApifyProxy off, the Actor logs a warning and continues without a proxy — requests in that state are likely to be blocked by Google.

⬇️ Input

Two inputs are required: keywords and country. Everything else has a default.

ParameterRequiredTypeDescriptionExample Value
keywordsYesarrayA list of keywords, Linkedin usernames, or profile URLs to search for. Each entry is searched separately. Prefilled with ["marketing"].["founder", "marketing director"]
countryYesstringSelect a country to scrape related phone numbers. The dial code is used to normalize and filter phone numbers. 194 values in the dropdown. Default "United Kingdom (+44)"."United Kingdom (+44)"
platformNostringSelect platform. For now this actor targets Linkedin via google.com SERP. Enum has one value: Linkedin. Default "Linkedin"."Linkedin"
maxPhoneNumbersNointegerMaximum number of phone numbers to collect per keyword. The scraper will stop once this limit is reached. Minimum 1, maximum 10000. Default 20.50
useApifyProxyNobooleanRoute Google search requests through Apify Proxy (recommended). Turn this off only if you intend to run without a proxy. Default true.true
minLeadScoreNointegerOnly keep leads whose computed leadScore (0-100) is at least this value. Rows below the threshold are dropped and never pushed; the dropped count is logged. Set 0 to keep everything. Minimum 0, maximum 100. Default 0.70
titleKeywordsNoarrayOptional role/keyword list. A result whose title or description contains any of these gets a +10 leadScore boost and titleMatch=true. Leave empty to disable boosting. Default [].["founder", "marketing director"]

Four honest notes on how these behave:

  • platform is effectively fixed. The schema exposes one value, and two things in the code pin the Actor to LinkedIn regardless: the search domain is built as linkedin.com from that value, and any result whose URL does not contain linkedin.com is dropped before a row is built. The value does change one visible thing — the platform field in the output, which comes back as Linkedin.com rather than Linkedin.
  • The proxy input is a boolean here, not an object. This Actor reads useApifyProxy as a plain true/false. If you are copying input JSON from a sibling contact Actor that takes a proxyConfiguration object, that key is simply ignored and the proxy stays on. The group is always GOOGLE_SERP; only the exit country varies, and it follows your country choice.
  • minLeadScore is clamped in code. Values outside 0–100 are pulled back into range and a non-numeric value falls back to 0, so an out-of-range API call cannot silently filter everything out.
  • maxPhoneNumbers is bounded by the schema, not by the code. The Console enforces 1–10000. The Actor reads the value as-is, so an API caller passing 0 or a negative number gets a run that completes with zero rows instead of an error.

Example input

{
"keywords": ["founder", "marketing director", "operations lead"],
"country": "United Kingdom (+44)",
"platform": "Linkedin",
"maxPhoneNumbers": 50,
"useApifyProxy": true,
"minLeadScore": 70,
"titleKeywords": ["founder", "marketing director"]
}

⬆️ Output

Typed, normalized JSON with a consistent shape across runs — the same thirteen keys on every row, never omitted. Only company can be null. Rows are pushed live as each results page is parsed, so the dataset fills while the run is still going. Export as JSON, CSV or Excel, or read the dataset through the Apify API. The default dataset view shows all thirteen fields with leadScore first, so the table is ready to sort on arrival.

Every row in the dataset is a result row, and every result row is charged as one row_result event. This Actor writes no header rows, no diagnostic rows, no accounting rows and no error rows — there is no marker field such as errorReason, status or isError to check, and no filter to apply. A keyword that finds nothing contributes no rows at all; a run that finds nothing anywhere finishes successfully with an empty dataset and a log line saying so. Rows dropped by minLeadScore are never pushed, so they are never charged. Failures live in the log, not in the data.

Duplicates are removed across the whole run. The seen-set is keyed on the (url, phone_number) pair and is created once per run, then shared across every keyword and every results page, so the same number from the same URL is pushed — and charged — exactly once even when several keywords surface the same page. What can legitimately repeat is one URL carrying two different numbers, or one number appearing on two different LinkedIn URLs; both are genuinely distinct rows. Deduplicate on phone_number alone if you want one row per contact:

seen, unique = set(), []
for row in items:
if row["phone_number"] not in seen:
seen.add(row["phone_number"])
unique.append(row)

Example output

{
"platform": "Linkedin.com",
"keyword": "founder",
"title": "Jane Doe - Founder & CEO at BrightLabs Ltd | LinkedIn",
"description": "Founder & CEO at BrightLabs Ltd, a Manchester-based B2B growth agency. WhatsApp or call the studio on +44 7585 467400 for new business enquiries and partnerships.",
"url": "https://www.linkedin.com/in/jane-doe/",
"phone_number": "+447585467400",
"country": "United Kingdom",
"dial_code": "+44",
"leadScore": 100,
"leadGrade": "A",
"seniorityLevel": "Founder/Owner",
"company": "BrightLabs Ltd",
"titleMatch": true
}

That row scores 30 for a valid +44 number, 35 for the Founder/Owner tier, 20 for a named company and 12 for a 162-character snippet — 97 — then takes the +10 boost for matching titleKeywords and is capped at 100.

A second row, from a different keyword in the same run, showing a Director-tier lead that lands one band lower:

{
"platform": "Linkedin.com",
"keyword": "marketing director",
"title": "Priya Raman - Marketing Director - Northwind Media Ltd | LinkedIn",
"description": "Media planning and buying for independent UK retail brands. Direct line 020 7946 0958 or email via the agency website.",
"url": "https://www.linkedin.com/in/priya-raman/",
"phone_number": "+442079460958",
"country": "United Kingdom",
"dial_code": "+44",
"leadScore": 94,
"leadGrade": "A",
"seniorityLevel": "Director",
"company": "Northwind Media Ltd",
"titleMatch": true
}

Note the number: the snippet wrote it in national trunk form as 020 7946 0958, and normalisation swapped the leading 0 for +44. Without the titleKeywords boost this row would score 84 and grade B.

And a lower-graded row, to show what the tiers do with an ambiguous title — Lead in "Operations Lead" matches the Manager tier:

{
"platform": "Linkedin.com",
"keyword": "operations lead",
"title": "Alex Whitmore - Operations Lead - Harborline Logistics | LinkedIn",
"description": "Freight forwarding and 3PL across the north west. Office: 0161 496 0123.",
"url": "https://www.linkedin.com/in/alex-whitmore/",
"phone_number": "+441614960123",
"country": "United Kingdom",
"dial_code": "+44",
"leadScore": 76,
"leadGrade": "B",
"seniorityLevel": "Manager",
"company": "Harborline Logistics",
"titleMatch": false
}

How does it work?

For each keyword the Actor builds one Google query — site:linkedin.com "+44" "marketing director" — and requests it through Apify's GOOGLE_SERP proxy over plain HTTP, ten results at a time, with English interface language (hl=en) and a gl country hint derived from your country choice. The proxy runs the search and returns the results HTML.

That HTML is parsed for result blocks using Google's result-wrapper class names, with a second known wrapper as fallback. Each block's link is unwrapped from Google's /url?q= redirect, non-LinkedIn links are discarded, and the block's visible text is scanned for a phone-shaped string that survives normalisation and starts with your country's dial code. Only then is a row built — and only at that point is the lead score computed, from the same title, snippet and number that go into the row.

Only publicly visible data is collected: this is Google's public index, with no LinkedIn account, cookie or session involved anywhere. And because the thirteen output keys and the scoring formula are defined by the Actor rather than by Google's markup, a Google layout change can affect coverage but never your field names, types or score scale.

Integrations

LinkedIn Phone Number Scraper: Lead Scoring is an Apify Actor, so it works with anything that can call the Apify API or consume a dataset.

Calling LinkedIn Phone Number Scraper: Lead Scoring programmatically

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/linkedin-phone-lead-scorer").call(run_input={
"keywords": ["founder", "marketing director"],
"country": "United Kingdom (+44)",
"maxPhoneNumbers": 50,
"minLeadScore": 70,
"titleKeywords": ["founder", "marketing director"],
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row["leadGrade"], row["leadScore"], row["phone_number"], row["company"], row["url"])

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request. Every row has the same thirteen keys, so no branching or presence checks are needed beyond a null guard on company.

No-code tools (n8n, Make, LangChain)

In n8n, use the Apify node — or an HTTP Request node pointed at the Apify run endpoint with your token — and pass the same JSON input shown above; a Filter node on leadScore gives you a second threshold without re-running the Actor. In Make, the Apify module supports run-and-wait, so a weekly keyword sweep can feed a Google Sheets, Airtable or CRM step directly, sorted by grade. In LangChain, wrap the run endpoint as a tool and hand the rows straight to the model, since they are already flat JSON with numeric scores. Apify schedules and webhooks cover recurrence and completion triggers.

Collecting publicly indexed contact details is generally treated as permissible where no login is bypassed, and this Actor reads only Google's public index. But the output is personal contact data — the most sensitive category of scraped data — and that changes your obligations rather than removing them.

A phone number tied to an identifiable person is personal data under every major regime, and a LinkedIn URL, name and job description alongside it makes the record more identifying, not less. The moment you store a row, you are a data controller. Under GDPR and UK GDPR you need a lawful basis before storing or reusing it; if you rely on legitimate interest, that requires a documented balancing test, not an assumption. Because the data was not collected from the individual, the Article 14 duty to inform them applies, along with access and erasure rights they can exercise against you.

Using a number is regulated separately, and more strictly, than collecting it. Direct marketing in the EU and UK falls under ePrivacy and PECR; calling and texting in the United States is governed by its own federal and state rules, including do-not-call registries. "The number was public" is not a lawful basis for calling or texting it.

Risk is also not uniform: a company switchboard published on a business page is a materially different proposition from an individual's mobile posted in a personal profile, and this Actor does not tell the two apart. Screen every list against suppression and do-not-call registers before any outreach, and take advice from qualified counsel for your jurisdiction. This Actor gives you data and a score; it does not give you permission to contact anyone.

❓ Frequently asked questions

What LinkedIn fields does LinkedIn Phone Number Scraper: Lead Scoring return?

Thirteen, on every row: leadScore, leadGrade, seniorityLevel, company, titleMatch, platform, keyword, title, description, url, phone_number, country and dial_code. The five you will use most are phone_number, leadScore, url, seniorityLevel and company. See the data fields table above for what each one contains.

Does LinkedIn Phone Number Scraper: Lead Scoring require a LinkedIn account or login?

No — and it cannot use one. The Actor never sends a request to LinkedIn; it queries Google's index of linkedin.com pages through Apify's GOOGLE_SERP proxy. There is no cookie, no session and no LinkedIn account to get flagged. The only credential involved is your Apify token.

Is leadScore a LinkedIn metric?

No. It is computed by this Actor from the row's own title, description, phone_number and dial_code, plus any titleKeywords you supply. Neither LinkedIn nor Google publishes it. The full formula and every weight are in the scoring section above, and no accuracy figure is claimed for it — it is a ranking aid, not a verdict on a person.

Can I compare leadScore across two different runs?

Yes, provided both runs used the same titleKeywords list. Nothing in the formula depends on the other rows in the run — no average, no median, no percentile — so a given result scores identically whenever it is found. That is what makes datasets from separate keyword or country runs safe to merge and sort together. Only the +10 boost varies with your input, so changing titleKeywords between runs shifts some scores by exactly 10.

How many phone numbers can I extract in one run?

maxPhoneNumbers accepts 1 to 10,000 and applies per keyword, so twenty keywords at 100 each is a 2,000-row target in one run. Whether you reach it depends on Google's index rather than on the Actor: a keyword stops early once three consecutive results pages return nothing usable. Rows dropped by minLeadScore do not consume the quota.

What happens if a keyword returns no results?

The run continues and finishes successfully. There is no error row and no diagnostic row — a keyword that finds nothing simply contributes no rows, the Actor logs • No more phone numbers found for this keyword. and moves to the next keyword. If nothing is found for any keyword, the dataset is empty and the log ends with a line explaining that public phone numbers for those keywords were not present in Google's indexed snippets. In code, treat an empty result set as "no indexed match", not as a failure: if not items: ....

Four causes account for almost all empty results. Google may have no indexed linkedin.com page containing both your keyword and the dial-code digits in visible text. The keyword may be too narrow or too brand-specific to appear in indexed text at all — a single profile URL is the hardest case, which is why the Actor falls back to broader queries for those. minLeadScore may be set high enough to drop everything that was found; the log reports the dropped count, so check that line before blaming coverage. Or Google may have blocked the pages, which the log records per attempt.

Can I scrape multiple keywords or countries at once?

Multiple keywords, yes — keywords is a list and each entry is searched in turn within one run, with the originating term stamped on every row as keyword. Countries, no: country is a single string, so one run covers one dial code. Run the Actor once per country with the same keyword list; because country, dial_code and an absolute leadScore are on every row, the datasets merge into one sortable multi-region table.

Does minLeadScore change what I am charged?

Yes, downward. Charging is per pushed row as a row_result event, and rows below the threshold are never pushed. The trade-off is that a high threshold on a broad keyword means more Google pages fetched for the same number of billed rows, since dropped rows do not count toward maxPhoneNumbers. Set it to 0 to keep everything and filter afterwards.

Does LinkedIn Phone Number Scraper: Lead Scoring work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP-triggered run through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. Because the row shape is fixed at thirteen keys with numeric leadScore, an agent tool schema for it is trivial to write and the results are directly rankable.

How does it compare to other LinkedIn phone number scrapers?

Checked on the Apify Store on 25 July 2026. scraper-mind/linkedin-phone-number-scraper documents three inputs on its listing — keyword as a single string with default "software engineer", platform, and country with default "United States (+1)" — and four output fields: title, url, description and phone_number. Its listing describes deduplication and E.164 formatting; no per-keyword result cap, no list input, no scoring or filtering fields are documented, and its example output shows numbers partially masked. williams_asante/scrape-haven-get-leads-emails-and-phone-numbers ranks for the same terms but its README documents a Google Maps business scraper, taking search_for and total and returning business name, address, phone, email, website, social links and review average — a different source and a different entity, useful when you want businesses rather than named professionals. Its listing documents pricing at $50 per month. A third listing, weighty_marshmallow/phone-number-enrichment-from-linkedin-profile-url-80-coverage, takes a profile URL according to its listing name; its README could not be retrieved when checked on that date, so nothing further about it is documented here.

Against all three, the observable differences here are the list-valued keywords input, the per-keyword maxPhoneNumbers cap, run-wide deduplication on (url, phone_number), and the five computed scoring keys with a published formula.

Does it return data in a format LLMs can use directly?

Yes. Every row is typed, normalized JSON with the same thirteen field names across runs. No HTML parsing, no selectors, no optional keys to guard against beyond a null on company. Pass a row straight into an LLM context window, index it into a vector store, or hand it to an agent tool.

What happens when Google changes its layout or anti-bot system?

The scraper is maintained, and the output schema is defined by the Actor rather than by Google's markup, so your thirteen field names, types and the 0–100 score scale stay put regardless. Layout changes affect coverage, not shape — the parser tries Google's result-wrapper class name and falls back to a second known wrapper, and block detection leans on HTTP status and interstitial-only phrases rather than on brittle body-text matching against words that appear on healthy pages.

Can I use it without managing proxies or browser infrastructure?

Yes. The Actor selects Apify's GOOGLE_SERP proxy group itself, sets the exit country from your country choice, and rotates user-agents and accept-language headers per attempt. You never create a proxy account, pick a group or rotate an IP, and no browser is involved — the requests are plain HTTP. It does not solve CAPTCHAs, and it makes no claim to: a page that stays blocked after three attempts is counted as an empty page and the run carries on.

Which fields work best for AI training data and RAG indexing?

For RAG indexing, description carries by far the most information per record — it is the natural-language snippet the number appeared in — with title as a shorter second text field and company as a useful entity anchor. For metadata filters, leadScore, leadGrade, seniorityLevel, titleMatch and keyword are the ready-made facets, and all of them are typed primitives: an integer, and four strings or a boolean. For training data or feature extraction, phone_number, dial_code, country and platform are the most structurally consistent, since all four are normalised rather than passed through. Keep the personal-data obligations in the legal section in mind before pushing contact numbers into a training set or a shared vector store.

Scraper NameWhat it extracts
LinkedIn Profile Phone Number Scraper By Seniority & Title FilterThe same Google-index method, filtering results by seniority and job function instead of scoring them
LinkedIn Profile & Company Enrichment ScraperProfile and employer detail for a lead you found here, to qualify it before outreach
LinkedIn Company URL - Mass Profile Finder With FirmographicsCompany pages and firmographics behind a company value
LinkedIn Jobs Scraper With Salary Range FiltersJob listings with salary ranges, for hiring-signal targeting
Instagram Phone Number ScraperThe same phone-number method applied to instagram.com
Tiktok Video Scraper (Creator Profile & Contact)Creator contact details attached to TikTok videos

💬 Your feedback

Found a bug, or a keyword and country pair that returns nothing when you expect results? Open an issue on the Actor's Issues tab. Reports that include the exact input JSON — the keyword list, the country string and your minLeadScore — are the fastest to reproduce and fix, and score disputes are easiest to settle when you paste the row's title and description alongside the grade.