Stream EstateStream Estate

Migration v1 → v2

Migration guide from Stream Estate API v1 to v2

Breaking changes between v1 (docs.stream.estate) and v2 (https://api-v2.stream.estate).

This migration changes most URLs, the search format (querystring → structured body built around a criteria tree), renames Search to Alert, and introduces a real destination system (EventDestination). Plan for a client rewrite.

TL;DR

  • Base URL — api-v2.stream.estate
  • Auth — same X-API-KEY header, but a new key must be generated (v1 keys do not work on v2)
  • Search — GET /documents/properties?... → POST /properties with a JSON body built around a recursive criteria tree (Filters / and / or), pagination PAGE or CURSOR
  • Renaming — Search → Alert (+ dedicated AlertEvents)
  • Webhooks — full system via EventDestination + signingKey + EventNotification (delivery log)
  • Indicators — /cities replaced by /geo/administrative-divisions/{uuid} (UUID lookup only, no collection endpoint); a new set of estimation endpoints (price per m², POI, comparables) is on the roadmap to replace the old /indicators/*

Endpoint mapping

Properties

v1v2Note
GET /documents/properties (70+ query params)POST /properties (JSON body, criteria tree)See Filtering
GET /documents/properties/{id}GET /properties/{uuid}Shorter path
GET /documents/properties/{id}/similar-properties🆕 New estimation endpoints coming

Searches → Alerts

v1v2Note
GET /searchesGET /alerts
GET /searches/{id}GET /alerts/{uuid}
POST /searchesPOST /alertsBody reworked around criteria + notificationDestination
PUT /searches/{id}PUT /alerts/{uuid}
DELETE /searches/{id}DELETE /alerts/{uuid}
(N/A)GET /alerts/{alertUuid}/eventsMatch history
(N/A)GET /alerts/{alertUuid}/events/{uuid}Event detail

Webhooks & notifications

v1v2Note
Webhook URL set on the SearchEventDestination (GET/POST on the collection, GET/DELETE by UUID) + HMAC signingKeyReusable destinations across alerts (no update — recreate the destination instead)
GET /webhook-tester→ integrated into EventDestination + EventNotificationNo dedicated endpoint needed
Events ad.update.*, property.ad.*New types: NEW_MATCH, PRICE_CHANGE, OTHER_CHANGE, EXPIRED, REACTIVATEDSee Alert
(N/A)GET /event-notificationsFull delivery-attempt log
(N/A)Self-recovering circuit breakerA suspended destination reactivates on its own once the endpoint answers; can be forced from the console (see Notifications)

Cities / Geo

v1v2Note
GET /citiesGET /geo/administrative-divisions/{uuid} (UUID lookup)No collection endpoint for now
GET /public/location-autocompleteGET /geo/autocomplete

Indicators & misc

v1v2Note
GET /indicators/points_of_interest🆕 New estimation endpoints coming
GET /indicators/price_per_meter🆕 New estimation endpoints coming

New in v2

EndpointDescription
GET /account/api-usageBillable usage aggregates (totals, byApiKey, byRoute) over a startDate/endDate window
GET /analytics/listingsAggregated listing analytics
GET /favorite-properties, POST /favorite-properties, DELETE /favorite-properties/{uuid}Favorites (list + create on the collection, delete by UUID only)
GET /event-notifications[/{uuid}]Notification delivery log

Model changes

Property

In v1, PropertyDocument contains an inlined adverts[] with all details. In v2, POST /properties returns a structured PropertySearchResultDTO: a canonical view under property, deduplicated publishers, and per-listing variants:

{
  "id": "019dedb1-fdf4-714c-8392-5b9cfd63cde3",
  "property": {
    "createdAt": "2025-01-15T10:30:00+00:00",
    "updatedAt": "2025-06-15T12:00:00+00:00",
    "propertyType": "FLAT",
    "transaction": { "type": "SELL" /* + status, saleType, availableAt */ },
    "area":     { /* displayed, indoor, outdoor, land */ },
    "unit":     { /* rooms, bedrooms, floor, condition */ },
    "building": { /* constructionYear, floors, condition */ }
  },
  "publishers": [/* deduplicated PublisherSectionDTO[] */],
  "listings":   [/* ListingSectionDTO[]: url, source, publishedAt, lastSeenAt, and any variants that diverge from the canonical view */]
}

Canonical details (area, type, rooms, transaction, etc.) live under property. listings[] only carries variants specific to each ad (title, URL, publication dates, and any field that diverges from the canonical view).

Body reworked — a single criteria tree (no searchMode discriminator anymore) and a declarative destination via notificationDestination:

{
  "name": "3+ room flats in Paris",
  "active": true,
  "criteria": {
    "property": {
      "type": { "in": ["FLAT"] },
      "transaction": { "type": "SELL" },
      "unit": { "rooms": { "gte": 3 } },
      "locations": { "countryCode": "FR", "in": { "uniqueCodes": ["75056"] } }
    }
  },
  "notificationConfig": {
    "rules": [
      { "eventType": "NEW_MATCH" },
      { "eventType": "PRICE_CHANGE", "variationPercent": [{ "lte": -5 }] }
    ]
  },
  "notificationDestination": {
    "webhookUrl": "https://your.app/hook"
  }
}

The POST /alerts response returns a signingKey HMAC once (prefixed whsec_) — store it securely.

Text search (formerly expressions)

In v1, expressions[i][j] carried a word and two options, strict and includes. In v2, each text field takes an object { "in": [...], "nin": [...] } under criteria.property.body, where every entry is a phrase (words side by side, in order; case and accents ignored):

v1v2
word: "à rénover", strict: true (exact phrase)"description": { "in": ["à rénover"] }
word: "à rénover", strict: false (independent words)"description": { "in": ["à", "rénover"] } — a single word is a one-word phrase
includes: false (exclude)"description": { "nin": ["bail commercial"] }
expressions[0] OR expressions[1]an or node with one leaf per expression
expressions[0][0] AND expressions[0][1]an and node with one leaf per word or phrase

v2 targets one field (title or description) where v1 searched both: to keep that behaviour, put both fields inside an or node. See Filtering — Text search.

Response format

v1v2
Classic collectionJSON:API (application/vnd.api+json) by default — data[] + meta + included — or flat JSON via Accept: application/json (see Response format)
Query pagination ?page=NPOST /properties with paginationType: "PAGE" or "CURSOR" (see Pagination)

Migration checklist

  1. 🔑 Generate a new v2 API key at console.stream.estate/api-keys (v1 keys are incompatible) and send it in X-API-KEY
  2. 🔄 Replace GET /documents/properties?... with POST /properties and a JSON body built around a criteria tree (Filters / and / or)
  3. 🔄 Adapt pagination (choose PAGE or CURSOR via paginationType)
  4. 🔄 Rename Search* → Alert* in client code
  5. 🔄 Create explicit EventDestinations (POST /account/event-destinations, then GET/DELETE by UUID — no update, recreate the destination instead) rather than passing a webhook URL on each Search, or use notificationDestination.webhookUrl when creating the Alert
  6. 🔐 Store the signingKey returned by POST (alert or event-destination) and verify the HMAC signature on receipt
  7. 🔄 Migrate consumers of PropertyDocument.adverts[] to PropertySearchResult.listings[] (top-level in the response, alongside property and publishers)
  8. 🔄 Replace v1 event names with v2 enums (NEW_MATCH, PRICE_CHANGE, OTHER_CHANGE, EXPIRED, REACTIVATED)
  9. ⚠️ Temporarily remove calls to /indicators/* — a new estimation-focused suite of endpoints is on the way
  10. ✨ Optionally integrate /event-notifications (delivery log) and /analytics/listings
  11. 🔄 Convert each expressions[i][j] into property.body.<field>.in / nin — strict: true becomes a phrase, strict: false one word per entry, includes: false moves to nin (see Text search)
Version 0.14.289Last updated

On this page