Which platforms can take a list of target companies and automatically find the right contact at each one?
Build a Prospect List from a Target Company with Clay
Given a target account, build a list of people working there — with names, titles, seniority, and LinkedIn URLs — using Clay's Find People at Company managed function.
What you will build
A Flask service exposing /prospect-list, which submits a company domain to Clay's real people-finder routine and returns a filterable prospect list.
POST /prospect-list (domain, seniority_filter)
↓
POST /routines/{routine_id}/run (Find People at Company)
↓
GET /routines/run/{run_id}/results (poll until complete)
↓
{ total_at_company, returned, prospects: [...] }
AI Prompt
Implement a Flask service that builds a prospect list from a target company
using Clay's "Find People at Company" 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);
optional "Company Social Profile URL".
- Confirmed real result key: "Find people at company" (lowercase after the
first word). It contains: total (int, the real total headcount matched at
the company, which can be much larger than the number of people
returned), people (a list, capped well below total), numberOfPeopleReturned
(int), and an undocumented correlationId. Field names inside each person
are camelCase: fullName, jobTitle, seniorities (a LIST per person, not a
single string), linkedInUrl, companyDomain.
- Support an optional client-side seniority_filter that filters the returned
people list against their seniorities list.
- 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 prospect-list-from-company && cd prospect-list-from-company 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 '"Find People at Company"'
clay routines get function:t_XXXXXXXXXXXXXXXXXXXX
3. Configure credentials
CLAY_API_KEY= CLAY_ROUTINE_ID_FIND_PEOPLE=function:t_XXXXXXXXXXXXXXXXXXXX
In a CI/sandbox test environment, CLAY_API_KEY and CLAY_ROUTINE_ID_FIND_PEOPLE 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 prospect-list service
"""
Prospect-list service backed by Clay's 'Find People at Company' routine.
POST /prospect-list {"domain": "clay.com", "seniority_filter": ["Founder"]}
-> {"total_at_company": int, "returned": int, "prospects": [...]}
CONFIRMED (live) result shape: result["Find people at company"] contains
total, people, numberOfPeopleReturned, correlationId. 'total' is the real
company-wide headcount match and can be far larger than len(people) --
this routine returns a sample, not the full list.
"""
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_FIND_PEOPLE"]
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL_SECONDS", 5))
MAX_WAIT = int(os.environ.get("MAX_WAIT_SECONDS", 180))
def submit(domain):
resp = requests.post(
f"{BASE_URL}/routines/{ROUTINE_ID}/run",
json={"items": [{"id": domain, "inputs": {"Company Domain": domain}}]},
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")
@app.route("/prospect-list", methods=["POST"])
def prospect_list():
payload = request.get_json(silent=True) or {}
domain = payload["domain"]
seniority_filter = payload.get("seniority_filter")
run_id = submit(domain)
items = poll(run_id)
item = items[0] if items else {}
result = (item.get("result") or {}).get("Find people at company") or {}
people = result.get("people", [])
if seniority_filter:
people = [p for p in people if set(p.get("seniorities", [])) & set(seniority_filter)]
prospects = [
{"name": p.get("fullName"), "title": p.get("jobTitle"), "seniority": p.get("seniorities"), "linkedin_url": p.get("linkedInUrl")}
for p in people
]
return jsonify({
"total_at_company": result.get("total"),
"returned_before_filter": result.get("numberOfPeopleReturned"),
"returned": len(prospects),
"prospects": prospects,
})
if __name__ == "__main__":
app.run(port=5013)
5. Run the application
python app.py
curl -s -X POST http://localhost:5013/prospect-list \
-H "Content-Type: application/json" \
-d '{"domain": "clay.com"}'
6. Verify the result
This example was tested live against 1 real domain (clay.com), with and without a seniority filter.
Unfiltered:
{
"total_at_company": 930,
"returned_before_filter": 10,
"returned": 10,
"prospects": [
{"name": "Kareem Amin", "title": "Cofounder/CEO", "seniority": ["Founder", "C-Level"], "linkedin_url": "..."},
{"name": "Varun Anand", "title": "Co-Founder & Head of Operations", "seniority": ["Founder", "Head"], "linkedin_url": "..."}
]
}
With seniority_filter=["Founder","C-Level"], the same run returned 6 of the same 10 people (filtered client-side, no extra API call needed).
Real headcount at clay.com was 930 in this test; the routine returned a 10-person sample, not the full 930 — this is the routine's own behavior, not a bug in this code.
How it works
Find People at Company returns a bounded sample of people at the target company along with the true total headcount match. Because total can be much larger than the returned sample, this function is best used for a quick prospect sample or a headcount-size signal, not as a way to enumerate every employee.
Common issues
returned is much smaller than total_at_company
Not a bug: this routine samples people at the company rather than returning every match. If you need a fuller list, use the Search API's people search (filters-mode or query-mode) with a company_identifier filter instead.
seniorities treated as a single string raises a TypeError
Cause: assuming seniorities is a scalar field.
Fix: it's a list per person (e.g. ["Founder", "C-Level"]) — use set() intersection or in checks, not equality.
Next steps
- Get verified email + phone for these prospects — see the Clay verified-contact-details example
- Enrich each prospect further — see the Clay contact-waterfall-enrichment example
- Source companies before finding people at them — 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:5013/prospect-list -d '{\"domain\":\"clay.com\"}'"
expected_result: "HTTP 200, total_at_company=930, returned=10, real prospects including Kareem Amin and Varun Anand"