A single-page shortlist of San Francisco studios and one-bedrooms still on the market, built to send to friends who know the city and can tell me which blocks are actually fine.
Live page: https://marsmsj.github.io/apt-hunting/
index.html — the whole thing. One self-contained file: no build step, no
dependencies, no framework. The only external request is a Google Fonts
stylesheet, and the page still reads fine if that fails to load.
Twenty-one listings, each with the rent, availability date, and whether it has parking, allows animals and has laundry. Every entry links out to the original listing and to the block on Google Maps. The list filters and sorts client-side.
Entries that have been reviewed by someone who lives in the city carry a from someone who lives here callout, in their words. Those notes are the point of the page — the listings are easy to gather, the neighbourhood read is not.
Settings → Pages → Source: Deploy from a branch, branch main, folder / (root).
It goes live at https://<username>.github.io/apt-hunting/ within a minute or two.
The listing data is a plain array called DATA near the top of the <script>
block at the bottom of index.html. Each entry looks like this:
{
r: 1, // sort order under "my order"
name: "947 Church St at 21st",
hood: "Noe Valley 94114 · studio, 500 sqft",
rent: 2500, // number: drives sorting and the under-$2,200 filter
when: "Available now",
d: 0, // days until available: drives sorting and the 1 Oct filter
park: 1, parkL: "Off-street parking", // 1 = yes, 0 = no, 2 = not stated
dog: 1, petL: "Cats + dogs",
laundry: 1, lauL: "W/D in unit",
note: "…", // my own read
localWho: "From Samson, who lives on Quintara", // optional; defaults to
// "From someone who lives here"
local: "…", // optional; renders the callout
url: "https://…", // the listing
map: "947 Church St, San Francisco, CA" // becomes a Google Maps link
}Delete a row when a listing dies. The counters at the top, the filter chips and the running tally all recalculate from the array, so nothing else needs touching. Rows that clear both parking and dogs are highlighted automatically.
- Contact phone numbers are deliberately not published here, since the page is public. They live in a private spreadsheet.
- The page carries a
noindextag so it doesn't turn up in search results. - Prices are base rent unless the entry says otherwise. Listings change fast — seven have died or been pulled in nine days.
- Dead listings, Tenderloin-adjacent addresses and two outright rental scams were filtered out before this list was built.
Last updated 1 September 2026.