DEV Community

Ben
Ben

Posted on

Build an n8n rental alert that skips unchanged listings

A daily rental search should not send yesterday's listings again. The useful output is a small set of newly observed listings and asking-rent changes, with enough context to see what changed.

I maintain Zumper Rental Scraper on Apify. This walkthrough uses its saved-search state and an n8n email template. The original source runs and sample are from September 9, 2026. On September 10, a fresh three-listing report reached the test inbox through Apify's native email Actor without SMTP credentials. The samples are dated rental data, not a current availability quote.

Watch the sample walkthrough

Watch the 21-second walkthrough. This silent clip walks through the September 9 sample and n8n setup. It predates the native email step and does not show a new source run or email delivery; use the written instructions below for the current setup.

Establish the baseline first

The downloadable workflow searches Austin apartments with at least one bedroom and a maximum asking-rent filter of $3,000:

{
  "mode": "search",
  "location": "austin-tx",
  "propertyType": "apartments-for-rent",
  "maxPrice": 3000,
  "minBeds": 1,
  "maxListings": 25,
  "includePhotos": false,
  "useApifyProxy": true,
  "onlyChanges": true,
  "monitorId": "austin-rentals-under-3000",
  "firstRunMode": "seedOnly"
}
Enter fullscreen mode Exit fullscreen mode

seedOnly stores the initial snapshot without exporting the backlog. Later runs with the same monitor ID return newly observed or changed listings. Use emitAll if you want the first snapshot in the dataset too.

Keep one monitor ID for one search and avoid overlapping runs. Changing cities or filters deserves a new ID. Leaving resetState enabled would repeatedly discard the baseline and defeat the monitor.

What the live check returned

For the bounded demonstration, I used maxListings: 3 on build 1.1.23. The initial emitAll snapshot contained:

Listing Minimum asking rent Source location
Seabrook Square $1,206/month 3515 Manor Rd, Austin
The Rail $1,300/month 2921 E 17th St, Austin
The Rhone $1,749/month 2450 Wickersham Ln, Austin

These can be building-level rent ranges, not individual units available at the minimum price. The Actor does not establish complete Austin market coverage or verify lease terms.

Run QDtz5Vvr2bRKoFbQs exported those three new rows. The next run, AdjAk7J4AZNMiTJrB, returned zero duplicates. A separate seed-only monitor returned zero rows on both its initial run and repeat. The earlier Austin case study provides additional background; the downloadable sample records this newer test.

There was also a failure worth keeping in the record: the first source attempt returned HTTP 403 through a residential route. That run failed before updating the baseline. The next attempt succeeded. A failed source request is not evidence that no rentals changed; check the run status before interpreting an empty dataset.

Wire the n8n workflow

Import zumper-rental-alerts.json from the Gist. Its five nodes run a daily schedule, call the Actor, build a text digest, send through Apify and check the mail run's status. The exported workflow is inactive and contains no credentials.

Create an HTTP Header Auth credential with header name Authorization and value Bearer YOUR_APIFY_TOKEN, then select it in both HTTP nodes. In Email rental alert, replace you@example.com in the JSON body expression with your Apify account email. No SMTP credential or custom sender address is needed. Check your n8n timezone before enabling the 08:00 schedule.

Apify Send Email allows Free accounts to send reports to their registered address; paying accounts can send to up to 20 different recipients. It is a reporting tool, not a promotional-mail service. The mail run uses 256 MB and a 60-second timeout, and its platform usage is separate from the rental Actor's charges.

The request uses 512 MB, a 240-second Actor timeout and a $0.75 maximum run charge. The formatter accepts both an array response and one n8n item per row. It returns no items when the dataset is empty, so the email node receives no digest on an unchanged run. Invalid rows raise an error instead of producing a misleading empty message.

I exercised the exact formatter code with the real three-row export and checked empty output, invalid rows and repeated inputs. That also caught literal \n sequences in the older template; the updated formatter produces actual line breaks. The September 10 delivery check used fresh output from build 1.1.24, passed that same formatter's text to Send Email 0.3.20, and verified one received Gmail message with an identical subject and body plus Apify's standard footer.

I also imported the workflow into n8n 2.38.6 and executed two manual checks. A saved copy of the fresh three-row export passed through every downstream node with Send Email in mock mode. A second execution called the live Zumper Actor with the same monitor ID, returned zero changes and stopped before the digest or email nodes. These were isolated test executions; no recurring schedule was activated. The inbox receipt was verified separately from those mock-mail checks.

The last node throws if the mail Actor fails or has not finished. Inspect the mail run before retrying a delivery: a request timeout can occur after a message was sent. Retry from the saved digest in that n8n execution; restarting the entire rental search can return zero rows because its baseline already advanced.

You can run the formatter checks without an Apify token:

git clone https://gist.github.com/e28968a940fe4195ffb5a997fe309503.git apify-workflows
cd apify-workflows
node test_zumper_rental_alerts.mjs
Enter fullscreen mode Exit fullscreen mode

Separate rent cuts from other changes

The current Actor uses change_type: new, price_changed or details_changed. It does not emit a price_drop event. To keep only reductions in the minimum asking rent, filter change rows with:

row.change_type === 'price_changed' && row.price_change_amount < 0
Enter fullscreen mode Exit fullscreen mode

The formatter's controlled test uses a simulated $2,000 → $1,800 change to check the “was” label. That is a formatting test, not an observed market price cut. A change in the maximum rent alone can have a zero minimum-rent delta. Missing listings are not labeled rented or withdrawn, because disappearance from a capped search window is not proof of either.

Know the event cost

On Free, the Actor charges $0.015 per emitted listing, plus $0.01 when that returned listing has a phone. The start event is $0.00005 at 512 MB. Plan discounts apply; the pricing was not changed for this workflow.

The three-row sample had two phones, so its Free-equivalent event cost is $0.06505. It was an owner test with no customer payment collected. A seed-only run or an unchanged repeat has no result or phone charge, but the start event remains. At the template's 25-row cap, even 25 rows with phones fit within its $0.75 charge limit at these prices.

Use the real sample to inspect the digest, then start with a small search for your own market. Keep the monitor ID stable, investigate failed runs, and preserve the source links when sending the changes onward.

Top comments (0)