Streaming and Real-Time Data
Borough offers two mechanisms for receiving real-time data beyond standard polling:
- Listing stream (Pro+) — SSE stream that delivers cached data immediately, then live data via Decodo
- Persistent watchers (Business+) — Durable Object-backed subscriptions that poll on a schedule and emit change events
Listing Stream (SSE)
Section titled “Listing Stream (SSE)”GET /v1/listing/{id} opens a Server-Sent Events stream for a single listing. The stream emits:
| Event | Description |
|---|---|
cached | Immediate response from D1 cache |
live | Fresh data fetched via Decodo Scraping API |
error | Live fetch or parse could not complete; the payload includes a machine-readable error code such as fetch_failed |
done | Stream complete |
curl -N -H "Authorization: Bearer BOROUGH-..." \ https://borough.qwady.app/v1/listing/4961849event: cacheddata: {"id":"4961849","price":3200,...}
event: livedata: {"id":"4961849","price":3150,...}
event: donedata: {}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
Section titled “Persistent Watchers”Watchers are long-lived subscriptions backed by Cloudflare Durable Objects. They support three watch types:
| Type | Monitors | Change Events |
|---|---|---|
listing | A single listing | Price changes, status changes |
building | A building’s listings | New listings added, listings removed |
search | A search query | New listings matching your filters |
Creating a watcher
Section titled “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
Section titled “Poll intervals”Each poll costs one API request against your monthly quota.
| Tier | Minimum | Default |
|---|---|---|
| Business | 300s (5 min) | 900s (15 min) |
| Internal | 60s (1 min) | 900s (15 min) |
Streaming watcher changes
Section titled “Streaming watcher changes”GET /v1/watchers/{id}/stream opens an SSE connection that emits change events:
| Event | Description |
|---|---|
connected | Stream opened, includes watcher metadata |
change | A change was detected (price drop, new listing, etc.) |
heartbeat | No changes detected this poll cycle |
reconnect | Stream duration limit reached — reconnect |
stopped | Watcher was deactivated |
error | Status check failed |
curl -N -H "Authorization: Bearer BOROUGH-..." \ https://borough.qwady.app/v1/watchers/abc-123/streamevent: connecteddata: {"watcherId":"abc-123","watchType":"listing","targetId":"4961849","pollInterval":600}
event: heartbeatdata: {"timestamp":"2026-02-19T12:00:00.000Z"}
event: changedata: {"eventType":"price_drop","listingId":"4961849","oldValue":"3200","newValue":"3150"}Managing watchers
Section titled “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
Section titled “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: reconnectdata: {"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
Section titled “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
Section titled “Choosing the Right Approach”| Scenario | Recommended |
|---|---|
| One-time fresh data for a listing | Business tier regular endpoint, or listing stream (SSE) for Pro |
| Ongoing monitoring of a specific listing | Watcher (listing type) |
| Alert when new units appear in a building | Watcher (building type) |
| Alert when new listings match search criteria | Watcher (search type) |
| Webhook delivery to your server | Webhook subscription + watcher |