Stream EstateStream Estate
Guides

Filtering

Compose a criteria tree for POST /properties

The POST /properties body is built around a single criteria field — a recursive tree where every node takes exactly one of three shapes:

  • a Filters leaf (a flat bag of facet filters — the most common case);
  • an AND node { "and": [/* children */] } — all children must match;
  • an OR node { "or": [/* children */] } — at least one child must match.

Omit criteria entirely to match every property.

Body shape

{
  "criteria": { /* CriteriaNode */ },
  "paginationType": "PAGE",
  "page": 1,
  "size": 20
}
FieldTypeValue
criteriaCriteriaNode | nullA Filters leaf, an and node, or an or node.
paginationTypeenum"PAGE" (default) or "CURSOR". See Pagination.
pageintegerHonored only in PAGE mode. Default 1, minimum 1.
cursorstring | nullHonored only in CURSOR mode. Echo back the value from the previous response.
sizeinteger0–100. Default 10. With 0, the response carries no property, only their count: see Count without fetching properties.
sortobjectSort directive (field + direction).

The Filters leaf

A leaf groups filters by facet (all AND-ed together inside the leaf):

FacetDTOTargets
propertyPropertyFacetFiltersDTOtype, transaction, pricing, areas, locations, unit, building, amenities, body.
publisherPublisherFiltersDTOtype, mandate, references, hasEmail, hasPhone (all clauses apply to the same publisher).
listingsListingsFiltersDTOurl, sources, marketing, expiredAt, priceCoherent (all clauses apply to the same listing, except priceCoherent, which is judged at the property level).
datesDatesFiltersDTOcreatedAt, updatedAt.

For the full list of nested fields per facet (e.g. property.pricing.displayed, property.areas.displayed, property.unit.rooms, property.locations.in, …), see /api-reference — the reference is regenerated from the live spec on every build.

Default masks

Two populations are excluded from results until the matching filter is set:

  • Expired listings — hidden by default. Setting listings.expiredAt includes them (narrowed to the requested range if bounds are given).
  • Incoherent prices — properties whose listings all carry a price judged incoherent with the local market are hidden by default: listings.priceCoherent is implicitly true. Send false to return only those properties. Properties with an undetermined verdict stay visible either way.

Example — simple leaf

Flats for sale in Paris between €200k and €600k, 3–5 rooms, with a balcony:

curl -X POST "https://api-v2.stream.estate/properties" \
  -H "X-API-KEY: <your_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "criteria": {
      "property": {
        "type":      { "in": ["FLAT"] },
        "transaction": { "type": "SELL" },
        "pricing":   { "displayed": { "gte": 200000, "lte": 600000 } },
        "areas":     { "displayed": { "gte": 50 } },
        "unit":      {
          "rooms":    { "gte": 3, "lte": 5 },
          "features": { "in": ["BALCONY"] }
        },
        "locations": {
          "countryCode": "FR",
          "in": { "uniqueCodes": ["75056"] }
        }
      }
    },
    "paginationType": "PAGE",
    "page": 1,
    "size": 20
  }'

property.body.title and property.body.description filter on the listing text. Each takes an object with two lists:

{
  "criteria": {
    "property": {
      "body": {
        "description": {
          "in":  ["à rénover", "travaux"],
          "nin": ["bail commercial", "ehpad"]
        }
      }
    }
  },
  "size": 20
}
  • Every entry is a phrase: its words must appear side by side, in that order. "division possible" does not match a listing where the two words sit three paragraphs apart. A single word is a one-word phrase.
  • Case and accents are ignored: "A RENOVER" and "à rénover" are the same query.
  • in — the text must contain at least one of the phrases (an OR). To require two phrases at once, put one leaf per phrase inside an and node.
  • nin — the text must contain none of the phrases.
  • Up to 25 phrases per list. Both lists are optional.

A bare string ("description": "travaux") is refused with a 400. To get back what "division possible" used to return — either word — send { "in": ["division", "possible"] }; for the phrase, { "in": ["division possible"] }.

Energy certificate standard

property.unit.diagnostic.standard filters on the regulatory standard of the DPE: FR_DPE_2006 for a DPE established before 1 July 2021, FR_DPE_2021 from that date. Same clause triplet as unit.condition:

ClauseEffect
inStrict: the standard must be in the list. Properties whose standard is unknown are excluded.
ninThe standard must not be in the list. Properties whose standard is unknown pass.
inOrMissingThe standard is in the list or unknown: keeps properties whose source did not state the standard.

A value outside the list is refused with a 400 naming the accepted values. The criterion goes as is into an alert's criteria.

Properties rated A to C whose DPE follows the 2021 standard, or whose standard is not stated:

{
  "criteria": {
    "property": {
      "unit": {
        "diagnostic": {
          "standard": { "inOrMissing": ["FR_DPE_2021"] },
          "scores": [ { "type": "ENERGY_CONSUMPTION", "rating": ["A", "B", "C"] } ]
        }
      }
    }
  },
  "size": 20
}

AND / OR nodes

To combine several leaves, use and: or or: — each child is itself a CriteriaNode (so a leaf, another and, or another or). Each array holds between 2 and 10 child nodes, and nesting depth is not capped: to combine more than ten branches, nest another node.

{
  "criteria": {
    "or": [
      {
        "property": {
          "type": { "in": ["FLAT"] },
          "areas": { "displayed": { "gte": 60 } }
        }
      },
      {
        "property": {
          "type": { "in": ["HOUSE"] },
          "areas": { "displayed": { "gte": 100 }, "land": { "gte": 300 } }
        }
      }
    ]
  },
  "paginationType": "PAGE",
  "size": 20
}

How many children per node

An and or or array must hold at least 2 and at most 10 children. The rule is the same for both operators, and it applies to each node on its own. Outside those bounds the request is rejected with a 422:

Children in the arrayResult
0 or 1422 — or must contain at least 2 children (or and must contain at least 2 children)
2 to 10accepted
11 or more422 — or must contain at most 10 children (or and must contain at most 10 children)

A node with a single child has no effect anyway — use the child directly. To combine more than ten branches, nest: depth itself is not capped.

{
  "criteria": {
    "or": [
      { "or": [ /* 10 branches */ ] },
      { "or": [ /* 10 more branches */ ] }
    ]
  }
}

Response

In JSON:API format (default), properties are under data[], pagination under meta, and full listings / publishers under included:

{
  "data": [ /* Property resources (type, id, attributes, relationships) */ ],
  "meta": {
    "totalItems": 1847,
    "hasNextPage": true,
    "cursor": "eyJzb3J0Ijpb..."
  },
  "links": { "self": "/properties" },
  "included": [ /* Listing, Publisher, PropertyPublisher, Source */ ]
}
  • meta.totalItems — total match count. In PAGE mode, it is capped at 10,000.
  • meta.hasNextPage — true if more results exist.
  • meta.cursor — opaque, echo it back as-is in the next request in CURSOR mode. null in PAGE mode.

Each search result already contains the whole property: exactly the same content as GET /properties/{id} (property, publishers[], listings[]). There is no need to call GET /properties/{id} to complete a result; that call only serves to fetch a property whose id you already know.

Version 0.14.289Last updated

On this page