Skip to content

Repository files navigation

Streetsweeper

Streetsweeper watches StreetEasy for new NYC rental listings and notifies you the moment one matches your search, through Pushover, ntfy, Telegram, or Discord. Give it a few neighborhoods, a price ceiling, and a bedroom count, run one command, and you get notified within a minute or two of a listing going live. It can also submit the broker inquiry for you, though that is off by default and covered below.

Not affiliated with, endorsed by, or sponsored by StreetEasy or Zillow. This is an independent, unofficial tool.

Setup

You need Docker and a Pushover account, or use ntfy, which is free and needs no account. Copy the example files, fill them in, and start it.

cp config.example.yaml config.yaml   # your searches
cp .env.example .env                  # your notification keys
docker compose up -d
docker compose logs -f

Edit config.yaml to change searches, then run docker compose restart.

Configuration

Everything lives in config.yaml, and secrets go in .env, referenced as ${VAR}. See config.example.yaml for the fully commented template. A minimal config looks like this.

poll_interval_seconds: 120
searches:
  - name: "West Village 1BRs"
    neighborhoods: ["West Village", "Greenwich Village"]
    max_price: 4000
    min_bedrooms: 1
    max_bedrooms: 1
    no_fee_only: false
notifications:
  - type: pushover
    user_key: ${PUSHOVER_USER_KEY}
    api_token: ${PUSHOVER_API_TOKEN}

Write neighborhoods by name. They resolve to StreetEasy area codes through streetsweeper/data/neighborhoods.json, which ships with a small verified set (West Village, Greenwich Village, SoHo, Tribeca). To add more, open DevTools on streeteasy.com, run a filtered search, find the api-v6.streeteasy.com request, and read the numeric code from its areas filter. Add it to the JSON, or put the raw integer code directly in a search's neighborhoods list. The script at scripts/fetch_areas.py can discover codes for you to review.

You can list one or more notification channels, and every alert goes to all of them. Pushover needs an app created at pushover.net. ntfy needs only a hard-to-guess topic that you subscribe to in its app. Telegram needs a bot token and a chat id. Discord needs an incoming webhook. Test whatever you configure with python -m streetsweeper --test-notify.

The primary data source is StreetEasy's own API. If it gets blocked, Streetsweeper can fall back to a paid RapidAPI backend and keep retrying the primary. Subscribe to a provider, put the key in .env as RAPIDAPI_KEY, and keep rapidapi_fallback on. The fallback is read only, so auto-outreach pauses while it is active.

StreetEasy blocks datacenter IPs, so running on a laptop or home server works out of the box, while a cloud server usually needs a residential proxy set as STREETSWEEPER_PROXY.

Auto-outreach

Streetsweeper can submit the broker inquiry automatically when a listing matches, so you are first to respond. This sends real inquiries to real brokers using your real contact information, it is a stronger violation of StreetEasy's terms than read-only polling, and brokers may notice automated inquiries, so use it only for your own genuine housing search and keep the cap low.

It is guarded on several levels. You opt in twice, with outreach.enabled and auto_outreach on the search. It runs in dry run by default, preparing and logging the inquiry without sending, and the form selectors in streetsweeper/outreach.py are best-effort, so verify them against a live listing before going live. Even with dry run off, nothing sends unless the environment variable STREETSWEEPER_OUTREACH_LIVE is also set, so a copied config can never send. The profile you fill in must be your own information, since it is submitted as-is. A daily cap limits successful sends, each listing is contacted at most once, every send notifies you, and it pauses on the RapidAPI fallback.

To enable it, fill in profile with your own details, set outreach.enabled, add auto_outreach to a search, watch the dry run notifications, verify the form, then set dry_run off and export STREETSWEEPER_OUTREACH_LIVE=yes.

Running without Docker

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
playwright install chromium
cp config.example.yaml config.yaml && cp .env.example .env
python -m streetsweeper

The command line supports --once for a single cycle, --seed to mark current inventory seen without notifying, --test-notify to send a test alert, --version, and -v for verbose logs.

How it works and troubleshooting

Each cycle fetches active rentals for your neighborhoods, keeps only genuinely new ones using a local SQLite record of what it has seen, and notifies you. On the first run it seeds silently, recording everything currently listed without notifying, so you only get alerts for listings that appear after you start. A background task keeps the StreetEasy session cookie fresh with a headless browser.

If you never get alerts, remember the first run is silent by design, and run with -v to watch poll activity. A message about missing the _se_t token means the browser could not get a session, so retry, and set a residential proxy if you are on a cloud host. An HTTP 403 means StreetEasy's anti-bot tripped, in which case it refreshes the cookie, fails over to RapidAPI if configured, and you should lower your poll rate. An unknown neighborhood means you need to add it to neighborhoods.json or use the raw code.

The original serverless version, built on AWS Lambda, is kept under deploy/aws-lambda for reference.

License

MIT, see LICENSE. The license covers this code only. It grants no right to access StreetEasy's data or API, which is governed by StreetEasy's terms, and how you use it is your responsibility.

About

Get notified when new NYC rentals hit StreetEasy.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages