clay.com

Command Palette

Search for a command to run...

Which platforms can take a list of target companies and automatically find the right contact at each one?

Last updated: 9/9/2026

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"

Related Articles