From find-apartments
Use when scraping Madlan rental listings for a specific city and room count — calls Madlan's internal GraphQL API directly via a Node.js script (no browser, no CAPTCHA).
How this skill is triggered — by the user, by Claude, or both
Slash command
/find-apartments:search-madlanThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Scrapes rental apartment listings from madlan.co.il by calling their internal GraphQL API at `https://www.madlan.co.il/api2`. No browser required.
Scrapes rental apartment listings from madlan.co.il by calling their internal GraphQL API at https://www.madlan.co.il/api2. No browser required.
Madlan's HTML pages are protected by PerimeterX (HUMAN Security), which blocks WebFetch, Puppeteer, and most stealth approaches through TLS/JA3 fingerprinting and behavioral analysis. Their GraphQL API, however, is only gated by a Bearer token — not PerimeterX — and a visitor-role JWT captured from a real browser session works fine for read-only queries.
The token is baked into scripts/madlan-api.mjs and currently valid until 2027. If Madlan rotates the scheme or the token expires, re-capture a fresh one from the browser's Network tab (look for any POST /api2 request and copy the authorization: Bearer ... header).
fetch).scripts/madlan-api.mjs must exist. Locate the plugin directory:PLUGIN_DIR=$(find ~/.claude/plugins/cache -path "*/find-apartments/scripts/madlan-api.mjs" -exec dirname {} \; 2>/dev/null | head -1 | sed 's|/scripts||')
If not found, check ~/.claude/plugins/local/find-apartments/.
city — Hebrew city name (e.g., "הוד השרון")min_rooms — minimum room count (e.g., 5)max_price — optional (not yet filtered server-side; filter client-side after)Run the script with the city and room count:
node "$PLUGIN_DIR/scripts/madlan-api.mjs" "הוד השרון" 5 --deal rent
Arguments:
4.5)--deal rent (default) or --deal buyThe script converts spaces in the city name to hyphens and builds Madlan's docId pattern: "<city>-ישראל". This works for standard Hebrew city names.
The script prints a single JSON object to stdout:
{
"total": 45,
"count": 45,
"listings": [
{
"url": "https://www.madlan.co.il/listings/DXVl8yEOS8M",
"source": "Madlan",
"city": "הוד השרון",
"address": "הדרים 1",
"neighborhood": "מגדיאל",
"rooms": 5,
"floor": "10",
"size_sqm": 150,
"price": 8500,
"mamad": null,
"parking": null,
"elevator": null,
"ac": null,
"balcony": null,
"pets": null,
"available_from": "2026-04-01T12:21:49.000Z",
"contact": null,
"images": ["https://madlan.co.il/bulletins/..."]
}
]
}
Parse the JSON and return listings from the skill. If max_price was passed as input, filter the listings array in-memory before returning.
"Madlan API failed: <reason>", and return an empty array. Do NOT fail the entire search run.errors field or empty poi, return an empty array with a note.scripts/madlan-api.mjs.Previous attempts that failed:
WebFetch — 403 from PerimeterX on every request.navigator.webdriver and initial fingerprint, but PerimeterX caught behavioral/TLS signatures on the second request.npx claudepluginhub cxt9/find-apartments --plugin find-apartmentsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.