B2b Lead Qualifier
Pricing
Pay per usage
B2b Lead Qualifier
Score and rank B2B leads by analyzing company websites for 30+ business quality signals. Get a score (0-100), letter grade, and transparent signal breakdown for every domain. No AI keys needed — pure rule-based scoring. Integrates with Website Contact Scraper and Email Pattern Finder.
Pricing
Pay per usage
Rating
0.0
(0)
Developer

ryan clinton
Actor stats
0
Bookmarked
4
Total users
0
Monthly active users
6 days ago
Last modified
Categories
Share
Score and rank B2B leads by analyzing company websites for business quality signals. Give it a list of domains and get back a score (0-100), letter grade (A-F), and a breakdown of exactly which signals contributed to the score.
No AI API keys needed. Pure rule-based scoring from publicly visible HTML — fast, transparent, and affordable.
How to score B2B leads
- Go to the B2B Lead Qualifier on Apify
- Enter company domains (e.g.,
stripe.com,zapier.com) - Click Start — get back scored, ranked leads with signal breakdowns
- Sort by score to focus on the highest-quality leads first
What does it analyze?
The Actor crawls each company website and checks for 30+ business quality signals across 5 categories:
| Category | Max Points | What It Measures |
|---|---|---|
| Contact Reachability | 30 | Can you actually reach someone? Email, phone, LinkedIn, decision-makers |
| Business Legitimacy | 25 | Is this a real, active business? Privacy policy, SSL, content depth |
| Online Presence | 20 | Marketing sophistication: blog, analytics, chat widget, testimonials |
| Website Quality | 15 | Technical professionalism: meta tags, structured data, mobile-ready |
| Team Transparency | 10 | Can you see who works there? Team page, named contacts, job titles |
Grading scale
| Score | Grade | Meaning |
|---|---|---|
| 90-100 | A | Excellent — highly reachable, established business |
| 75-89 | B | Good — solid web presence, likely reachable |
| 60-74 | C | Average — some signals present, may need more research |
| 40-59 | D | Below average — limited online presence |
| 0-39 | F | Poor — minimal signals, likely hard to reach |
Output
Each domain produces one scored result. Here's a real result from analyzing apify.com:
{"domain": "apify.com","url": "https://apify.com","score": 62,"grade": "C","scoreBreakdown": {"contactReachability": 10,"businessLegitimacy": 22,"onlinePresence": 15,"websiteQuality": 12,"teamTransparency": 3},"signals": [{ "signal": "hasLinkedInCompany", "category": "contactReachability", "points": 4, "detail": "http://linkedin.com/company/apify/" },{ "signal": "hasContactPage", "category": "contactReachability", "points": 3, "detail": "Contact page link found" },{ "signal": "hasGenericEmail", "category": "contactReachability", "points": 3, "detail": "Found: hello@apify.com" },{ "signal": "hasCopyrightCurrent", "category": "businessLegitimacy", "points": 3, "detail": "Copyright 2026" },{ "signal": "hasPrivacyPolicy", "category": "businessLegitimacy", "points": 3, "detail": "Privacy policy link found" },{ "signal": "hasMarketingTools", "category": "onlinePresence", "points": 3, "detail": "hubspot" },{ "signal": "hasAnalytics", "category": "onlinePresence", "points": 2, "detail": "google-analytics, segment" }],"extractedData": {"emails": ["hello@apify.com"],"phones": [],"contactNames": [],"socialLinks": { "linkedin": "http://linkedin.com/company/apify/", "twitter": "https://x.com/apify" },"address": null,"cmsDetected": null,"techSignals": ["google-analytics", "segment", "hubspot"]},"pagesScraped": 5,"qualifiedAt": "2026-02-07T01:57:12.078Z"}
Signals array shortened for brevity — the full output includes all 25 signals that fired.
Output fields
| Field | Type | Description |
|---|---|---|
domain | String | Company domain analyzed |
score | Integer | Lead quality score (0-100) |
grade | String | Letter grade: A, B, C, D, F |
scoreBreakdown | Object | Points per category (contactReachability, businessLegitimacy, onlinePresence, websiteQuality, teamTransparency) |
signals | Array | Every signal that contributed to the score, with signal name, category, points, and human-readable detail |
extractedData | Object | All extracted contact data: emails, phones, contact names, social links, address, CMS, tech stack |
pagesScraped | Integer | Number of pages crawled for this domain |
qualifiedAt | String | ISO timestamp |
Use cases
Prioritize outreach from a lead list
You have 500 company domains. Instead of contacting them all, run them through the qualifier to rank them by quality. Focus your outreach on grade A and B leads first — they have the best web presence and are most likely to respond.
Filter low-quality leads before enrichment
Running email enrichment on 1,000 domains is expensive. Pre-qualify them first: filter out grade D and F leads (set minScore to 60) and only enrich the promising ones.
Complete the lead generation pipeline
Google Maps Scraper Website Contact Scraper Email Pattern Finder B2B Lead Qualifier┌──────────────────┐ ┌──────────────────────┐ ┌────────────────────┐ ┌───────────────────┐│ "plumbers in LA" │ ────> │ 500 website URLs │ ───> │ Detect email │ ──> │ Score & rank ││ │ sites │ │ data │ patterns │ │ all 500 leads ││ 500 businesses │ │ emails, names, titles │ │ Generate addresses │ │ Focus on grade A │└──────────────────┘ └──────────────────────┘ └────────────────────┘ └───────────────────┘
Want this entire pipeline in one click? Use B2B Lead Generation Suite — it chains all 3 actors automatically with a single input.
Input
| Field | Type | Description | Default |
|---|---|---|---|
domains | Array of strings | Company domains to score (required) | — |
pipelineData | Array of objects | Pre-scraped data from Website Contact Scraper / Email Pattern Finder (optional) | [] |
maxPagesPerDomain | Integer (1-15) | Max pages to crawl per website | 5 |
minScore | Integer (0-100) | Only output leads at or above this score | 0 |
proxyConfiguration | Object | Proxy settings for website scraping | Apify Proxy |
Example input
{"domains": ["stripe.com", "zapier.com", "basecamp.com"],"maxPagesPerDomain": 5,"minScore": 50}
Pipeline integration input
Feed output from Website Contact Scraper directly as pipelineData to skip re-scraping contact info:
{"domains": ["buffer.com"],"pipelineData": [{"domain": "buffer.com","emails": ["hello@buffer.com"],"contacts": [{ "name": "Joel Gascoigne", "title": "Founder CEO" },{ "name": "Jenny Terry", "title": "VP of Finance & Operations" }],"socialLinks": {"linkedin": "https://www.linkedin.com/company/bufferapp","twitter": "https://x.com/buffer"}}]}
How to use the API
Python
from apify_client import ApifyClientclient = ApifyClient(token="YOUR_API_TOKEN")run = client.actor("ryanclinton/b2b-lead-qualifier").call(run_input={"domains": ["stripe.com", "zapier.com", "basecamp.com"],"minScore": 50,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(f"{item['domain']}: {item['score']}/100 ({item['grade']})")for signal in item["signals"]:print(f" +{signal['points']} {signal['signal']}: {signal['detail']}")
JavaScript / Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });const run = await client.actor('ryanclinton/b2b-lead-qualifier').call({domains: ['stripe.com', 'zapier.com', 'basecamp.com'],minScore: 50,});const { items } = await client.dataset(run.defaultDatasetId).listItems();items.forEach(item => {console.log(`${item.domain}: ${item.score}/100 (${item.grade})`);item.signals.forEach(s => console.log(` +${s.points} ${s.signal}: ${s.detail}`));});
Python — full pipeline example
from apify_client import ApifyClientclient = ApifyClient(token="YOUR_API_TOKEN")# Step 1: Get businesses from Google Mapsmaps_run = client.actor("compass/crawler-google-places").call(run_input={"searchStringsArray": ["dentists in Austin TX"]})websites = []for biz in client.dataset(maps_run["defaultDatasetId"]).iterate_items():if biz.get("website"):websites.append(biz["website"])# Step 2: Scrape contact infoscraper_run = client.actor("ryanclinton/website-contact-scraper").call(run_input={"urls": websites, "maxPagesPerDomain": 5})pipeline_data = []domains = []for item in client.dataset(scraper_run["defaultDatasetId"]).iterate_items():domains.append(item["domain"])pipeline_data.append(item)# Step 3: Qualify and rank leadsqualifier_run = client.actor("ryanclinton/b2b-lead-qualifier").call(run_input={"domains": domains,"pipelineData": pipeline_data,"minScore": 50,})# Step 4: Get ranked resultsfor item in client.dataset(qualifier_run["defaultDatasetId"]).iterate_items():print(f"{item['domain']}: {item['score']}/100 ({item['grade']}) - {len(item['signals'])} signals")
Signals reference
Contact Reachability (30 points max)
| Signal | Points | What triggers it |
|---|---|---|
| Personal email found | 8 | Non-generic email discovered (not info@, sales@, etc.) |
| Generic email found | 3 | At least a role-based email like info@ exists |
| Phone number | 5 | Phone number found via tel: link or in contact sections |
| Physical address | 4 | Street address detected in footer, contact page, or <address> tag |
| Contact page | 3 | Link to a contact/get-in-touch page found in navigation |
| LinkedIn company | 4 | LinkedIn company page linked from the website |
| Decision-maker | 3 | C-level, founder, or president found in team contacts |
Business Legitimacy (25 points max)
| Signal | Points | What triggers it |
|---|---|---|
| Privacy policy | 3 | Link to a privacy policy page |
| Terms of service | 2 | Link to terms of service |
| Current copyright | 3 | Copyright year is recent (2024+) |
| About page | 3 | Link to an about/company page |
| Structured data | 3 | JSON-LD or microdata markup present |
| Multiple pages | 3 | Navigation has 3+ internal links |
| Content depth | 3 | Homepage has 200+ words |
| Favicon | 2 | Custom favicon set |
| SSL active | 3 | HTTPS connection successful |
Online Presence (20 points max)
| Signal | Points | What triggers it |
|---|---|---|
| Social media | 1-5 | 1 point per platform linked (LinkedIn, Twitter, Facebook, Instagram, YouTube) |
| Blog/news | 3 | Link to blog, news, or press section |
| Testimonials | 3 | Customer testimonials, case studies, or "trusted by" section |
| Analytics | 2 | Google Analytics, Segment, Mixpanel, Hotjar, or Plausible detected |
| Marketing tools | 3 | HubSpot, Marketo, Pardot, Mailchimp, or Klaviyo detected |
| Chat widget | 2 | Intercom, Drift, Crisp, Tawk, Zendesk, or Freshdesk detected |
| Pricing page | 2 | Link to a pricing or plans page |
Website Quality (15 points max)
| Signal | Points | What triggers it |
|---|---|---|
| Meta description | 2 | Meaningful meta description tag present |
| Open Graph | 2 | OpenGraph meta tags for social sharing |
| Mobile viewport | 2 | Responsive viewport meta tag configured |
| Proper title | 2 | Page title exists, is longer than 10 characters |
| H1 heading | 1 | H1 element present on the page |
| Schema.org Org | 3 | Organization or LocalBusiness structured data |
| Images | 1 | At least 3 images on the page |
| Navigation | 2 | Navigation element with 3+ links |
Team Transparency (10 points max)
| Signal | Points | What triggers it |
|---|---|---|
| Team page | 3 | Link to team, people, or leadership page |
| Named contacts | 1-4 | 1 point per person found, up to 4 |
| Job titles | 3 | At least one contact has a job title |
Performance and cost
This Actor uses CheerioCrawler (HTTP only, no browser) for minimal cost:
| Domains | Estimated time | Estimated platform cost |
|---|---|---|
| 10 | ~15 seconds | < $0.01 |
| 100 | ~3 minutes | ~$0.05 |
| 1,000 | ~20 minutes | ~$1.00 |
Estimates based on 5 pages per domain with datacenter proxies.
FAQ
How is the score calculated?
The score is a simple sum of points from detected signals. Each signal has a fixed point value, grouped into 5 categories with maximum caps. The scoring is fully transparent — every signal that contributed is listed in the output with its point value and what triggered it.
Does this use AI or LLMs?
No. The scoring is entirely rule-based — it checks for specific HTML elements, meta tags, links, and script patterns. No AI API keys needed, which keeps costs predictable and results reproducible.
Why did my domain get a low score?
Common reasons: the website uses JavaScript rendering (this Actor uses HTTP requests, no browser — JS-rendered content won't be detected), the site has minimal public contact info, or it's a simple single-page website. Check the signals array to see exactly which signals fired and which didn't.
Can I customize the scoring weights?
Not currently — the weights are designed based on B2B outreach best practices. If you need custom scoring, export the signals array and apply your own weights in post-processing.
Does this verify emails or phone numbers?
No — it detects whether contact information is publicly visible on the website. For email verification, use a dedicated verification service after qualifying leads.
What's the difference between standalone and pipeline mode?
Standalone: Just provide domains — the Actor crawls each website and extracts everything from scratch. Pipeline: Provide pre-scraped data from Website Contact Scraper via pipelineData — the Actor uses that contact data instead of re-scraping, but still crawls for website quality and tech signals.
Responsible use
This Actor analyzes publicly visible website content. By using it, you agree to:
- Comply with all applicable laws, including GDPR, CAN-SPAM, and CCPA
- Respect each website's Terms of Service
- Use the scores and extracted data only for legitimate business purposes
- Not use this tool to facilitate spam or unsolicited bulk outreach
Limitations
- JavaScript-rendered content — Uses HTTP requests only (CheerioCrawler). SPAs that render everything client-side will score lower because body content isn't visible.
- No email verification — Detects emails on the page but doesn't verify deliverability.
- English-centric — Name extraction and signal patterns work best with English-language websites.
- Snapshot scoring — Scores reflect the website at the time of crawling. Websites change over time.
Pricing
- $5 per 1,000 domains scored
- First 100 domains free — try it risk-free
- Only pay for successful results
| Domains | Price |
|---|---|
| 100 | Free |
| 1,000 | $5 |
| 5,000 | $25 |
| 10,000 | $50 |
Changelog
v1.0 (2026-02-07)
- Initial release
- 30+ business quality signals across 5 categories
- Rule-based scoring (0-100) with letter grades
- Pipeline integration with Website Contact Scraper and Email Pattern Finder
- Tech stack detection: CMS, analytics, marketing, chat, e-commerce
- Contact extraction: emails, phones, social links, physical addresses, team members