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.
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.
{
"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.
| Question | Endpoint |
|---|---|
| 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 |
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.
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.
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
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.
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.