clay.com

Command Palette

Search for a command to run...

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?

Last updated: 9/9/2026

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"

Related Articles