Product Reports Resources Pricing Login Book a demo

Cookbook 02 ยท Monitoring

Build a startup watchlist that detects real changes

Save a baseline, check the same companies again, and report new funding, open roles, removed roles, and linked news.

Python 15 minutes 11 API calls Intermediate
python startup_watchlist.py baseline saved
$ python startup_watchlist.py
Baseline created for 8 companies.
No alerts were emitted on the first run.
State saved to .dealroom-watchlist-state.json

$ python startup_watchlist.py
Checked 8 companies. No changes found.
8companies tracked
Liveopen roles indexed
3signal types compared

The goal

Turn a shortlist into a monitoring loop

A shortlist answers who fits today. A watchlist answers what changed since the last check. That requires the same API data plus one small piece of infrastructure: persisted state.

Track the eight industrial AI companies from Cookbook 01. Alert only when a funding round, news item, or job record appears or disappears after the baseline.

The example uses company UUIDs as the stable watchlist key. It fetches the current records, compares their IDs with a local JSON file, prints any differences, and replaces the state file atomically.

What counts as an alert?

A newly observed record is a signal, not proof that the event happened since the previous run. It may also reflect improved Dealroom coverage. Keep a human in the review loop.

Baseline

Make the first run intentionally quiet

Without a previous state, every historical round and every current role would look new. The first run therefore writes a baseline and emits zero alerts. The second run is the first meaningful comparison.

Run 1Fetch and save the baseline
Run 2Compare IDs and print changes
Run NReplace state after every check
state shape
{
  "saved_at": "2026-09-02T09:00:00+00:00",
  "companies": { "company_uuid": { "open_jobs_count": 3 } },
  "funding": { "round_id": { "company_name": "sensmore" } },
  "news": { "article_id": { "title": "..." } },
  "jobs": { "job_id": { "title": "..." } }
}

Keys make set comparison simple. They also retain enough normalized context to explain a removed job after that record is no longer present in the live response.

Current state

Fetch the records that can change

Use the companies list once for current hiring totals, the typed funding-round subresource once per company, and the global news and jobs lists with an entity_id[in_any] filter.

QuestionEndpoint
Which companies are hiring now?GET /data/companies
Which funding rounds are on record?GET /data/companies/{id}/funding-rounds
Which linked news items exist?GET /data/news
Which roles are open?GET /data/jobs
startup_watchlist.py
id_filter = "|".join(watchlist_ids)

companies = client.list_all(
    "/data/companies",
    {"filter": f"id[in_any]:{id_filter}"},
)

news = client.list_all(
    "/data/news",
    {"filter": f"entity_id[in_any]:{id_filter}",
     "sort": "-publish_date"},
)

jobs = client.list_all(
    "/data/jobs",
    {"filter": f"entity_id[in_any]:{id_filter}",
     "sort": "-date_posted"},
)

The helper follows cursors and falls back to offsets when a collection is larger than one page. Funding is fetched per company because its typed subresource is the direct company-to-round relationship.

Change detection

Compare identifiers before fields

Record IDs answer the most important questions cleanly: which rounds, articles, or roles appeared, and which roles disappeared. Field comparisons then add a summary such as the change in open-role count.

startup_watchlist.py
new_round_ids = current["funding"].keys() - previous["funding"].keys()
new_news_ids = current["news"].keys() - previous["news"].keys()
opened_job_ids = current["jobs"].keys() - previous["jobs"].keys()
removed_job_ids = previous["jobs"].keys() - current["jobs"].keys()

The script emits structured alert kinds: new_funding, new_news, job_opened, job_removed, and hiring_count_changed.

Run it

Keep credentials and state on the server

Create a Programmatic M2M key in Dealroom, install the dependencies, and run the monitor locally or from a scheduled server job.

.env
DEALROOM_CLIENT_ID=your_client_id
DEALROOM_CLIENT_SECRET=your_client_secret
DEALROOM_USER_AGENT=your-company-watchlist/1.0

# Optional: comma-separated company UUIDs
DEALROOM_WATCHLIST_IDS=company_uuid_1,company_uuid_2
terminal
pip install authlib requests python-dotenv
python startup_watchlist.py
python startup_watchlist.py --json
python startup_watchlist.py --state data/team-watchlist.json

The default script includes the Cookbook 01 shortlist, so it works immediately after credentials are set. Override DEALROOM_WATCHLIST_IDS to monitor your own companies.

Current snapshot

See what the monitor reads

8 companies, live hiring, with records from the same four endpoints.

JSON

Loading the current watchlist snapshot...

Snapshot generated from the Dealroom API. This is current input data, not a fabricated change feed. Your first local run establishes its own baseline.

Next steps

Connect alerts to the way your team works

The script prints human-readable output by default and structured JSON with --json. From there, a production monitor can:

  • Run daily in GitHub Actions, a server cron job, or your orchestration system.
  • Store state in object storage or a database instead of the local filesystem.
  • Post only selected alert kinds to Slack, email, or a CRM review queue.
  • Require a minimum round size or restrict roles to functions that matter to your thesis.
  • Log the first-seen timestamp so coverage additions stay distinguishable from event dates.

Complete example

Download the stateful monitor

The file includes OAuth2 authentication, pagination, all four data pulls, normalized state, atomic writes, and structured change output.