[Brokerage Name] · Listings
A brokerage listings site. MLS feed in, individual listing pages out, agent contact form on every listing, saved searches via email.
Source of truth
The MLS is the source of truth for listings. A scheduled job pulls the RETS/RESO Web API feed every 15 minutes and writes to Postgres. Listing pages are statically regenerated from the DB. Agent profiles are CMS-managed (Sanity).
Tech stack
Next.js 15 (App Router) + React + Tailwind v4. Postgres (Vercel Postgres) for listings + saved-search subscribers. Sanity for agent profiles + brokerage content. Mapbox for the listing map. Cloudinary for listing photo optimization. Resend for the daily saved-search emails. Vercel cron for the MLS pull.
Deploy
- Site:
git push-> Vercel auto-deploys - MLS sync: Vercel cron hits
/api/cron/mls-syncevery 15 minutes - Saved search emails: daily cron hits
/api/cron/saved-search-digest
File map
app/page.tsxhome: featured listings, search bar, agent gridapp/listings/page.tsxsearch results with filtersapp/listings/[mlsId]/page.tsxindividual listingapp/agents/[slug]/page.tsxagent profile + their active listingsapp/saved-search/page.tsxuser manages their saved searchesapp/api/cron/mls-sync/route.tspull feed, upsert to Postgresapp/api/cron/saved-search-digest/route.tsdaily emailapp/api/lead/route.tslead form: assigns to the listing's agentlib/mls.tsRESO Web API client (auth, pagination, parsing)lib/round-robin.tslead routing when no agent attached
.env keys
DATABASE_URLMLS_API_BASE_URL,MLS_API_USERNAME,MLS_API_PASSWORD(RESO Web API)SANITY_PROJECT_ID,SANITY_DATASET,SANITY_TOKENCLOUDINARY_CLOUD_NAME,CLOUDINARY_API_KEY,CLOUDINARY_API_SECRETRESEND_API_KEY,RESEND_FROM_EMAILMAPBOX_ACCESS_TOKENCRON_SECRETVercel cron auth
Hard rules
- MLS feed terms have rules. Listing photos can only be served from MLS-approved URLs (Cloudinary fetch + cache is usually fine; cold-stored copies are usually not). Read the feed agreement.
- Hide "off market", "expired", "pending" or "sold" from the public site unless explicitly opted in.
- Display the agent name AND brokerage name on every listing (MLS attribution rule).
- Lead form notifies the listing agent within 30 seconds. Real estate leads decay FAST.
- Saved searches send a daily digest, not realtime. Daily is the sweet spot for engagement.
- Comply with IDX rules for your MLS. Each MLS has slightly different display requirements.
Recent significant changes
- 2026-05-02: Scaffolded. Locked: RESO Web API over RETS (RESO is the modern path), Sanity for agent CMS (non-dev edits), saved searches over realtime alerts (better retention).
Next session: start here
- Get MLS credentials. Most MLSs require a brokerage license + signed agreement.
- Set up Postgres. Apply schema for
listings,agents,saved_searches. - Run the MLS sync once manually. Confirm 50-200 listings ingest cleanly.
- Build the listing detail page. Match an agent to it. Submit a real lead.
- Confirm the agent receives the lead email within 30 seconds.
- Set up Vercel cron, confirm it fires on schedule.