Automatically monitors Yad2 for new apartment listings and sends Telegram alerts.
- Polls configured neighborhood search URLs every 30 minutes
- Extracts listings from Yad2's server-rendered
__NEXT_DATA__JSON (no headless browser needed) - Tracks seen listing tokens in a local SQLite DB to avoid duplicate alerts
- Fetches the seller's phone number via the Yad2 gateway API (works for both private and agency listings)
- Sends formatted Telegram messages to one or more chats with listing details, phone number, and a direct link
- Appends every new listing to a monthly Markdown journal (
~/.yad2/journal/) for persistent history and Obsidian integration
cd /Users/ilia/projects/yad2
/opt/homebrew/bin/poetry installCreate a .env file in the project root (never commit this):
echo "YAD2_BOT_TOKEN=<your-bot-token>" > .envThe bot token is loaded automatically from .env at startup — do not put it in config.yaml.
Send any message to @yad2_jlm_bot, then run:
/opt/homebrew/bin/poetry run yad2-watcher get-chat-idCopy the chat_id values and add them to config.yaml:
telegram:
chat_ids:
- "111111111"
- "222222222" # add as many recipients as you like/opt/homebrew/bin/poetry run yad2-watcher test-notifyYou should receive a test message in every configured chat.
/opt/homebrew/bin/poetry run yad2-watcher run# Create logs directory
mkdir -p logs
# Install the launchd plist
cp com.yad2.watcher.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.yad2.watcher.plistThe watcher will now run every 30 minutes automatically, even after reboots (as long as you're logged in).
To stop it:
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.yad2.watcher.plistTo check status:
launchctl list | grep yad2To view logs:
tail -f logs/watcher.log/opt/homebrew/bin/poetry run yad2-watcher statusyad2-watcher run # Single scan pass
yad2-watcher watch # Continuous loop (for testing)
yad2-watcher watch -i 15 # Loop with custom interval (15 min)
yad2-watcher get-chat-id # Find your Telegram chat IDs
yad2-watcher test-notify # Send a test message to all configured chats
yad2-watcher status # Show DB stats and recent runs
yad2-watcher download <url> # Download apartment details and photos to downloads/<token>
Edit config.yaml and add entries to the neighborhoods list:
neighborhoods:
- id: 561
name: "גבעת הורדים, רסקו"
url_slug: "jerusalem-area"
- id: 567
name: "רחביה"
url_slug: "jerusalem-area"To find neighborhood IDs: look at the Yad2 search URL — the neighborhood= parameter is the ID.
Every new listing is appended to a monthly Markdown file in ~/.yad2/journal/:
~/.yad2/journal/
├── journal_2026-04.md
├── journal_2026-05.md
└── ...
Each entry looks like:
## 2026-04-19 22:31 | ₪7,500/חודש #neighborhood/rasko
- 📍 **Address:** דוד שמעוני 10, גבעת הורדים
- 🛏️ **Rooms:** 4 | **SQM:** 90 | **Floor:** 2
- 🏷️ **Type:** פרטי · מרפסת · חניה
- 📞 **Phone:** 052-4283314
- 🔗 [פתח מודעה](https://www.yad2.co.il/item/abc123)
- ⏰ **Seen at:** 2026-04-19 22:31:05Open ~/.yad2/journal/ as an Obsidian vault (or add it to an existing vault).
Each entry carries a #neighborhood/<name> tag, so you can:
- Filter by area — click any tag in the Tags panel (e.g.
#neighborhood/rasko) - Graph view — neighborhood tags appear as cluster nodes linking their listings
- Dataview — build a live index note with a query like
LIST FROM #neighborhood/rasko
To disable the journal, set in config.yaml:
watcher:
journal_enabled: falseTo change the output directory:
watcher:
journal_path: "~/my-notes/apartments"/opt/homebrew/bin/poetry run pytest tests/ -v90 tests covering fetcher, store, notifier, watcher, and journal — all mocked, no network calls.
yad2/
├── .env # Secrets — bot token (gitignored)
├── config.yaml # Configuration — neighborhoods, filters, chat IDs
├── pyproject.toml # Poetry project
├── com.yad2.watcher.plist # macOS launchd scheduler
├── logs/ # Runtime logs (gitignored)
├── src/yad2_watcher/
│ ├── fetcher.py # HTTP fetch + JSON parsing
│ ├── store.py # SQLite dedup store (~/.yad2_watcher/seen.db)
│ ├── notifier.py # Telegram Bot sender (broadcasts to all chat IDs)
│ ├── journal.py # Monthly Markdown journal (~/.yad2/journal/)
│ ├── watcher.py # Main orchestration loop
│ └── cli.py # Click CLI
└── tests/
├── conftest.py # Shared fixtures and sample data
├── test_fetcher.py # Fetcher + Listing tests
├── test_store.py # SQLite store tests
├── test_notifier.py # Message formatting + Telegram API tests
├── test_watcher.py # Orchestration tests
└── test_journal.py # Journal tests