Ryan Smith
production2026-05

Chicago's Probate Notices, Same Day They Print

Every afternoon it pulls new Cook County notices to creditors, drops repeats, and files them as a clean PDF and a ledger.

871Notices captured
7 to 36New per run
2 to 14 secondsRun time

The Problem

When someone dies in Cook County and an estate is opened, the court requires a notice to creditors to run in the local legal newspaper. Each notice names the deceased, the person handling the estate, and their attorney. The paper publishes them behind a search page, repeats each one for three weeks, and offers no way to download just the new ones. Reading the page every day and keeping track of what you have already seen is tedious enough that almost nobody does it.

What It Does

At 15:06 Central every day it asks the paper's notice feed for every current notice to creditors. It reads each notice, pulls out the case number, the decedent, the representative, the attorney, and the claims deadline, and collapses the repeats down to one entry per notice. It checks each bulletin number against a Google Sheet of everything it has captured before and keeps only the ones that are new. Those go into a PDF with a cover page, an index, and one notice per page, which is saved to two Drive folders. It then appends one row per new notice to the ledger and emails a summary with a preview table.

Real estate investors and estate attorneys who want new Cook County probate cases the day they are announced, and the downstream parser that turns the PDF into contact-ready leads.

Outcome

Between 2026-06-26 and 2026-09-15 it logged 871 notices across 32 days with new notices, between 7 and 36 per run after the first day. Every bulletin number in the ledger is unique, so nothing was captured twice. A run finishes in 2 to 14 seconds. Nobody is paying for the feed yet; the ledger and the PDFs are the stock it would be sold from.

Screenshots

The summary email for the 2026-09-15 run: 26 new notices, the PDF filename, and the preview table with decedent, case number, attorney, and claims bar date.
The summary email for the 2026-09-15 run: 26 new notices, the PDF filename, and the preview table with decedent, case number, attorney, and claims bar date.

The summary email for the 2026-09-15 run: 26 new notices, the PDF filename, and the preview table with decedent, case number, attorney, and claims bar date.

The ledger filtered to 2026-09-15: the same 26 rows, one per notice, with the bulletin ID that keeps each from being captured twice.
The ledger filtered to 2026-09-15: the same 26 rows, one per notice, with the bulletin ID that keeps each from being captured twice.

The ledger filtered to 2026-09-15: the same 26 rows, one per notice, with the bulletin ID that keeps each from being captured twice.

Fourteen consecutive daily runs at 15:06, every one succeeded, each finishing in 2 to 14 seconds.
Fourteen consecutive daily runs at 15:06, every one succeeded, each finishing in 2 to 14 seconds.

Fourteen consecutive daily runs at 15:06, every one succeeded, each finishing in 2 to 14 seconds.

How It's Built

Trigger:Schedule trigger, daily at 15:06 America/Chicago, plus a manual trigger for reruns.
Components:19

A Configuration node holds the feed endpoint, the search keyword, the publication type code, the Drive folder IDs, the sheet ID, and the notify address, so nothing is hardcoded further down. An HTTP node calls the paper's notice feed with a browser user agent and gets JSON back. A Code node unwraps the CDATA HTML in each notice, keeps only the notice-to-creditors type, extracts the bulletin number, case number, decedent, representative, role, attorney, claims deadline, and run dates, normalizes the case number, and dedupes within the run by bulletin number, keeping the earliest publication date. A Google Sheets read pulls the ledger's sourceId column and a second Code node drops anything already seen. A third Code node builds an HTML document with a cover, an index, and one sanitized notice per page, and a self-hosted Gotenberg service converts it to PDF. The PDF is renamed with the run date and count, uploaded to an archive folder and a parser folder in parallel, and the two upload results are merged. One row per new notice is appended to the ledger, then a Gmail node sends a summary with a 12-row preview and links to the PDF and both folders. An error workflow is attached for failures.

By the numbers

notices in the ledger
871 (2026-06-26 to 2026-09-15)
days with new notices logged
32, one PDF per run, 35 PDFs total (three on the first day)
new notices per run after day one
7 to 36
run time
2.0 to 14.2 seconds, 2026-09-02 to 2026-09-15, every run succeeded
unique bulletin IDs in the ledger
871 of 871 rows
unique case numbers
868 of 871 rows
attorney extracted
860 of 871; the other 11 notices did not print one
personal representative extracted
870 of 871
first-publication dates covered
2026-06-01 to 2026-09-15
sample run, 2026-09-15
26 notices, 28-page PDF
rows this source contributed to the downstream Airtable
1,119 of 1,400 Cook County rows; the rest come from a second paper captured by a sibling workflow
paying subscribers
0 at present

Key Decisions

Dedupe within a run by the paper's own notice number rather than by decedent or case number.

The feed returns the same notice once per publication date in a rolling three-week window, so a single notice appears up to three times in one pull. The bulletin number is the one field that is identical across those copies.

The Google Sheet is the dedupe store. The sourceId column is read at the top of every run and only unseen IDs continue.

No database to host, and the ledger doubles as the memory. The code comment says it plainly: the sheet's sourceId column is the dedupe store.

Case numbers are normalized to YYYY-P-000000 and a separate caseKey is stored with the dashes stripped.

The paper prints the same format inconsistently (the ledger shows both '2026 P 2785' and '2026P001479'), so a normalized key is needed to match cases downstream.

The PDF keeps the court's centered header and justified body and puts one notice per page after a cover and an index.

It mirrors a sample PDF the downstream parser was built against, so the parser reads it without changes.

Ledger columns match the Palm Beach probate ledger exactly, including dateOfDeath and postDate columns this source never fills.

One schema across counties so the downstream steps do not change per source.

Every run uploads the PDF twice, to an archive folder and to a parser folder.

The parser folder is watched by the parser workflow, which consumes what lands there. The archive folder is the permanent copy.

The paper's title field is taken as the decedent name.

It is the name on all but a handful of notices. On 4 of 871 the paper put the case number in the title instead, and those rows carry a case number where the name should be. Ryan had not noticed until this review and calls it a known rough edge to fix.

Where else this applies

Any legal notice, permit, or filing that a publisher posts on a rolling window fits this shape: pull the whole window every day, collapse repeats by the publisher's own ID, check against a ledger of what you have seen, and package only the new items for whatever reads them next. The same pattern already runs against Palm Beach County and against a second Cook County paper, and the ledger columns were kept identical so a new source is a configuration change rather than a rebuild.