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.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub cxt9/find-apartments --plugin find-apartments