Tutorial: Build a Lead Scoring Engine with Enrichment Data
Turn raw signups into scored leads by combining B2B Enrichment API data with your ideal customer profile (ICP). This tutorial shows you how to request the right fields and build a scoring function that ranks leads automatically.
What You'll Build
A scoring function that enriches a company and assigns a 0-100 score based on industry fit, company size, and technology overlap.
Define Your ICP
Before writing code, define what makes a lead valuable. Here's an example for a developer tools company:
| Signal | High Score | Medium Score | Low Score |
|---|---|---|---|
| Industry | SaaS, Developer Tools, Fintech | E-commerce, Marketing Tech | Education, Government |
| Size | 51-500 employees | 11-50 or 501-1000 | 1-10 or 10000+ |
| Tech Stack | Uses React, Next.js, or TypeScript | Uses Node.js or Python | No tech signals |
The Scoring Function
JavaScript
const ICP = {
highValueIndustries: ["SaaS", "Developer Tools", "Fintech", "Software"],
mediumValueIndustries: ["E-commerce", "Marketing", "Data Analytics"],
idealSizeRanges: ["51-200", "201-500"],
goodSizeRanges: ["11-50", "501-1000"],
targetTech: ["React", "Next.js", "TypeScript", "Node.js"],
};
async function scoreCompany(domain) {
const response = await fetch(
"https://b2b-enrichment.p.rapidapi.com/api/enrich",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.RAPIDAPI_KEY,
},
body: JSON.stringify({
company: domain,
fields: ["name", "industry", "size", "techStack"],
}),
}
);
if (!response.ok) return { domain, score: 0, reason: "enrichment_failed" };
const { data } = await response.json();
let score = 0;
const signals = [];
// Industry scoring (0-40 points)
const industry = (data.industry || "").toLowerCase();
if (ICP.highValueIndustries.some((i) => industry.includes(i.toLowerCase()))) {
score += 40;
signals.push("high-value industry");
} else if (
ICP.mediumValueIndustries.some((i) => industry.includes(i.toLowerCase()))
) {
score += 20;
signals.push("medium-value industry");
}
// Size scoring (0-30 points)
if (ICP.idealSizeRanges.includes(data.size)) {
score += 30;
signals.push("ideal company size");
} else if (ICP.goodSizeRanges.includes(data.size)) {
score += 15;
signals.push("acceptable company size");
}
// Tech stack scoring (0-30 points)
const techStack = (data.techStack || []).map((t) => t.toLowerCase());
const techMatches = ICP.targetTech.filter((t) =>
techStack.includes(t.toLowerCase())
);
const techScore = Math.min(techMatches.length * 10, 30);
score += techScore;
if (techMatches.length > 0) {
signals.push(`tech match: ${techMatches.join(", ")}`);
}
return {
domain,
company: data.name,
score,
tier: score >= 70 ? "hot" : score >= 40 ? "warm" : "cold",
signals,
};
}
Python
import os
import requests
ICP = {
"high_industries": ["saas", "developer tools", "fintech", "software"],
"medium_industries": ["e-commerce", "marketing", "data analytics"],
"ideal_sizes": ["51-200", "201-500"],
"good_sizes": ["11-50", "501-1000"],
"target_tech": ["react", "next.js", "typescript", "node.js"],
}
def score_company(domain: str) -> dict:
response = requests.post(
"https://b2b-enrichment.p.rapidapi.com/api/enrich",
headers={
"Content-Type": "application/json",
"X-API-Key": os.environ["RAPIDAPI_KEY"],
},
json={
"company": domain,
"fields": ["name", "industry", "size", "techStack"],
},
timeout=30,
)
if not response.ok:
return {"domain": domain, "score": 0, "reason": "enrichment_failed"}
data = response.json()["data"]
score = 0
signals = []
# Industry (0-40)
industry = (data.get("industry") or "").lower()
if any(i in industry for i in ICP["high_industries"]):
score += 40
signals.append("high-value industry")
elif any(i in industry for i in ICP["medium_industries"]):
score += 20
signals.append("medium-value industry")
# Size (0-30)
size = data.get("size")
if size in ICP["ideal_sizes"]:
score += 30
signals.append("ideal size")
elif size in ICP["good_sizes"]:
score += 15
signals.append("acceptable size")
# Tech stack (0-30)
tech = [t.lower() for t in (data.get("techStack") or [])]
matches = [t for t in ICP["target_tech"] if t in tech]
score += min(len(matches) * 10, 30)
if matches:
signals.append(f"tech: {', '.join(matches)}")
tier = "hot" if score >= 70 else "warm" if score >= 40 else "cold"
return {
"domain": domain,
"company": data.get("name"),
"score": score,
"tier": tier,
"signals": signals,
}
Score a Batch of Leads
const leads = ["vercel.com", "stripe.com", "notion.so", "shopify.com"];
const scored = [];
for (const domain of leads) {
scored.push(await scoreCompany(domain));
await new Promise((r) => setTimeout(r, 6000)); // rate limit
}
// Sort by score, highest first
scored.sort((a, b) => b.score - a.score);
console.table(scored.map(({ company, score, tier, signals }) => ({
company,
score,
tier,
signals: signals.join("; "),
})));
Using Scores in Your Pipeline
Once you have scores, route leads based on tier:
| Tier | Score | Action |
|---|---|---|
| Hot (70-100) | Strong ICP match | Route to sales immediately. Assign to AE. |
| Warm (40-69) | Partial match | Add to nurture sequence. Auto-assign SDR follow-up. |
| Cold (0-39) | Low fit | Add to long-term nurture. No outbound. |
Tips
- Request only scoring fields. You need
industry,size, andtechStack— skipdescription,socialLinks, andfoundedto keep requests fast. - Cache enrichment results. Company data doesn't change daily. Store results keyed by domain and re-enrich monthly.
- Combine with first-party signals. Enrichment scores get more powerful when combined with behavioral data — page visits, email opens, feature usage.
- Tune your ICP weights. Start with the 40/30/30 split above, then adjust based on which scored leads actually convert.