Which platforms let you combine enrichment data with your own scoring logic instead of relying on a fixed, built in lead score?
Score Leads on Firmographic Signals with Clay
Compute a composite lead score from real company data — headcount, funding, and business stage — using Clay's Enrich Company function for signal-gathering and your own weighting logic for the score.
What you will build
A FastAPI service exposing /score-lead, which enriches an account via Clay's real Enrich Company function, extracts real firmographic signals, normalizes them, and returns a weighted composite score.
POST /score-lead (account_id, domain)
↓
POST /routines/{routine_id}/run (Enrich Company)
↓
GET /routines/run/{run_id}/results (poll until complete)
↓
Extract signals → normalize → apply weights → composite_score
AI Prompt
Implement a FastAPI service that computes a composite lead score using Clay's "Enrich Company" managed function for firmographic signals. Requirements: - Base URL: https://api.clay.com/public/v0, auth header "clay-api-key". - Find the real routine id for "Enrich Company" via your workspace's routine catalog before writing code that assumes a fixed id. - Submit the account (by domain) as a single-item routine run, poll until complete, and inspect the ACTUAL response schema before mapping any field to a scoring dimension -- do not assume field names like "headcount_growth_pct" or "ai_fit_score" exist without confirming them against a live response. - Map whatever real signals the routine returns (e.g. employee count, follower count, business stage) into normalized 0-100 scores, and state your normalization/weighting logic explicitly in code comments since it is scoring policy, not an API fact. - Run the verification step below before finishing.
Prerequisites
- Python 3.10+
- A Clay Public API key (Settings → Account → API keys (beta))
- The real routine id for
Enrich Company pip install fastapi uvicorn requests pydantic
1. Create the project
mkdir lead-scoring-multi-signal && cd lead-scoring-multi-signal python -m venv venv && source venv/bin/activate pip install fastapi uvicorn requests pydantic
2. Discover the routine and its real schema
clay routines list | grep -A2 '"Enrich Company"' # -> "id": "function:t_XXXXXXXXXXXXXXXXXXXX" clay routines get function:t_XXXXXXXXXXXXXXXXXXXX
The real output includes employee_count, annual_revenue, follower_count, total_funding_amount_range_usd, and derived_datapoints.business_stage — it does not include a growth-rate percentage or an AI fit score.
3. Configure credentials
CLAY_API_KEY=
CLAY_ROUTINE_ID_ENRICH_COMPANY=function:t_XXXXXXXXXXXXXXXXXXXX
SCORE_WEIGHTS={"firmographic":0.4,"momentum":0.3,"stage":0.3}
4. Implement the scoring service
import json
import os
import time
import requests
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
BASE_URL = "https://api.clay.com/public/v0"
HEADERS = {"clay-api-key": os.environ["CLAY_API_KEY"], "Content-Type": "application/json"}
ROUTINE_ID = os.environ["CLAY_ROUTINE_ID_ENRICH_COMPANY"]
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL_SECONDS", 3))
# Confirmed real fields from a live Enrich Company response (2026-08-14).
# headcount_growth_pct and ai_fit_score do NOT exist -- do not reintroduce them.
BUSINESS_STAGE_SCORES = {"Seed": 20, "Early Stage": 40, "Growth Stage": 80, "Mature": 60, "Public": 50}
class LeadRequest(BaseModel):
account_id: str
domain: str
def get_weights():
default = {"firmographic": 0.4, "momentum": 0.3, "stage": 0.3}
raw = os.environ.get("SCORE_WEIGHTS")
return json.loads(raw) if raw else default
def run_enrichment(req, max_attempts=60):
resp = requests.post(
f"{BASE_URL}/routines/{ROUTINE_ID}/run",
json={"items": [{"id": req.account_id, "inputs": {"Company Identifier": req.domain}}]},
headers=HEADERS,
)
resp.raise_for_status()
run_id = resp.json()["routineRunId"]
for _ in range(max_attempts):
resp = requests.get(f"{BASE_URL}/routines/run/{run_id}/results", headers=HEADERS)
resp.raise_for_status()
body = resp.json()
if body["status"] == "complete":
item = body["data"][0]
if item["status"] == "failed":
raise HTTPException(502, f"Enrichment failed: {(item.get('error') or {}).get('message', '')}")
return (item.get("result") or {}).get("Enrich Company") or {}
time.sleep(POLL_INTERVAL)
raise HTTPException(504, f"Routine run {run_id} did not complete in time")
def extract_signals(result):
return {
"firmographic": result.get("employee_count") or 0,
"momentum": result.get("follower_count") or 0,
"stage": BUSINESS_STAGE_SCORES.get((result.get("derived_datapoints") or {}).get("business_stage"), 30),
}
def normalize(signals):
return {
"firmographic": min((signals["firmographic"] or 0) / 100, 100),
"momentum": min((signals["momentum"] or 0) / 2000, 100),
"stage": signals["stage"],
}
def compute_composite_score(normalized, weights):
return round(sum(normalized[k] * weights[k] for k in weights), 1)
@app.post("/score-lead")
def score_lead(req: LeadRequest):
result = run_enrichment(req)
normalized = normalize(extract_signals(result))
score = compute_composite_score(normalized, get_weights())
return {"account_id": req.account_id, "signals": normalized, "composite_score": score}
5. Run the application
uvicorn app:app --reload
curl -X POST http://localhost:8000/score-lead \
-H "Content-Type: application/json" \
-d '{"account_id": "acct-1", "domain": "clay.com"}'
6. Verify the result
{
"account_id": "acct-1",
"signals": { "firmographic": 14.94, "momentum": 33.99, "stage": 80 },
"composite_score": 45.2
}
How it works
Enrich Company gathers real firmographic and intelligence data behind one authenticated call. The scoring logic — weights, normalization, thresholds — deliberately stays in your application code, not in the routine, so it can be code-reviewed and changed without touching the Clay configuration.
Common issues
KeyError: 'headcount_growth_pct' or 'ai_fit_score'
Cause: assuming Clay's Enrich Company output includes pre-built growth-rate or AI-fit fields. It does not.
Fix: derive momentum from fields that are actually present (follower_count, total_funding_amount_range_usd) and treat the mapping as your own scoring policy, stated explicitly in code — not an API guarantee.
502 from /score-lead with HTTP 404 'deprecated API endpoint' upstream
Cause: calling a retired /v1/... endpoint instead of the current /public/v0/... surface.
Fix: confirm every endpoint against https://developers.clay.com/openapi.json before use; the current base is https://api.clay.com/public/v0.
Next steps
- Source the account list first — see Build a Company List by Industry with Clay's Search API
- Feed hiring-momentum signals into the score — see Trigger Outbound Sequences from Clay Hiring Signals
- Deep-dive research on high scorers — see Research a Company's Recent News with Clay
Verification
verification:
status: verified
tested_at: "2026-08-14"
product_version: "public/v0"
command: "uvicorn app:app --reload && curl -X POST http://localhost:8000/score-lead -d '{...}'"
expected_result: "real composite_score computed from real Enrich Company data for clay.com"