If your evenings look like twelve Zillow tabs, a Slack channel with links you swear you’ll revisit, and a spreadsheet called maybe-next-home.xlsx, this article is for you. I’m walking through exactly how I wired OpenClaw (v3.7.2 on Node 22.4) to monitor listings on Rightmove and Zillow, ping me on Telegram the minute something new matches my filters, run monthly-payment maths against live rates, pull neighbourhood crime stats, and shove viewing slots straight into Google Calendar. No sponsored plugins, no mystical AI — just the open-source bits shipping on GitHub right now.

Why automate house hunting with OpenClaw?

Before the recipes, a quick list of pain points automation actually fixes:

  • Latency: Listing sites keep the good stuff at the top for 5–30 minutes. If you’re not first, you’re tenth.
  • Repetition: Most buyers search identical filters multiple times a day. Agents do not discount your stress tax.
  • Context switching: Mortgage calculators, school ratings, commute times live in different tabs. Copy-pasting guarantees errors.
  • Scheduling: Calling agents while you’re at work is awkward. Automatically proposing viewings based on your calendar saves the back-and-forth.

OpenClaw ships with browser control, persistent memory, scheduled tasks and 800+ tool integrations through Composio. That’s enough to stitch the entire workflow together without writing a private SaaS from scratch.

Prerequisites and one-time setup

I’m assuming you already have Node 22+ and Git installed. Everything else is just npm.

# fresh directory for the agent mkdir claw-estate && cd claw-estate npm init -y npm install openclaw@3.7.2 playwright@1.44.0 # Chromium driver # optional: dotenv for local secrets npm install dotenv

Run the OpenClaw gateway locally:

npx openclaw gateway --port 3000

You’ll see the browser UI at http://localhost:3000. That’s where we’ll paste YAML workflows in a minute. For production I push the same agent to ClawCloud so it runs even when my laptop sleeps:

# from the same directory npx openclaw cloud deploy --name "home-hunt" --plan hobby

The --plan hobby tier is free and gives one agent, 256 MB memory, and scheduled tasks every five minutes — plenty for listing checks.

Scraping listings with Playwright actions

OpenClaw’s built-in browser control is a thin wrapper around Playwright. That means every selector and wait condition you see in Playwright docs works unchanged. Below is the action I use for Rightmove. Zillow is near-identical; just swap URLs and selectors.

# .claw/actions/rightmove-scrape.mjs import { chromium } from 'playwright'; export default async function (ctx) { const browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); const url = `https://www.rightmove.co.uk/property-for-sale/find.html?locationIdentifier=REGION%5E93966&minBedrooms=3&maxPrice=600000`; // edit to taste await page.goto(url, { waitUntil: 'domcontentloaded' }); const cards = await page.$$('[data-test="property-card"]'); const listings = []; for (const card of cards) { const title = await card.$eval('address', el => el.textContent.trim()); const price = await card.$eval('[data-test="property-card-price" ]', el => el.textContent.trim()); const href = await card.$eval('a.propertyCard-link', el => el.href); listings.push({ title, price, href }); } await browser.close(); return listings; }

Save that file, then declare it in openclaw.yaml so the agent can call it:

actions: rightmoveList: ./actions/rightmove-scrape.mjs

Repeat for Zillow. Community PR #482 shows a full selector map if you’re curious.

De-duplicating and alerting in real time

Scraping every five minutes will spam you unless you remember what you’ve already seen. OpenClaw’s memory store is a simple key–value blob that survives restarts. Perfect for checksum lists.

# .claw/flows/listing-alert.yaml name: listingAlert schedule: "*/5 * * * *" # every 5 minutes steps: - id: pull uses: rightmoveList - id: filterNew run: | const seen = memory.get('seenLinks') || []; const fresh = pull.output.filter(l => !seen.includes(l.href)); memory.set('seenLinks', [...seen, ...fresh.map(f => f.href)].slice(-5000)); return fresh; - id: notify if: filterNew.output.length > 0 uses: composio/gmail.send with: to: "me@gmail.com" subject: "🏠 New Rightmove matches ({filterNew.output.length})" body: | ${filterNew.output.map(l => `${l.price} - ${l.title}\n${l.href}`).join('\n\n')}

Yes, the Unicode house in the subject survives most mail clients; feel free to drop it. Swap composio/gmail.send for telegram.sendMessage or slack.postMessage if that’s your channel. The key point: only unseen links trigger notifications.

Tossing the same logic at Zillow

My convention is seenLinks:zillow vs seenLinks:rightmove in memory to keep the keys separate. Otherwise the flow is identical.

Mortgage and affordability calculations

Numbers turn listings into decisions. The snippet below relies on npm install mortgage-js@2.1.0, a tiny library that handles compound interest correctly. I wired it as a custom action so I can reuse it across flows.

// .claw/actions/mortgage.mjs import { monthlyPayment } from 'mortgage-js'; export default async function (ctx, { price, deposit = 0.1, years = 25, rate = 0.05 }) { const principal = price * (1 - deposit); const payment = monthlyPayment(principal, rate, years); return { principal, payment: payment.toFixed(2) }; }

Now chain it after the scrape:

- id: enrich uses: mortgage with: price: "{{ pull.output[0].price.replace(/[^0-9]/g, '') }}"

The RegEx strips commas and the pound sign. Feel free to over-engineer with Intl.NumberFormat.

Area research: schools, crime, commute

Listings don’t mention that the nearest primary school is “Requires Improvement”. For UK readers, Ofsted publishes JSON feeds; in the US, GreatSchools has an open API with rate limiting. Crime data is public for both regions. I bundled three quick actions:

  • schoolScore: fetches rating by postcode or ZIP
  • crimeStats: counts incidents within 1 mile radius last 12 months
  • commuteTime: hits Google Maps Distance Matrix (yes, API key required)

The idea isn’t to replace your own judgement — it’s to decide in 30 seconds whether a listing is worth booking.

Auto-proposing viewing slots

Everyone hates calendar ping-pong. Since Composio wraps Google Calendar, the flow is one additional step once you’ve short-listed a property:

- id: nextFreeSlot uses: composio/gcal.findNextFree with: durationMinutes: 30 windowStart: "2024-09-01T09:00:00" windowEnd: "2024-09-07T19:00:00" - id: hold uses: composio/gcal.createEvent with: summary: "Viewing: {{ property.title }}" start: "{{ nextFreeSlot.output.start }}" end: "{{ nextFreeSlot.output.end }}"

You’ll end up with a tentative hold on your calendar that you can quote when you phone the agent. If they confirm, keep it; if not, delete — but you’ve saved two emails already.

Putting it all together: sample full workflow

Below is my current openclaw.yaml. Eight steps, runs every five minutes, and touches only free APIs.

# openclaw.yaml version: "1" actions: rightmoveList: ./actions/rightmove-scrape.mjs mortgage: ./actions/mortgage.mjs schoolScore: ./actions/schools.mjs crimeStats: ./actions/crime.mjs commuteTime: ./actions/commute.mjs flows: - name: hunt schedule: "*/5 * * * *" steps: - id: listings uses: rightmoveList - id: fresh run: | const seen = memory.get('links') || []; const news = listings.output.filter(l => !seen.includes(l.href)); memory.set('links', [...seen, ...news.map(n => n.href)].slice(-10000)); return news; - id: each foreach: fresh.output steps: - id: loan uses: mortgage with: price: "{{ item.price.replace(/[^0-9]/g, '') }}" - id: school uses: schoolScore with: postcode: "{{ item.title.split(',').pop().trim() }}" - id: crime uses: crimeStats with: postcode: "{{ item.title.split(',').pop().trim() }}" - id: commute uses: commuteTime with: origin: "SW1A 1AA" # office postcode destination: "{{ postcode }}" - id: grade run: | const ok = school.output.rating > 3 && crime.output.incidents < 50 && commute.output.minutes < 45; return { ok }; - id: notify if: grade.output.ok uses: composio/telegram.sendMessage with: chat_id: "123456" text: | 🏠 {{ item.price }} {{ item.title }}\n\n💰 £{{ loan.output.payment }} / month\n🎓 School: {{ school.output.rating }}/5\n🚨 Crime: {{ crime.output.incidents }} last year\n🚇 Commute: {{ commute.output.minutes }} min\n\n{{ item.href }}

Total runtime per cycle on ClawCloud hobby tier: ~4 seconds, 30 MB RSS. Plenty of headroom.

Operational considerations and caveats

  • Rate limits: Zillow throws a 403 if you hit them more than ~100 times per hour per IP. Five-minute schedule is fine; one-minute will burn you.
  • CAPTCHA: Both sites occasionally serve bot checks. Playwright’s stealth plugin works but violates TOS; use at your risk.
  • Data freshness: Mortgage rates change daily. I pull the Bank of England CSV at 01:00 and update a memory value used by the mortgage action.
  • GDPR/PII: Crime and school APIs return aggregates, so no personal data is stored, but double-check local laws if you’re in a different region.
  • Mobile push: Telegram is easy. If you’re on iMessage only, wrap osa script under a custom action; works, but fragile.

Next steps

The flow above got me from “I might have missed a house yesterday” to “I’m booking viewings before the listing has 100 views”. Clone the repo skeleton (github.com/psteiner/openclaw-realestate), tweak the selectors for your market, and deploy to ClawCloud so it keeps grinding while you sleep. Your future self — and possibly your estate agent — will thank you.