| name | apartment-search |
| version | 0.1.0 |
| description | Search NYC apartment listings using NYBits (no-bot-detection). Works from headless servers where StreetEasy/Zillow/Apartments.com block with captchas. Supports filtering by neighborhood, bedrooms, price, fee status, and amenities. Use this skill whenever the user asks to find, search, or browse apartments in NYC.
|
| triggers | ["apartment","rental","2br","1br","for rent","find apartment","search apartment","listing"] |
Apartment Search Skill
Problem
Major NYC real estate sites (StreetEasy, Zillow, Apartments.com, RentHop) all use
aggressive bot detection (Cloudflare, captchas, "Press & Hold") that blocks headless
browsers on cloud servers. Google Search also blocks from datacenter IPs.
Solution: NYBits.com
NYBits is a smaller NYC rental aggregator that does NOT use bot detection. It works
with both curl and headless Chrome from cloud servers.
How NYBits Search Works
NYBits uses a server-side HTML form (not AJAX). URL query params alone DON'T work —
the form requires hidden session tokens (_rid_, _ust_todo_, _xid_).
Step 1: Load the search page
Navigate browser to: https://www.nybits.com/search/
Step 2: Fill form via JavaScript
Use browser_console to set form values and submit:
(() => {
const f = document.forms['sform'];
document.getElementById('sball').checked = false;
document.getElementById('sb2').checked = true;
f.elements['!!rmax'].value = '6000';
f.querySelector('input[name="!!fee"][value="any"]').checked = true;
f.querySelectorAll('input[name="nei"]').forEach(cb => {
if (cb.value === 'downtown_brooklyn' || cb.value === 'long_island_city') cb.checked = true;
});
f.querySelector('input[type="submit"]').click();
return 'submitted';
})()
Step 3: Extract results via JavaScript
After the results page loads:
(() => {
const arts = document.querySelectorAll('article');
const listings = [];
arts.forEach(a => {
const title = a.querySelector('h3')?.textContent?.trim() || '';
const price = a.textContent.match(/\$[\d,]+\/month/)?.[0] || '';
const beds = a.textContent.match(/(\d+)\s*Beds?/)?.[1] || '';
const baths = a.textContent.match(/([\d.]+)\s*Baths?/)?.[1] || '';
const link = a.querySelector('a')?.href || '';
listings.push({ title, price, beds, baths, link });
});
return JSON.stringify(listings);
})()
Step 4: Check individual listings for amenities
Navigate to each listing URL. The description paragraph reveals in-unit W/D and
other details not shown in search results. Use curl for speed:
curl -sL -A "Mozilla/5.0" "<listing_url>" | grep -oP 'listing__text[^>]*>\K[^<]+'
Available Neighborhoods (NYBits nei values)
See references/nybits-neighborhoods.md for the complete list. Key ones:
Brooklyn: downtown_brooklyn, brooklyn_heights, dumbo, bushwick,
flatbush, greenpoint, park_slope, williamsburg
Queens: long_island_city, astoria, rego_park, ridgewood
Manhattan: alphabet_city, battery_park_city, chelsea, east_village,
financial_district, gramercy_park, hells_kitchen, hudson_yards,
kips_bay, midtown_east, murray_hill, soho, tribeca, upper_east_side,
upper_west_side, west_village, stuy_town, and many more.
Form Fields Reference
| Field | Name | Values |
|---|
| Layout | !!atypes | studio, 1br, 2br, 3more, room |
| Min rent | !!rmin | number |
| Max rent | !!rmax | number |
| Fee status | !!fee | nofee, any |
| Sort order | !!orderby | dateposted, neighborhood, rent |
| Neighborhoods | nei | see list above (repeatable) |
| Amenities | checkbox names | doorman, elevator, gym, garage, children |
Pitfalls
- URL params don't work — the form requires session tokens; must use browser JS
- Checkbox
all must be unchecked first — otherwise all layouts are returned
- Variable redeclaration errors — browser_console runs in shared scope; wrap code
in IIFE
(() => { ... })() or use var instead of const/let if re-running
- NYBits has limited inventory — may have 0 results for some neighborhoods;
try expanding to adjacent areas
- In-unit W/D not in search filters — must check individual listing descriptions
submit button naming conflict — NYBits names its submit button submit,
which shadows form.submit(). Use btn.click() instead.
- ⚠️ NYBits pricing can be STALE — prices shown may not match current StreetEasy
or building-site listings. User reported a listing showing $5,700 on NYBits but
$6,895 on the actual site. Always verify prices on the building's own website or
StreetEasy before presenting to the user. NYBits is useful for discovery (finding
buildings/neighborhoods with inventory) but NOT as a source of truth for pricing.
- Browserbase plan requirements — residential proxies and advanced stealth (needed
for StreetEasy/Zillow) require Browserbase Scale plan, not Launch. The API returns
402 for proxies on Launch plan, and the code gracefully falls back to no proxies.
Without proxies, Browserbase sessions are blocked by the same captchas as local Chrome.
See
references/browserbase-plans.md for details.
Multi-Source Strategy (Future)
If more inventory is needed, try these additional sources that may work:
- ListingsProject.com — returned HTTP 200 from curl, may have listings
- NakedApartments.com — no-fee focused
- The Listings Project (newsletter) — weekly curated no-fee listings
Major sites requiring stealth mode (BROWSERBASE_ADVANCED_STEALTH):
- StreetEasy, Zillow, Apartments.com, RentHop — all blocked from datacenter IPs