Skip to main content
Borough offers two mechanisms for receiving real-time data beyond standard polling:
  1. Listing stream (Pro+) — SSE stream that delivers cached data immediately, then live data via Decodo
  2. Persistent watchers (Business+) — Durable Object-backed subscriptions that poll on a schedule and emit change events

Listing Stream (SSE)

GET /v1/listing/{id} opens a Server-Sent Events stream for a single listing. The stream emits:
EventDescription
cachedImmediate response from D1 cache
liveFresh data fetched via Decodo Scraping API
errorLive fetch or parse could not complete; the payload includes a machine-readable error code such as fetch_failed
doneStream complete
curl -N -H "Authorization: Bearer BOROUGH-..." \
  https://borough.qwady.app/v1/listing/4961849
event: cached
data: {"id":"4961849","price":3200,...}

event: live
data: {"id":"4961849","price":3150,...}

event: done
data: {}
The cached event arrives immediately. The live event may take 2-5 seconds depending on upstream response time. If the live fetch fails, an error event is emitted with an error code and message, and the stream closes. This is a short-lived stream — it completes after delivering cached + live data (typically under 10 seconds). Tier requirement: Pro, Business, or Internal.

Persistent Watchers

Watchers are long-lived subscriptions backed by Cloudflare Durable Objects. They support three watch types:
TypeMonitorsChange Events
listingA single listingPrice changes, status changes
buildingA building’s listingsNew listings added, listings removed
searchA search queryNew listings matching your filters

Creating a watcher

curl -X POST https://borough.qwady.app/v1/watchers \
  -H "Authorization: Bearer BOROUGH-..." \
  -H "Content-Type: application/json" \
  -d '{"watchType":"listing","listingId":"4961849","pollInterval":600}'

Poll intervals

Each poll costs one API request against your monthly quota.
TierMinimumDefault
Business300s (5 min)900s (15 min)
Internal60s (1 min)900s (15 min)

Streaming watcher changes

GET /v1/watchers/{id}/stream opens an SSE connection that emits change events:
EventDescription
connectedStream opened, includes watcher metadata
changeA change was detected (price drop, new listing, etc.)
heartbeatNo changes detected this poll cycle
reconnectStream duration limit reached — reconnect
stoppedWatcher was deactivated
errorStatus check failed
curl -N -H "Authorization: Bearer BOROUGH-..." \
  https://borough.qwady.app/v1/watchers/abc-123/stream
event: connected
data: {"watcherId":"abc-123","watchType":"listing","targetId":"4961849","pollInterval":600}

event: heartbeat
data: {"timestamp":"2026-02-19T12:00:00.000Z"}

event: change
data: {"eventType":"price_drop","listingId":"4961849","oldValue":"3200","newValue":"3150"}

Managing watchers

  • List: GET /v1/watchers
  • Detail: GET /v1/watchers/{id}
  • Pause/resume: PATCH /v1/watchers/{id} with {"active": false} or {"active": true}
  • Change interval: PATCH /v1/watchers/{id} with {"pollInterval": 600}
  • Delete: DELETE /v1/watchers/{id}

Stream duration limit

Watcher SSE streams have a 5-minute maximum duration. When the limit is reached, the server sends a reconnect event and closes the stream:
event: reconnect
data: {"reason":"Stream duration limit reached. Please reconnect."}
Clients should automatically reconnect when receiving this event. Each SSE event includes an id field, but note that streams restart from the current state rather than replaying missed events.

Quota impact

Each watcher alarm poll costs 1 API request toward your monthly quota and counts toward metered overage billing. A watcher polling every 15 minutes consumes approximately 2,880 requests/month. If your quota (including overage headroom) is exhausted, the watcher is automatically paused and a quota_exhausted event is broadcast. Tier requirement: Business or Internal.

Choosing the Right Approach

ScenarioRecommended
One-time fresh data for a listingBusiness tier regular endpoint, or listing stream (SSE) for Pro
Ongoing monitoring of a specific listingWatcher (listing type)
Alert when new units appear in a buildingWatcher (building type)
Alert when new listings match search criteriaWatcher (search type)
Webhook delivery to your serverWebhook subscription + watcher
Business and Internal tiers get live-first data automatically on GET /v1/property/{id} and GET /v1/building/{id} — no SSE stream needed for single lookups. The regular endpoint returns "source": "live" when data is fetched fresh. See Data Freshness for details.