StreetEasy Scraper - NYC Listings, Prices & Agents avatar

StreetEasy Scraper - NYC Listings, Prices & Agents

Pricing

from $1.00 / 1,000 results

Go to Apify Store
StreetEasy Scraper - NYC Listings, Prices & Agents

StreetEasy Scraper - NYC Listings, Prices & Agents

Scrape StreetEasy sales, rentals and in-contract listings across NYC and Jersey City. Search with filters or URLs and extract prices, GPS coordinates, photos, amenities, building details, brokerages, agent contacts and price history.

Pricing

from $1.00 / 1,000 results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

33

Total users

6

Monthly active users

a day ago

Last modified

Share

StreetEasy Scraper: NYC and Jersey City Listings, Prices, and Price History

StreetEasy Scraper turns StreetEasy into structured, exportable property data. Combine filters, price, beds, baths, square feet, amenities, no-fee, keywords, and sort order into one search, or paste a StreetEasy search-result URL and let the actor walk pagination forward. Covers for-sale, for-rent, and in-contract listings across all five NYC boroughs plus Jersey City and Hoboken, with optional detail-page enrichment for full descriptions, amenities, building info, agent contacts, price history, and open house dates. Export to JSON, CSV, or Excel, read results through the Apify API, or pipe them into your own app.

Why This Scraper?

  • Two ways in. Combine filters (price, beds, baths, square feet, amenities, no-fee, keywords, sort, and three listing categories) into one search, or paste StreetEasy search-result URLs, including their own price, beds, and sort path segments.
  • Big pages. Up to 500 listings per page, so a large pull needs fewer requests.
  • Coordinates included. Latitude and longitude on every listing without detail-page enrichment.
  • Full-market coverage. Auto-expand mode splits a search above roughly 1,050 matching results into price buckets and merges them.
  • Optional enrichment. Detail pages add the full description, verified amenities, building name and year, price history, agent contacts, and open house dates.
  • Rental concessions. Net effective rent, months free, lease term, and move-in date for concession-aware rental analysis.
  • Incremental mode. For scheduled runs, get back only listings that are new, updated, reappeared, or gone since the last run.

Use Cases

  • Real estate investors and analysts: track price per square foot and price history to spot pricing trends across buildings or neighborhoods.
  • Renters and apartment hunters: filter by no-fee, amenities, and neighborhood to shortlist rentals without paging through search results.
  • PropTech and market research teams: build a recurring dataset of NYC and Jersey City listings for pricing models and market reports.
  • Brokerages and agencies: monitor a set of buildings or neighborhoods for new listings and price changes.
  • Content and news teams: source current listing data for neighborhood market coverage.

Data You Get

Sample shape, values are illustrative placeholders, not from a live listing.

FieldExample
id"0000001"
url"https://streeteasy.com/building/sample-tower/4a"
saleType / status"forSale" / "For sale"
price1250000
neighborhood / borough"Williamsburg" / "Brooklyn" (borough is a coordinate-based estimate, see FAQ)
address / unit"100 Sample Street" / "4A"
bedrooms / bathrooms / halfBathrooms2 / 2 / 0
squareFeet / pricePerSqft1100 / 1136.36
propertyType"Condo"
latitude / longitude40.7000 / -74.0000
imageUrl / imagesa listing photo URL / array of photo URLs
brokerage"Sample Realty"
listedAt / daysOnMarket"2026-01-01" / 30 (filled with Fetch detail pages on, null otherwise)
zipCode / state"10011" / "NY"
mediaAssetCount / hasVideos / hasTour3d12 / false / false
priceDelta / priceChangedAtlast recorded price change and its date, null if never changed
netEffectivePrice / monthsFree / leaseTerm / availableAtrentals only: rent after a free-month concession, months free, lease length, move-in date; null on sales
description / amenities(when Fetch detail pages is on) full listing description and a normalized amenity list
buildingName / buildingYear(when Fetch detail pages is on, and the site's page exposes them)
priceHistory(when Fetch detail pages is on) list of {date, event, price} entries
agents(when Fetch detail pages is on) one object per listing agent: name, phone, brokerage, license (license type), profileId, profileUrl, contactId, isPro, headshotUrl, and the office businessName plus businessAddress1, businessAddress2, businessCity, businessState, businessZip, fullBusinessAddress. Sale listings also carry email, agentId and (when the agent wrote one) bio as plain text; rentals expose a phone but no email. A key is simply absent when the page does not ship it.
openHouseDates / virtualTourUrl(when Fetch detail pages is on) upcoming open house dates, and the 3D tour link when the listing publishes one (otherwise null)
buildingId / areaId / residentialUnitCount / roomCount / nearbySchools(when Fetch detail pages is on) StreetEasy building id, neighborhood area id, residential units in the building, rooms in the unit, and nearby schools as {id, name, district, grades}
statusDate / verifiedUpToDate / salesStartDate(when Fetch detail pages is on) date of the latest status change, whether the listing is marked verified up to date (sales), and the sales start date (new developments only, otherwise null)

With incrementalMode on, every row also carries changeType, changedFields, firstSeenAt, and lastSeenAt (see Output Example).

How to Use

  1. Pick a mode: search (build a query from filters) or url (paste one or more StreetEasy search-result URLs).
  2. In search mode, choose Listing type and Locations. In url mode, paste the URLs. The Filters section (price, beds, baths, square feet, amenities, no-fee, keywords, sort) applies to both modes.
  3. Set Max listings, and if needed Max pages and Fetch detail pages, then click Start.
  4. Download the dataset as JSON, CSV, or Excel, or read it through the API.

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 no-fee rentals:

{
"mode": "search",
"saleType": "forRent",
"locations": ["brooklyn", "queens"],
"maxPrice": 4000,
"noFee": true,
"minBedrooms": 1,
"sortBy": "LISTED_AT_DESC",
"maxListings": 100
}

URL shapes (URL mode):

ShapeExampleSupported
Rent, neighborhood + price filterhttps://streeteasy.com/for-rent/williamsburg/price:3000-5000yes
Sale, city + sorthttps://streeteasy.com/for-sale/nyc/sort:price_ascyes (price_asc, price_desc, listed_desc, listed_asc, sqft_desc, beds_desc)
Sale, borough + bedshttps://streeteasy.com/for-sale/manhattan/beds:2yes (beds:2, beds:1-3, beds:2-)
Plain search, ?page=N, in-contracthttps://streeteasy.com/for-sale/williamsburg, ...?page=2, https://streeteasy.com/in-contract/nycyes (each returned rows in a 2026-10-15 run on Apify Residential US; StreetEasy turns some individual requests away, so a request may be retried)
Building detail pagehttps://streeteasy.com/building/one-essex-crossingno, skipped with a warning ("building detail pages are not supported; paste a for-sale/for-rent/in-contract search URL instead")
Sold pagehttps://streeteasy.com/sold/williamsburgno, skipped with a warning ("sold pages are not supported")
Single-listing page (/rental/..., /sale/...)no, skipped with a warning (paste the search URL that contains the listing)
Agent profile page (/profile/...)no, skipped with a warning (agent name, phone, email, licence type, brokerage, profile link and office address ride on each listing row when Fetch detail pages is on)

Filters can be joined with | in one segment (price:3000-5000|beds:2). Unrecognized path segments are logged as a warning and ignored (results are not narrowed by them). A sort: segment in a pasted URL overrides the Sort order field; a beds: or price: segment overrides the matching Min/Max field bound by bound (a URL with only a minimum still takes your maximum from the field). A URL starting at ?page=N with N above 1 never saw pages 1 to N-1, so in incremental mode it never marks listings EXPIRED.

Behavior change: urls now defaults to the first three example URLs above (rent price, sale sort, sale beds). Running URL mode with the field left untouched scrapes those three searches, up to your Max listings, instead of nothing.

URL mode, multiple URLs:

{
"mode": "url",
"urls": [
"https://streeteasy.com/for-sale/williamsburg",
"https://streeteasy.com/for-rent/upper-east-side?page=2",
"https://streeteasy.com/for-sale/manhattan/price:900000-1500000"
],
"maxPages": 3,
"perPage": 200
}

Detail enrichment (full description, amenities, agent contacts, price history):

{
"mode": "search",
"saleType": "forSale",
"locations": ["dumbo"],
"fetchDetails": true,
"maxListings": 25
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/streeteasy-scraper").call(run_input={
"mode": "search",
"saleType": "forRent",
"locations": ["brooklyn"],
"maxListings": 50,
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
print(listing["address"], listing["price"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/streeteasy-scraper').call({
mode: 'search',
saleType: 'forRent',
locations: ['brooklyn'],
maxListings: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets, or webhooks from the Integrations tab.

Auto-expand for large searches

A search matching more than roughly 1,050 listings hits the site's own per-query cap. Turn on Auto-expand queries above 1050 results and the actor splits the search into price buckets, runs each one, and merges the results, so you still get the full market instead of just the first 1,050.

Resume and recurring updates

Two different things, both configurable on the input:

  • resumeFromRunId continues one specific interrupted run, or grows one big catalogue pull across separate runs. Paste a previous run ID or dataset ID and this run appends only listings not already saved there (matched by listing id).
  • incrementalMode is for running the same search again and again on a schedule and getting back only what changed. Turn it on and every emitted row carries changeType (NEW, UPDATED, REAPPEARED, EXPIRED, or UNCHANGED), changedFields, firstSeenAt, and lastSeenAt. By default only NEW, UPDATED, and REAPPEARED rows are returned and billed; turn on emitUnchanged or emitExpired to also get those (both billed). A listing is UPDATED when any compared field changes; these fields are kept in the output but never drive UPDATED: daysOnMarket, zipCode, state, mediaAssetCount, hasTour3d, hasVideos, isNewDevelopment, priceDelta, priceChangedAt, netEffectivePrice, monthsFree, leaseTerm, availableAt, buildingId, areaId, residentialUnitCount, roomCount, salesStartDate, statusDate, verifiedUpToDate, nearbySchools, and the agent profile keys (agentId, contactId, profileId, profileUrl, bio, isPro, headshotUrl, business address keys); an agent's name, phone, email, license and brokerage still count (use ignoreFieldsForChanges to add your own). stateKey names or shares the stored state; leaving it empty derives one automatically from the search scope. For example, incrementalMode: true with locations: ["park-slope"], saleType: "forSale", and stateKey: "park-slope-daily" tracks that one search across scheduled runs.

Output views

The dataset has four views, plus a run summary:

  • overview: the main listing columns, including changeType and the other incremental fields when incremental mode is on.
  • detail: description, amenities, building, price history, agents, open house dates, building ids, unit/room counts, status date, and nearby schools (filled when Fetch detail pages is on).
  • changes: id, url, address, price, changeType, changedFields, firstSeenAt, lastSeenAt, for incremental runs.
  • agents: one row per listing agent (name, phone, email, license, brokerage, profileUrl, isPro, bio, businessName, fullBusinessAddress), unwound from the listing; the full agent objects stay in the agents array of each row.
  • Run summary (key-value store record OUTPUT): totalEmitted, per-query status (complete, partial, or blocked), and an incomplete flag.

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.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to 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.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (build a query from filters) or url (paste search-result URLs).
saleTypestringforSaleforSale, forRent, or inContract. Search mode only. Past sales surface as priceHistory events on a listing found this way (with Fetch detail pages on), not as a separate queryable category.
locationsarray["nyc"]Borough/neighborhood slugs (e.g. manhattan, williamsburg, jersey-city) or numeric area ids. Search mode only.
urlsarraythree example URLs (see URL shapes)StreetEasy search-result URLs. URL mode only. A URL's own /price:LOW-HIGH, /beds:N (or beds:MIN-MAX) and /sort:KEY path segments are honored. Building/single-listing detail URLs and /sold/... URLs are not supported and are skipped with a warning.
propertyTypesarray[]CONDO, CO_OP, HOUSE, TOWNHOUSE, MULTI_FAMILY, RENTAL, CONDOP. Applies to both modes. RENTAL is accepted here but currently has no effect on results; use saleType: forRent to search rentals instead.
minPriceintegernoneLower bound on price (USD). For sales, total price; for rentals, monthly rent.
maxPriceintegernoneUpper bound on price (USD).
minBedroomsintegernoneUse 0 for studios.
maxBedroomsintegernoneUpper bound on bedroom count.
minBathroomsnumbernoneLower bound on bathroom count. Decimals supported (e.g. 1.5).
minSqftintegernoneLower bound on living area (square feet).
maxSqftintegernoneUpper bound on living area (square feet).
amenitiesarray[]Listings must include every selected amenity.
noFeebooleanfalseRentals only: filter to no-broker-fee listings. Ignored when saleType is not forRent.
keywordsstringnoneFree-text filter matched against the listing description/title.
sortBystringRECOMMENDEDRECOMMENDED, PRICE_ASC, PRICE_DESC, LISTED_AT_DESC, LISTED_AT_ASC, SQFT_DESC, BEDROOMS_DESC.
maxPagesinteger0Pagination cap per query. 0 walks every result page until the site's own end of results, bounded only by maxListings.
perPageinteger100Listings per page, 10 to 500. Higher values mean fewer round trips.
maxListingsinteger20Hard cap across the whole run. 0 means unlimited within budget. Raise it for production scrapes.
fetchDetailsbooleanfalseOpens each listing's detail page to add description, amenities, building info, agent contacts, and price history. One extra request per listing.
expandLargeQueriesbooleanfalseWhen a search matches more than roughly 1,050 listings, split it into price buckets and merge the results.
proxyobjectApify Residential, country USApify Residential proxy is required; datacenter ranges are turned away. Country defaults to US when not set.
resumeFromRunIdstringnonePrevious run ID or dataset ID to continue a catalogue pull across separate runs. Listings already saved there (matched by listing id) are skipped.
incrementalModebooleanfalseRecurring, scheduled monitoring of the same search. Off by default, so no existing run changes until you opt in.
stateKeystringnoneIncremental mode only. Names a monitoring campaign, or deliberately shares state across differently configured runs. Leave empty to derive one automatically from the search scope.
emitUnchangedbooleanfalseIncremental mode only. Also return listings unchanged since the last run, marked UNCHANGED. Returns and bills extra rows.
emitExpiredbooleanfalseIncremental mode only. Also return listings no longer found, marked EXPIRED, but only once a run has fully scanned the tracked search. Returns and bills extra rows.
ignoreFieldsForChangesarray[]Incremental mode only. Fields that never count as a change (kept in the output, excluded from UPDATED and changedFields). Use a top-level name or a dot path such as agents.phone; arrays apply to every element. Adding an entry does not cause a one-time wave of UPDATED rows.
mcpConnectorsarraynoneSend a condensed summary of each record to apps authorized under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
notionParentPageUrlstringnoneNotion connector only. Page under which item pages are created.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.

Output Example

Sample shape, values are illustrative placeholders, not from a live listing.

{
"id": "0000001",
"url": "https://streeteasy.com/building/sample-tower/4a",
"saleType": "forSale",
"status": "For sale",
"price": 1250000,
"neighborhood": "Sample Neighborhood",
"borough": null,
"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://images.streeteasy.com/photos/sample-uncropped.webp",
"images": ["https://images.streeteasy.com/photos/sample-uncropped.webp"],
"brokerage": "Sample Realty",
"listedAt": "2026-01-01",
"daysOnMarket": 30,
"offMarketAt": null,
"furnished": false,
"noFee": null,
"advertisedListing": false,
"description": "Full listing description text appears here when Fetch detail pages is on.",
"amenities": ["Doorman", "Elevator", "Gym"],
"buildingName": "Sample Tower",
"buildingYear": 1990,
"schoolDistricts": [],
"transit": ["6", "Q"],
"priceHistory": [{ "date": "2026-01-01", "event": "Listed", "price": 1250000 }],
"agents": [{ "name": "Agent Name", "phone": "+10000000000", "email": "agent@example.com", "license": "Licensed Real Estate Salesperson", "brokerage": "Sample Realty", "contactId": "100001", "profileId": "200001", "profileUrl": "https://streeteasy.com/200001", "isPro": false, "businessName": "Sample Realty LLC", "businessAddress1": "1 Example Avenue", "businessCity": "New York", "businessState": "NY", "businessZip": "10001", "fullBusinessAddress": "1 Example Avenue, New York, NY 10001" }],
"openHouseDates": ["2026-02-01"],
"virtualTourUrl": null,
"buildingId": "10001",
"areaId": 120,
"residentialUnitCount": 40,
"roomCount": 4,
"statusDate": "2026-01-01T09:00:00.000-05:00",
"verifiedUpToDate": false,
"salesStartDate": null,
"nearbySchools": [{ "id": "ps-1-manhattan", "name": "P.S. 001 Sample School", "district": "2", "grades": ["PK", "01", "02"] }],
"sourceUrl": null,
"zipCode": "10011",
"state": "NY",
"mediaAssetCount": 12,
"hasTour3d": false,
"hasVideos": false,
"isNewDevelopment": false,
"priceDelta": -25000,
"priceChangedAt": "2026-01-15T00:00:00.000Z",
"netEffectivePrice": null,
"monthsFree": null,
"leaseTerm": null,
"availableAt": null
}

borough is always a coordinate-based estimate, filled in whether or not Fetch detail pages is on (it is null only when the listing carries no usable coordinates). schoolDistricts is always [] (the school list is in nearbySchools), and virtualTourUrl is null unless the listing publishes a 3D tour. With incrementalMode on, every row also carries changeType (one of NEW, UPDATED, REAPPEARED, UNCHANGED, or EXPIRED), changedFields (an array of field names), firstSeenAt, and lastSeenAt; these four keys are absent entirely when incrementalMode is off.

Plan Requirement

This actor needs Apify's Residential proxy group, country US. StreetEasy turns away datacenter IP ranges with an "Access to this page has been denied" page (HTTP 403) in our measurements, so a datacenter proxy returns no listings. Even on Residential, some individual requests are turned away and the actor retries them; this varies by connection and by page, so a very large run can occasionally end partial.

Residential proxy availability depends on your Apify plan; Apify free plans do not include it. What you will see in the log and run status:

  • Plan without Residential: a warning block reads "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." When pages are served that way, the log says "Served over the backup connection (the default connection was unavailable)."
  • If the backup connection cannot load the pages either, the run ends FAILED with: "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested StreetEasy pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again."
  • If the proxy connection is refused for the account, the run ends FAILED with: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing."
  • If no results page could be loaded at all, the run ends FAILED with: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."
  • If a page is turned away after some listings were already collected, the log warns "Results are INCOMPLETE: page N of ... was turned away after X listing(s) had been collected, so the remaining pages were skipped." and the run summary marks that query partial with incomplete: true.

Pricing

Pay per event: a start event of $0.08 per run, $0.002 per listing returned, and $0.0008 per listing whose detail page was actually fetched (only when Fetch detail pages is on). The Pricing tab shows the current rates; use Max listings to cap a run. In incremental mode only the rows returned are billed.

FAQ

How much does it cost?

You pay per listing returned, plus an optional surcharge that applies only when Fetch detail pages is switched on, and only for listings that actually returned a detail page. Current rates: $0.08 per run start, $0.002 per listing, $0.0008 per detail page (see Pricing above). Use Max listings to cap the cost of any run.

This actor collects only publicly available listing data. You are responsible for how you use it: follow StreetEasy's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as price and square footage are generally data, but photos and descriptions may carry their own rights.

Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode for the search you want to monitor. Each row is classified by comparing a stable fingerprint of the listing's fields against the previous run's state, so a listing with no real content change is marked UNCHANGED and, by default, skipped and not billed; a genuine change is marked UPDATED with the changed fields listed.

Why is borough sometimes null?

borough is estimated from the listing's coordinates, with or without Fetch detail pages. It is null only when the listing has no usable coordinates or they fall outside the five boroughs plus Jersey City and Hoboken. neighborhood and address always come straight from the listing. If you track a search with Incremental mode and your saved state was taken before borough was always filled in, the first run after the upgrade treats a row whose only difference is borough going from empty to a value as UNCHANGED: it is not emitted and not billed.

Why did my run fail instead of returning an empty dataset?

The run only fails when the site turned away every request it made (search pages or the location lookup), which is a connection problem, not an empty search. Check the Proxy setting and try again. If your filters simply match nothing, the run finishes with an empty dataset and a message in the log. A run without Residential proxy continues on a backup connection (you will see the warning described under Plan Requirement) and ends FAILED only when no page could be loaded at all, with the message "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push a summary of each listing into Notion, Linear, or Airtable.

🔗 Want more real estate data?

Pair this actor with these related scrapers from the same team:

🏠 Zumper Scraper
Scrape Zumper.com rental listings: apartments, houses, condos and rooms. Pulls 60+ fields...
🧩 Padmapper Scraper
Scrape Padmapper apartment rentals across US/CA cities: rent, beds, baths, sqft...
🏠 Trulia Scraper
Scrape Trulia.com property listings by location, filters, or URL. Extract prices, beds...
🏠 Realtor.com
From $0.9/1K. Extract property listings from realtor.com. Get comprehensive data...
🏠 Rent.com Scraper
Extract rental listings from rent.com. Get comprehensive data including monthly rent...
🏠 Apartments.com Scraper
From $1.50/1K. Scrape Apartments.com rentals by city, ZIP, neighborhood, address or URL...

👉 Browse all abotapi scrapers

💬 Support & custom scrapers

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com or message Telegram @abotapi.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.