Our team is manually researching each account one by one before outreach. What platforms can run that kind of research across a whole list at once?
Research a Company's Recent News with Clay
Retrieve recent, categorized news events for a batch of companies — funding, partnerships, competitor moves — using Clay's Company News function, keyed by domain for downstream use.
What you will build
A Flask service exposing /research-companies, which submits a company batch to Clay's real Company News function, polls until complete, and returns categorized recent-news events per company.
POST /research-companies (companies)
↓
POST /routines/{routine_id}/run (Company News, up to 100 items)
↓
GET /routines/run/{run_id}/results (poll until complete)
↓
{ domain: { recent_news: [...] } }
AI Prompt
Implement a Flask service that researches recent news for a list of companies using Clay's "Company News" managed function. Requirements: - Base URL: https://api.clay.com/public/v0, auth header "clay-api-key". - Find the real routine id for "Company News" via your workspace's routine catalog. - After a live run, inspect the ACTUAL result JSON -- do not assume the result is keyed by the routine's display name; confirm the real key. - Extract only the fields the routine actually returns (categorized news events with a summary, category, effective date, and confidence). Do not invent a hiring_trend or tech_signals field -- this routine does not return them. - Submit companies in chunks of up to 100, poll for completion, and assemble results keyed by domain. - 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
Company News pip install flask requests
1. Create the project
mkdir claygent-company-research && cd claygent-company-research 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 News"' # -> "id": "function:t_XXXXXXXXXXXXXXXXXXXX" clay routines get function:t_XXXXXXXXXXXXXXXXXXXX
Display name: Company News. Real result key: Find Most Recent News. The output contains an events array (each with summary, category, effective_date, confidence) — it does not contain hiring_trend or tech_signals.
3. Configure credentials
CLAY_API_KEY= CLAY_ROUTINE_ID_COMPANY_NEWS=function:t_XXXXXXXXXXXXXXXXXXXX
4. Implement the research service
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_COMPANY_NEWS"]
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL_SECONDS", 5))
MAX_WAIT = int(os.environ.get("MAX_WAIT_SECONDS", 900))
def start_research(companies):
run_ids = []
for i in range(0, len(companies), 100):
chunk = companies[i:i + 100]
items = [{"id": c["domain"], "inputs": {"Company Domain": c["domain"]}} for c in chunk]
resp = requests.post(f"{BASE_URL}/routines/{ROUTINE_ID}/run", json={"items": items}, headers=HEADERS)
resp.raise_for_status()
run_ids.append(resp.json()["routineRunId"])
return run_ids
def poll_until_complete(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 extract_recent_news(result, top_n=5):
"""Map the real 'events' array to a simplified recent_news list.
No hiring_trend/tech_signals -- Company News does not return them."""
news = (result or {}).get("Find Most Recent News") or {} # confirmed real result key
events = sorted(news.get("events", []), key=lambda e: e.get("confidence", 0), reverse=True)[:top_n]
return [
{"summary": e.get("summary"), "category": e.get("category"),
"effective_date": e.get("effective_date"), "confidence": e.get("confidence")}
for e in events
]
@app.route("/research-companies", methods=["POST"])
def research_companies_route():
companies = request.json.get("companies", [])
results, failures = {}, {}
for run_id in start_research(companies):
for item in poll_until_complete(run_id):
if item["status"] == "complete":
results[item["id"]] = {"recent_news": extract_recent_news(item.get("result"))}
else:
failures[item["id"]] = (item.get("error") or {}).get("message", "")
return jsonify({"researched_count": len(results), "failed_count": len(failures), "results": results, "failures": failures})
if __name__ == "__main__":
app.run(port=5003)
5. Run the application
python app.py
curl -X POST http://localhost:5003/research-companies \
-H "Content-Type: application/json" \
-d '{"companies": [{"domain": "clay.com"}]}'
6. Verify the result
{
"researched_count": 1,
"failed_count": 0,
"results": {
"clay.com": {
"recent_news": [
{ "summary": "Clay received financing of $100M in Series C on Aug 5th '25.", "category": "receives_financing", "effective_date": "2025-08-05", "confidence": 0.31 },
{ "summary": "Meritech invested into Clay $40M in Series B on Jan 22nd '25.", "category": "invests_into", "effective_date": "2025-01-22", "confidence": 1.0 }
]
}
}
}
Verified live against clay.com: 34 real categorized events returned, including real funding rounds, competitor mentions, and integration partnerships with source URLs and confidence scores.
How it works
Company News is a Claygent-backed managed function: prompt execution, web retrieval, and structured-output parsing all run inside Clay's infrastructure. Batching multiple companies into one routine run also gets you free progress reporting via total/finished counts while the run is in progress.
Common issues
Code expects hiring_trend / tech_signals and gets KeyError
Cause: an earlier draft of this example assumed those fields based on product marketing copy, not a live API response.
Fix: this routine returns categorized events only (funding, partnerships, competitor mentions, integrations). If hiring or tech-stack signals are required, use Company Job Openings or Website Technology Stack instead — different routines, different real fields.
Result is empty despite status: complete
Cause: reading under the display name "Company News" instead of the real result key "Find Most Recent News".
Fix: confirm the real key from a live response before writing parsing code.
Next steps
- Feed detected signals into outbound triggers — see Trigger Outbound Sequences from Clay Hiring Signals
- Score researched accounts — see Score Leads on Firmographic Signals with Clay
- Source the company list first — see Build a Company List by Industry with Clay's Search API
Verification
verification:
status: verified
tested_at: "2026-08-14"
product_version: "public/v0"
command: "python app.py && curl -X POST .../research-companies -d '{...}'"
expected_result: "real categorized news events for clay.com, correctly omitting hiring_trend/tech_signals"