# Search properties

> **Casafari is the AI agent-native real estate data intelligence platform.** The most complete property index in Europe: a deduplicated, cleaned property graph of residential and commercial property, for sale and for rent, in 16 countries. Every property is one record with its full price and market history. [How the property graph is built](/docs/property-graph).

`POST https://platform.casafari.com/v1/property-sourcing/searches`

[Property Sourcing](/docs/property-sourcing) · [REST API](/docs/rest/property-sourcing). Send `Authorization: Bearer $CASAFARI_API_KEY`: a platform API key or OAuth access token; see [platform sign-in](/docs/rest#platform-api-keys). Costs 100 units per page (1 unit = EUR 0.04).

Over MCP: none; this product is REST only.

## Description

Structured search of the property graph: one request body for every country. Name the places with `location_ids` (find them with `GET /v1/locations/search`), then filter by property type, price, price per m², bedrooms, bathrooms, living and plot area and features, and sort. Each result is one property, however many listings advertise it, with an opaque `prop_` id. `next` is a URL for the following page (valid for one hour, bound to your account) or `null`. Plain-language phrases go to `POST /v1/property-sourcing/searches/describe`. 100 units per page.

## Request body

Content type `application/json`. Required. Type: `object`.

- `location_ids` (integer[], required): 1 to 100 location ids, from GET /v1/locations/search?query=<place>. 1–100 items.
- `country` (string, optional): ISO 3166-1 alpha-2 code, e.g. "ES". Needed when the location ids do not include the country's own id and the account covers more than one country.
- `operation` (string, optional): For sale or for rent. Values: `sale`, `rent`. default "sale".
- `types` (string[], optional): Property type names in lower case, e.g. "apartment", "house", "villa". at most 40 items.
- `price` (object, optional): Asking price range.
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `price_per_sqm` (object, optional): Asking price per m² range.
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `bedrooms` (object, optional): Bedrooms, 0 to 100.
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `bathrooms` (object, optional): Bathrooms, 1 to 100.
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `living_area_sqm` (object, optional): Living area in m².
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `plot_area_sqm` (object, optional): Plot area in m².
  - `min` (integer, optional): Lowest value, inclusive.
  - `max` (integer, optional): Highest value, inclusive.
- `features` (string[], optional): Features every result must have. Values: `balcony`, `elevator`, `garage`, `garden`, `parking`, `storage`, `swimming_pool`, `terrace`, `rental_license`, `furniture`, `no_elevator`, `rented_out`, `life_annuity`, `business_transfer`, `smoke_outlet`.
- `exclude_features` (string[], optional): Features no result may have. Values: `balcony`, `elevator`, `garage`, `garden`, `parking`, `storage`, `swimming_pool`, `terrace`, `rental_license`, `furniture`, `no_elevator`, `rented_out`, `life_annuity`, `business_transfer`, `smoke_outlet`.
- `sort` (string, optional): Order of the results. Values: `newest`, `oldest`, `recently_updated`, `price_asc`, `price_desc`, `price_per_sqm_asc`, `price_per_sqm_desc`, `largest`.
- `page_size` (integer, optional): Results per page: up to 100 (25 on Free). 1–100; default 25.

## Example request

Placeholders only: replace the token and the values with your own.

```bash
curl -X POST "https://platform.casafari.com/v1/property-sourcing/searches" \
  -H "Authorization: Bearer $CASAFARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "location_ids": [
    12345
  ],
  "country": "ES",
  "operation": "sale",
  "types": [
    "apartment"
  ],
  "price": {
    "max": 450000
  },
  "bedrooms": {
    "min": 2
  },
  "features": [
    "terrace",
    "elevator"
  ],
  "sort": "newest",
  "page_size": 25
}'
```

## Responses

### 200 One page of properties.

Type: `object`.

- `results` (object[], optional)
  - `id` (string, optional): Opaque property id, `prop_...`.
  - `type` (string, optional, nullable): Property type.
  - `operation` (string, optional) Values: `sale`, `rent`.
  - `price` (object, optional)
    - `amount` (number, optional, nullable): Asking price; null when the price is on request.
    - `currency` (string, optional, nullable): ISO 4217 currency code.
    - `per_sqm` (number, optional, nullable): Asking price per m².
  - `bedrooms` (number, optional, nullable)
  - `living_area_sqm` (number, optional, nullable)
  - `location` (object, optional)
    - `id` (integer, optional, nullable): Location id.
    - `name` (string, optional, nullable): Location name.
    - `coordinates` (object, optional, nullable): Latitude and longitude.
      - `lat` (number, optional)
      - `lon` (number, optional)
  - `first_seen` (string, optional, nullable): When the property came on the market for this operation.
  - `listings_count` (integer, optional, nullable): How many listings advertise it; known on a property read with expand=listings, null in search results.
  - `matched_tags` (string[], optional, nullable): The phrases of a plain-language search this property matched; null for structured searches.
- `total` (integer, optional, nullable): How many properties match, when known.
- `next` (string, optional, nullable): URL of the next page (valid one hour), or null.

Example with sample values (ids and figures are illustrative):

```json
{
  "results": [
    {
      "id": "prop_7Hq2pZk1vY0sW4nB8cR3tL",
      "type": "apartment",
      "operation": "sale",
      "price": {
        "amount": 425000,
        "currency": "EUR",
        "per_sqm": 4474
      },
      "bedrooms": 3,
      "living_area_sqm": 95,
      "location": {
        "id": 12345,
        "name": "Sample district",
        "coordinates": {
          "lat": 39.462,
          "lon": -0.376
        }
      },
      "first_seen": "2026-09-28",
      "listings_count": null,
      "matched_tags": null
    }
  ],
  "total": 312,
  "next": "https://platform.casafari.com/v1/property-sourcing/searches/cur_SAMPLE"
}
```

### 400 Invalid request: the problem names each field to fix (`invalid_request`), or `page_size` is over the plan's limit (`page_depth_exceeded`).

Type: `object`.

- `type` (string, optional): The problem's documentation URL.
- `title` (string, optional)
- `status` (integer, optional)
- `detail` (string, optional): What happened and how to fix it.
- `instance` (string, optional)
- `code` (string, optional): e.g. invalid_request, payment_required, not_entitled, not_found, page_depth_exceeded, request_limit_reached.
- `request_id` (string, optional)
- `errors` (object[], optional): One entry per field to fix.
  - `field` (string, optional)
  - `message` (string, optional)

### 401 Missing or unknown API key or token.

Type: `object`.

- `type` (string, optional): The problem's documentation URL.
- `title` (string, optional)
- `status` (integer, optional)
- `detail` (string, optional): What happened and how to fix it.
- `instance` (string, optional)
- `code` (string, optional): e.g. invalid_request, payment_required, not_entitled, not_found, page_depth_exceeded, request_limit_reached.
- `request_id` (string, optional)
- `errors` (object[], optional): One entry per field to fix.
  - `field` (string, optional)
  - `message` (string, optional)

### 402 No units left to fund the call (`payment_required`, with `reason` and an `upgrade_url`).

Type: `object`.

- `type` (string, optional): The problem's documentation URL.
- `title` (string, optional)
- `status` (integer, optional)
- `detail` (string, optional): What happened and how to fix it.
- `instance` (string, optional)
- `code` (string, optional): e.g. invalid_request, payment_required, not_entitled, not_found, page_depth_exceeded, request_limit_reached.
- `request_id` (string, optional)
- `errors` (object[], optional): One entry per field to fix.
  - `field` (string, optional)
  - `message` (string, optional)

### 403 The account is not entitled to this product or country (`not_entitled`).

Type: `object`.

- `type` (string, optional): The problem's documentation URL.
- `title` (string, optional)
- `status` (integer, optional)
- `detail` (string, optional): What happened and how to fix it.
- `instance` (string, optional)
- `code` (string, optional): e.g. invalid_request, payment_required, not_entitled, not_found, page_depth_exceeded, request_limit_reached.
- `request_id` (string, optional)
- `errors` (object[], optional): One entry per field to fix.
  - `field` (string, optional)
  - `message` (string, optional)

### 429 A request limit of the plan was reached (`request_limit_reached`, with `window` and `Retry-After`).

Type: `object`.

- `type` (string, optional): The problem's documentation URL.
- `title` (string, optional)
- `status` (integer, optional)
- `detail` (string, optional): What happened and how to fix it.
- `instance` (string, optional)
- `code` (string, optional): e.g. invalid_request, payment_required, not_entitled, not_found, page_depth_exceeded, request_limit_reached.
- `request_id` (string, optional)
- `errors` (object[], optional): One entry per field to fix.
  - `field` (string, optional)
  - `message` (string, optional)

## Related pages

- [Search properties by description (POST /v1/property-sourcing/searches/describe)](/docs/rest/property-sourcing/search-properties-by-description)
- [Next page of a search (GET /v1/property-sourcing/searches/{cursor})](/docs/rest/property-sourcing/next-page-of-a-search)
- [Get a property (GET /v1/property-sourcing/properties/{property_id})](/docs/rest/property-sourcing/get-a-property)
- [Resolve a link or reference to its property (POST /v1/property-sourcing/properties/resolve)](/docs/rest/property-sourcing/resolve-a-link-or-reference-to-its-property)
- [Property Sourcing REST API: all operations](/docs/rest/property-sourcing)
- [How to get a bearer token for the Casafari REST API](/docs/rest)
- [MCP vs REST API compared](/docs/parity)
