Lead scoring sounds like it requires a data science team and months of model training. It does not. A straightforward Python script with rule-based scoring handles 80% of what most RevOps teams need, and you can have it running against your CRM by end of day.
The Scoring Framework¶
Every lead scoring model needs two signal categories:
| Signal Type | Examples | Why It Matters |
|---|---|---|
| Firmographic | Company size, industry, revenue, location | Indicates fit with your ICP |
| Behavioral | Page visits, email opens, demo requests, content downloads | Indicates buying intent |
The script assigns points for each signal and sums them into a total score between 0 and 100.
Defining Your Scoring Rules¶
Start with a configuration dictionary that makes rules easy to adjust without changing code:
SCORING_RULES = {
"firmographic": {
"employee_count": [
(500, float("inf"), 20), # 500+ employees: 20 pts
(100, 499, 15), # 100-499: 15 pts
(50, 99, 10), # 50-99: 10 pts
(0, 49, 5), # Under 50: 5 pts
],
"industry_match": {
"Technology": 15,
"Financial Services": 15,
"Healthcare": 10,
"Manufacturing": 10,
"_default": 5,
},
},
"behavioral": {
"demo_requested": 25,
"pricing_page_visit": 15,
"case_study_download": 10,
"email_opened_last_30d": 5,
"webinar_attended": 10,
},
}
The Scoring Engine¶
The scoring function evaluates each lead against the rules:
def score_lead(lead):
score = 0
breakdown = {}
# Firmographic: employee count
emp_count = lead.get("employee_count", 0)
for low, high, points in SCORING_RULES["firmographic"]["employee_count"]:
if low <= emp_count <= high:
score += points
breakdown["employee_count"] = points
break
# Firmographic: industry match
industry = lead.get("industry", "")
industry_scores = SCORING_RULES["firmographic"]["industry_match"]
industry_pts = industry_scores.get(industry, industry_scores["_default"])
score += industry_pts
breakdown["industry"] = industry_pts
# Behavioral signals
for signal, points in SCORING_RULES["behavioral"].items():
if lead.get(signal, False):
score += points
breakdown[signal] = points
return min(score, 100), breakdown
Pulling Lead Data From Salesforce¶
Query your CRM for the fields that map to your scoring signals:
import os
from simple_salesforce import Salesforce
sf = Salesforce(
username=os.environ["SF_USERNAME"],
password=os.environ["SF_PASSWORD"],
security_token=os.environ["SF_TOKEN"],
)
def get_leads():
query = """
SELECT Id, Company, Industry, NumberOfEmployees,
HasRequestedDemo__c, Pricing_Page_Visit__c,
Case_Study_Download__c, Email_Opened_30d__c,
Webinar_Attended__c
FROM Lead
WHERE IsConverted = false AND Status != 'Disqualified'
"""
results = sf.query_all(query)
leads = []
for r in results["records"]:
leads.append({
"id": r["Id"],
"company": r["Company"],
"industry": r.get("Industry", ""),
"employee_count": r.get("NumberOfEmployees", 0) or 0,
"demo_requested": r.get("HasRequestedDemo__c", False),
"pricing_page_visit": r.get("Pricing_Page_Visit__c", False),
"case_study_download": r.get("Case_Study_Download__c", False),
"email_opened_last_30d": r.get("Email_Opened_30d__c", False),
"webinar_attended": r.get("Webinar_Attended__c", False),
})
return leads
Pushing Scores Back to the CRM¶
After scoring, write the results back so sales reps see them directly in Salesforce:
def push_scores(sf, scored_leads):
updates = []
for lead in scored_leads:
updates.append({
"Id": lead["id"],
"Lead_Score__c": lead["score"],
"Lead_Grade__c": grade_label(lead["score"]),
})
sf.bulk.Lead.update(updates, batch_size=200)
def grade_label(score):
if score >= 75:
return "A"
elif score >= 50:
return "B"
elif score >= 25:
return "C"
return "D"
Running the Full Scoring Pipeline¶
def main():
leads = get_leads()
for lead in leads:
lead["score"], lead["breakdown"] = score_lead(lead)
print(f"{lead['company']}: {lead['score']} ({lead['breakdown']})")
push_scores(sf, leads)
print(f"Scored and updated {len(leads)} leads")
if __name__ == "__main__":
main()
Key Takeaways¶
- Rule-based lead scoring in Python delivers immediate value without the complexity of machine learning
- Separating scoring rules into a configuration dictionary makes it easy for non-engineers to adjust weights
- Pushing scores back to the CRM ensures sales reps see lead quality in their normal workflow, not in a separate tool
- Start simple with five to ten signals and add complexity only when the data supports it