StreetEasy $1๐ฐ URL Search | Keyword Filtering | Rich Output
Pricing
from $1.00 / 1,000 results
StreetEasy $1๐ฐ URL Search | Keyword Filtering | Rich Output
From $1/1K. Scrape streeteasy.com across NYC and Jersey City for sales, rentals, in-contract, and sold properties. Supports filters or direct URLs. Returns pricing, coordinates, photos, brokerage, amenities, building details, agent contacts, and price history.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
18
Total users
4
Monthly active users
1.8 hours
Issues response
4 days ago
Last modified
Categories
Share
StreetEasy NYC Scraper
Pull complete NYC and Jersey City listings from StreetEasy: for-sale, for-rent, in-contract, and recently sold. Combine filters (areas, price, beds, baths, sqft, amenities, sort) into a single search, or paste any StreetEasy URL and let the actor walk pagination forward. Returns 30+ fields per listing including coordinates, neighborhood, brokerage, photos, and optional detail-page enrichment for amenities, building info, agent contact, and price history.
Why This Scraper?
- Structured filter mode covering price, beds, baths, sqft, amenities, sort, and 5 listing categories (sale, rent, in contract, sold, off market).
- Returns up to 500 listings per page, so a 100-result run completes in seconds.
- Coordinates (latitude / longitude) on every record without needing detail enrichment.
- Auto-expand mode splits queries above ~1,050 results into price buckets so you can extract the full market.
- URL mode accepts any sale, rent, sold, building, or filtered StreetEasy URL.
- Pay-per-result pricing: low base rate; detail enrichment surcharge fires only when you turn it on, only for records that actually returned detail.
- All five NYC boroughs plus Jersey City, Hoboken, and surrounding NJ areas.
- Optional incremental mode for recurring/scheduled monitoring: run the same search daily or weekly and get back only what's new, changed, reappeared, or gone.
Data You Get
Sample shape, values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
| id | 0000001 |
| url | https://streeteasy.com/building/sample-tower/4a |
| saleType | forSale |
| status | For sale |
| price | 1250000 |
| neighborhood | Sample Neighborhood |
| borough | Manhattan |
| address | 100 Sample Street |
| unit | 4A |
| bedrooms | 2 |
| bathrooms | 2 |
| halfBathrooms | 0 |
| squareFeet | 1100 |
| pricePerSqft | 1136.36 |
| propertyType | Condo |
| latitude | 40.7000 |
| longitude | -74.0000 |
| imageUrl | https://photos.zillowstatic.com/fp/0-uncropped.webp |
| brokerage | Sample Realty |
| listedAt | 2026-01-01 |
| daysOnMarket | 30 |
| advertisedListing | false |
| description | (when fetchDetails = true) Full listing description |
| amenities | ["Doorman", "Elevator", "Gym"] |
| buildingName | Sample Tower |
| buildingYear | 1990 |
| schoolDistricts | ["1"] |
| transit | ["6", "Q"] |
| priceHistory | [{date,event,price}, ...] |
| agents | [{name,phone,brokerage}, ...] |
| openHouseDates | ["2026-02-01"] |
How to Use
Search mode, single borough:
{"mode": "search","saleType": "forSale","locations": ["manhattan"],"minPrice": 1000000,"maxPrice": 2000000,"minBedrooms": 2,"amenities": ["DOORMAN", "ELEVATOR"],"sortBy": "PRICE_ASC","maxPages": 5,"maxListings": 200}
Search mode, multi-borough rentals:
{"mode": "search","saleType": "forRent","locations": ["brooklyn", "queens"],"maxPrice": 4000,"noFee": true,"minBedrooms": 1,"sortBy": "LISTED_AT_DESC","maxListings": 100}
URL mode, multi-URL:
{"mode": "url","urls": ["https://streeteasy.com/for-sale/williamsburg","https://streeteasy.com/for-rent/upper-east-side?page=2","https://streeteasy.com/sold/manhattan"],"maxPages": 3,"perPage": 200}
Detail enrichment (slower, full agent + amenity coverage):
{"mode": "search","saleType": "forSale","locations": ["dumbo"],"fetchDetails": true,"maxListings": 25}
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | enum | search | search (filters) or url (paste URLs). |
| saleType | enum | forSale | forSale, forRent, inContract, recentlySold, offMarket. |
| locations | string[] | ["nyc"] | Slugs (manhattan, brooklyn, williamsburg, jersey-city, ...) or numeric IDs. |
| propertyTypes | enum[] | [] | CONDO, CO_OP, HOUSE, TOWNHOUSE, MULTI_FAMILY, RENTAL, CONDOP. |
| minPrice / maxPrice | integer | none | Price range in USD. |
| minBedrooms | integer | none | Use 0 for studios. |
| maxBedrooms | integer | none | |
| minBathrooms | number | none | Decimal supported (1.5). |
| minSqft / maxSqft | integer | none | Living area in square feet. |
| amenities | enum[] | [] | Listings must include all selected amenities. |
| noFee | boolean | false | Rentals only: filter to no-broker-fee listings. |
| keywords | string | none | Free-text search inside the description / title. |
| sortBy | enum | RECOMMENDED | RECOMMENDED, PRICE_ASC/DESC, LISTED_AT_ASC/DESC, SQFT_DESC, BEDROOMS_DESC. |
| urls | string[] | [] | URL mode only. |
| maxPages | integer | 0 (unlimited) | Pagination cap per query. Leave empty to walk every result page until exhausted (bounded only by maxListings). |
| perPage | integer | 100 | Listings per page (10 to 500). |
| maxListings | integer | 20 | Hard cap across the whole run, 0 means unlimited. Bump for production scrapes. |
| fetchDetails | boolean | false | Enrich each record with detail page fields. Adds 1 HTTP request per record. |
| expandLargeQueries | boolean | false | Auto-shard queries above 1,050 results into price buckets and merge. |
| proxy | object | Apify Residential US | Country forced to US. |
| resumeFromRunId | string | none | Previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already saved there (matched by listing id) are skipped. |
| incrementalMode | boolean | false | Recurring/scheduled monitoring of the SAME search. Off by default so no existing run changes until you opt in. |
| stateKey | string | none (auto) | Incremental mode only. Names a monitoring campaign, or deliberately shares state across differently-configured runs. Leave empty to auto-derive one from the search scope. |
| emitUnchanged | boolean | false | Incremental mode only. Also return listings unchanged since the last run, marked UNCHANGED. Returns and bills extra rows. |
| emitExpired | boolean | false | Incremental mode only. Also return listings no longer found, marked EXPIRED โ only after a complete scan (not when Max listings/Max pages capped the run, the site blocked a page, or Resume was used). Returns and bills extra rows. |
For an unbounded pull (maxPages left empty, maxListings: 0), resumeFromRunId lets you continue in a brand-new run: paste the ID of a previous run (or its dataset), and this run skips every listing id already saved there, appending only new listings. The actor also checkpoints its dedup set to the key-value store periodically, so a platform migration or restart mid-run resumes without re-scraping (and re-charging) already-saved listings.
Resume and recurring updates
Two different things, both under the same input section:
resumeFromRunIdcontinues one specific interrupted run (or grows one big catalogue pull across separate runs). It does not remember anything about the search itself โ you paste the previous run/dataset id each time.incrementalModeis for running the same search again and again (daily, weekly โ on a schedule) and getting back only what changed. Turn it on and the actor remembers the previous run of that search itself, keyed bystateKey(or an automatic key derived from your search scope โ locations/URLs, filters, andfetchDetails). Every emitted row then carrieschangeType(NEW/UPDATED/REAPPEARED/EXPIRED/UNCHANGED),changedFields,firstSeenAt, andlastSeenAt.- By default only
NEW,UPDATED, andREAPPEAREDlistings are returned โ unchanged listings are skipped and not billed. - Turn on
emitUnchangedto also getUNCHANGEDrows back (billed like any other row). - Turn on
emitExpiredto also get a syntheticEXPIREDrow for a listing that dropped out of the tracked search โ but only once a run has proven a complete scan (Max listings/Max pages didn't cap it, the site didn't block a page mid-walk, and Resume wasn't used). A partial run never marks anything expired; it just keeps last run's state as-is. - Two differently-configured searches (different locations, filters, or
fetchDetails) never share a baseline unless you set the samestateKeyon purpose. incrementalModeandresumeFromRunIdare normally not combined. If you turn both on against a search that already has saved incremental state, the run fails fast with a clear message instead of silently mixing the two. Combining them only makes sense to bootstrap a brand-new monitoring campaign from an existing catalogue pull โ see the schema field descriptions for details.- Detail-enrichment billing is unaffected by suppression: an
UNCHANGEDrow that gets skipped was never charged thedetail-enrichmentsurcharge either, even though its detail page still had to be fetched to check whether it changed.
- By default only
{"mode": "search","saleType": "forSale","locations": ["park-slope"],"incrementalMode": true,"stateKey": "park-slope-daily","maxListings": 0}
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape โ the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record โ not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify โ Settings โ Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Output Example
Sample shape, values are illustrative placeholders.
{"id": "0000001","url": "https://streeteasy.com/building/sample-tower/4a","saleType": "forSale","status": "For sale","price": 1250000,"neighborhood": "Sample Neighborhood","borough": "Manhattan","address": "100 Sample Street","unit": "4A","bedrooms": 2,"bathrooms": 2,"halfBathrooms": 0,"squareFeet": 1100,"pricePerSqft": 1136.36,"propertyType": "Condo","latitude": 40.7000,"longitude": -74.0000,"imageUrl": "https://photos.zillowstatic.com/fp/0-uncropped.webp","images": ["https://photos.zillowstatic.com/fp/0-uncropped.webp"],"brokerage": "Sample Realty","listedAt": "2026-01-01","daysOnMarket": 30,"offMarketAt": null,"furnished": false,"noFee": null,"advertisedListing": false,"description": "Full seller description text appears here when fetchDetails = true.","amenities": ["Doorman", "Elevator", "Gym"],"buildingName": "Sample Tower","buildingYear": 1990,"schoolDistricts": ["1"],"transit": ["6", "Q"],"priceHistory": [{ "date": "2026-01-01", "event": "Listed", "price": 1250000 }],"agents": [{ "name": "Agent Name", "phone": "+10000000000", "brokerage": "Sample Realty" }],"openHouseDates": ["2026-02-01"],"virtualTourUrl": null,"sourceUrl": null}
With incrementalMode: true, every row additionally carries:
{"changeType": "UPDATED","changedFields": ["price", "status"],"firstSeenAt": "2026-06-01T00:00:00.000Z","lastSeenAt": "2026-07-29T00:00:00.000Z"}
changeType is one of NEW / UPDATED / REAPPEARED / UNCHANGED (only when emitUnchanged is on) / EXPIRED (only when emitExpired is on). These four fields are absent entirely when incrementalMode is off โ the default output shape is unchanged.
Plan Requirement
This actor requires Apify Residential proxy with country US. StreetEasy blocks datacenter ranges, so the default Apify proxy (datacenter) will return 0 results.
- Free plan: does not include Residential proxy. Upgrade to Starter or higher.
- Starter plan and above: includes Residential proxy access.
- The actor automatically pins country to US even if you forget to select it.
If you run on Free plan with the default proxy, you will see a prominent warning at the start of the run and an empty dataset. Upgrade and re-run.