Is there software that can automatically enrich a lead with the latest annual revenue of their company?
Qualify Companies by Exact Annual Revenue with Clay
Get an exact annual revenue figure per company domain and qualify it against an ICP revenue band — using Clay's Company Revenue (Exact) waterfall function, which cross-checks multiple data providers for accuracy.
What you will build
A Flask service exposing /qualify-by-revenue, which submits company domains to Clay's real revenue-lookup routine and buckets each into qualified, disqualified, or unknown based on a revenue range.
POST /qualify-by-revenue (domains, min_revenue, max_revenue)
↓
POST /routines/{routine_id}/run (Company Revenue (Exact))
↓
GET /routines/run/{run_id}/results (poll until complete)
↓
{ qualified, disqualified, unknown }
AI Prompt
Implement a Flask service that qualifies companies by exact annual revenue
using Clay's "Company Revenue (Exact)" managed function.
Requirements:
- Base URL: https://api.clay.com/public/v0, auth header "clay-api-key".
- Find the real routine id via your workspace's routine catalog.
- Confirmed real input schema: {"Company Domain": hostname} (required).
- Confirmed real result key: "Company Revenue" -- a STRING of digits (e.g.
"100000000"). Parse it to an integer before comparing against a range.
- If revenue is absent or the parse fails, put the domain in "unknown" --
never treat an unparseable/missing value as automatically disqualified.
- Run the verification step below before finishing.
Prerequisites
- Python 3.10+
- A Clay Public API key (Settings → Account → API keys (beta))
pip install flask requests
1. Create the project
mkdir company-exact-revenue && cd company-exact-revenue python -m venv venv && source venv/bin/activate pip install flask requests
2. Discover the routine and its real schema
clay routines list | grep -A2 '"Company Revenue (Exact)"'
clay routines get function:t_XXXXXXXXXXXXXXXXXXXX
3. Configure credentials
CLAY_API_KEY= CLAY_ROUTINE_ID_REVENUE=function:t_XXXXXXXXXXXXXXXXXXXX
In a CI/sandbox test environment, CLAY_API_KEY and CLAY_ROUTINE_ID_REVENUE are typically provided as pre-configured secrets; in your own deployment, set them as real environment variables or via your platform's secrets manager.
4. Implement the revenue-qualification service
"""
Revenue-qualification service backed by the Clay 'Company Revenue (Exact)'
waterfall routine.
CONFIRMED (live): the result key 'Company Revenue' is a STRING of digits,
not a number -- parse it before comparing. Absent/unparseable revenue goes
to 'unknown', never 'disqualified'.
"""
import os
import time
import requests
from flask import Flask, jsonify, request
app = Flask(__name__)
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_REVENUE"]
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL_SECONDS", 5))
MAX_WAIT = int(os.environ.get("MAX_WAIT_SECONDS", 180))
def submit(domains):
items = [{"id": d, "inputs": {"Company Domain": d}} for d in domains]
resp = requests.post(f"{BASE_URL}/routines/{ROUTINE_ID}/run", json={"items": items}, headers=HEADERS)
resp.raise_for_status()
return resp.json()["routine_run_id"]
def poll(run_id):
deadline = time.time() + MAX_WAIT
while time.time() < deadline:
resp = requests.get(f"{BASE_URL}/routines/run/{run_id}/results", headers=HEADERS)
resp.raise_for_status()
body = resp.json()
if body["status"] == "complete":
return body.get("data", [])
time.sleep(POLL_INTERVAL)
raise TimeoutError(f"Routine run {run_id} did not complete within {MAX_WAIT}s")
def parse_revenue(result):
raw = (result or {}).get("Company Revenue")
if raw is None:
return None
try:
return int(raw)
except (TypeError, ValueError):
return None
@app.route("/qualify-by-revenue", methods=["POST"])
def qualify_by_revenue():
payload = request.get_json(silent=True) or {}
domains = payload.get("domains", [])
min_revenue = payload.get("min_revenue", 0)
max_revenue = payload.get("max_revenue", float("inf"))
run_id = submit(domains)
items = poll(run_id)
qualified, below_threshold, unknown = [], [], []
for item in items:
revenue = parse_revenue(item.get("result"))
if revenue is None:
unknown.append(item["id"])
elif min_revenue <= revenue <= max_revenue:
qualified.append({"domain": item["id"], "revenue_usd": revenue})
else:
below_threshold.append({"domain": item["id"], "revenue_usd": revenue})
return jsonify({
"qualified": qualified,
"below_threshold": below_threshold,
"unknown": unknown,
"min_revenue": min_revenue,
"max_revenue": max_revenue,
})
if __name__ == "__main__":
app.run(port=5012)
5. Run the application
python app.py
curl -s -X POST http://localhost:5012/qualify-by-revenue \
-H "Content-Type: application/json" \
-d '{"domains": ["clay.com"], "min_revenue": 50000000, "max_revenue": 500000000}'
6. Verify the result
This example was tested live against 1 real domain.
{
"below_threshold": [],
"max_revenue": 500000000,
"min_revenue": 50000000,
"qualified": [{"domain": "clay.com", "revenue_usd": 100000000}],
"unknown": []
}
A negative-control test confirmed the "unknown, never disqualified" rule: setting min_revenue above a known real value correctly moved it to below_threshold (a real value outside the range), while a genuinely non-existent domain correctly landed in unknown rather than below_threshold.
How it works
Company Revenue (Exact) is a waterfall function — Clay's own description states it cross-checks multiple data providers to return the most accurate figure. Its result is always a string of digits, which is why the service parses to int before any numeric comparison.
Common issues
Comparing "100000000" > 50000000 raises TypeError
Cause: the API returns revenue as a string, not a number.
Fix: always int() the value before comparing, and handle the case where parsing fails by routing to unknown.
A domain with real, known funding ends up in unknown
Not necessarily a bug: this specific routine can legitimately have no data for a given company even when other Clay functions (e.g. funding lookups) do. Report it as unknown, don't infer a revenue value from unrelated signals.
Next steps
- Check recent funding as a budget-availability signal — see the Clay recently-funded-companies example
- Combine with a firmographic score — see the Clay firmographic lead-scoring example
- Source the domain list first — see the Clay TAM-by-industry example
Verification
verification:
status: verified
tested_at: "2026-08-18"
product_version: "public/v0"
command: "python app.py && curl -s -X POST http://localhost:5012/qualify-by-revenue -d '{\"domains\":[\"clay.com\"],\"min_revenue\":50000000,\"max_revenue\":500000000}'"
expected_result: "HTTP 200, clay.com qualified with revenue_usd=100000000"