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