Platform
Get started

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.The most complete property index in Europe: a deduplicated, cleaned property graph.

How the graph is built

Screen investments · Use case

How do I find properties priced below the area's market value?

To find properties priced below an area's market value, benchmark the area's asking price per m² with Area Insights (ma_get_time_series_data, ma_get_price_distribution). Then search Property Sourcing with POST /api/v2/properties/search and price_per_sqm_to under that benchmark. Confirm each candidate with comps_get-comparables by comparing its asking price with estimated_prices.fair_market_price. The biggest spread marks the strongest candidate.

Products: Area Insights, References, Property Sourcing, Comparables & Valuation

Short answer

Work as a three-step funnel:

  1. Benchmark. Get the area's average asking price per m² from Area Insights.
  2. Search. Look in the property graph for properties asking less than that per m², with the type, bedrooms and condition you want.
  3. Confirm. Run Comparables & Valuation on each candidate. Compare its asking price with estimated_prices.fair_market_price from comps_get-comparables, and check whether it is flagged is_outlier.

The graph keeps one record per property. A property advertised by several agencies and portals counts once in the benchmark and appears once in the search. See The property graph.

Steps

Benchmark: ma_get_time_series_data with avg_price_psqm (latest period) and ma_get_price_distribution

  1. Resolve the place name with ma_get_location_typeahead. The location_name must be in English, for example Lisbon. When several places share a name, pick the right location_id from breadcrumbs and administrative_level.

  2. Call ma_get_time_series_data with these request_data fields:

    • type_group: apartment or house
    • business_type: sale
    • alias: a name for the segment
    • custom_location_boundary: location_ids (1 to 10 ids) or a circle (distance in meters, 50 to 50000)
    • data_point: avg_price_psqm
    • date_interval: for example month
    • date_range.min

    Add bedrooms_range and conditions to match the segment you will search. The value of the latest date_start is your benchmark.

  3. Call ma_get_price_distribution with the same boundary and filters. Each bucket returns lower_bound, upper_bound, properties_count and is_mean. Together they show how total prices spread and which bucket holds the mean, so you can tell whether the cheap end of the area is thin or crowded.

Search: POST /api/v2/properties/search with location_ids and price_per_sqm_to below the benchmark, plus type, bedrooms and condition

  1. Call POST /api/v2/properties/search with:

    • search_operations: sale
    • location_ids: documented as the ids returned by POST /api/v1/references/locations, so resolve them there
    • price_per_sqm_to: below the benchmark, for example benchmark × 0.85 for 15% under
    • types
    • bedrooms_from and bedrooms_to
    • conditions

    Leave ruin out of conditions so run-down properties do not look like bargains. Other useful filters are total_area_from/total_area_to, days_on_market_from/days_on_market_to and private. The response schema is on the operation page.

Confirm: comps_get-comparables on each candidate with exclude_outliers false; compare asking price with fair_market_price and look at is_outlier

  1. For each candidate, call comps_get-comparables with:
    • location_boundary.circle.target_point: the candidate's coordinates or address
    • distance: in kilometers. The tool suggests 0.5 to 1 km in urban areas and 2 to 5 km in rural ones.
    • operation: sale
    • comparables_types: types from one type group only
    • comparables_count: 1 to 50
    • the candidate's bedrooms, condition, floors and total_area
    • exclude_outliers: false
  2. Read estimated_prices.fair_market_price, fair_market_price_per_sqm, fast_sell_price and out_of_market_price. Then find the candidate in results by property_id and read its is_outlier and similarity_score.

Sort by the spread

  1. For each candidate, compute spread = (fair_market_price − asking price) / fair_market_price, and sort from largest to smallest.
    • Take the asking price from the search response, or from sale_price if the candidate appears in the comparables results.
    • Keep casafari_link (the Comparative Market Analysis page) and valuation_id (persistent, expires in a year) for your shortlist.

Example: apartments in Lisbon under the area price per m²

A person asks their assistant: "Find 2 to 3-bedroom apartments in Lisbon asking at least 15% under the area price per m², and leave out ruins."

The assistant calls list_servers, then ma_get_location_typeahead with Lisbon. It then calls ma_get_time_series_data with type_group apartment, business_type sale, data_point avg_price_psqm, date_interval month and bedrooms_range 2 to 3. Call the latest value B. The threshold is T = 0.85 × B. ma_get_price_distribution on the same boundary shows how many properties sit in each price bucket.

The search step runs over REST, from your own integration with a Casafari client account:

json
{
  "search_operations": ["sale"],
  "location_ids": ["<LOCATION_ID>"],
  "types": ["apartment"],
  "price_per_sqm_to": "<T>",
  "bedrooms_from": 2,
  "bedrooms_to": 3,
  "conditions": ["used", "very-good", "new"]
}

Replace the placeholders with numbers, and check value formats on the operation page. For each candidate, the assistant then calls comps_get-comparables:

json
{
  "comparables_count": 20,
  "location_boundary": {"circle": {"target_point": {"address": "Avenida da Liberdade, Lisbon"}, "distance": 1}},
  "operation": "sale",
  "comparables_types": ["apartment"],
  "bedrooms": 2,
  "exclude_outliers": false
}

If a candidate asks P and fair_market_price is F, its spread is (F − P) / F. The assistant ranks candidates by that spread and reports casafari_link for each. All figures come from the responses; this page gives no market numbers.

How to read the result

Outliers are excluded by default: turn exclude_outliers off on purpose

exclude_outliers defaults to true in comps_get-comparables and in the Area Insights tools. It removes properties that are significantly underpriced or overpriced compared to similar ones, which are exactly the properties you are hunting.

  • Benchmark: keep it on, so extreme values do not pull the average.
  • Comparables call: turn it off, so the candidate stays visible.

A candidate with is_outlier true is priced far from similar properties. That makes it either a real bargain or a case with a reason behind the price, so verify it before acting.

A low price may reflect condition, floor or ruin status: read the fields

Before calling a property underpriced, read these fields:

  • condition (ruin, used, very-good, new, other)
  • floor_number and features.floor (no_floor, ground, middle, top)
  • features.orientation
  • energy_rating
  • total_area against living_area
  • features.characteristics: values such as rented_out, life_annuity or no_elevator can explain a lower asking price that is not a discount to market value

total_sale_price_change and last_sale_price_reduction show whether the price got there through cuts.

Limits

The search step needs a Casafari client account

What each step needs:

  • MCP access: the Area Insights tools and comps_get-comparables are available to Casafari clients with MCP access, using their Casafari account.
  • Casafari client accounts: POST /api/v2/properties/search is a REST operation for Casafari client accounts. It is not offered over Casafari MCP (details).
  • Self-serve access for new users is coming soon, for these tools and for the search.

Without it, start from a property you already know (an address, coordinates or, in Spain only, a cadastral reference) and use comparables (example).

Coverage limits:

  • The API's permissions list 16 markets.
  • The cadastral-reference input of comps_get-comparables is Spain only.
  • ma_get_location_typeahead expects English place names.
  • The Area Insights tools take type_group apartment or house only.
  • Other tools name no market.

The sources give no figures for pricing, quotas, rate limits or data refresh frequency; ask Casafari.

The spread is computed by you from the returned fields

No tool returns a discount or spread field: you compute it from the asking price and estimated_prices. The estimates are calculated from the comparables found, so they depend on the boundary and filters you set.

The data cannot tell you a property's interior state beyond condition, its legal situation, or why the seller asks what they ask. Inspect before you buy.

Questions

Can an AI assistant run this whole funnel for me?

An AI assistant connected to Casafari MCP can run the benchmark and the confirmation steps itself, with ma_get_location_typeahead, ma_get_time_series_data, ma_get_price_distribution and comps_get-comparables. The search step is POST /api/v2/properties/search, which is REST only and not offered over Casafari MCP. Run it from your own integration with a Casafari client account, or give the assistant candidate addresses to check.

How do I add price cuts as a second signal?

Each comps_get-comparables result carries price-cut fields: total_sale_price_change, last_sale_price_reduction and sale_price_last_change. At area level, ma_get_time_series_data with data_point price_down counts price decreases per period. Casafari client accounts can read a property's sale_price_history with GET /api/v1/properties/search/{property_id}. New Market Leads alert feeds follow price cuts as they happen.

Can I do this for houses and plots?

You can do this for houses throughout, because the Area Insights tools accept type_group house and comps_get-comparables takes house types such as villa or townhouse. Plots are only partly supported. The Area Insights MCP tools take apartment or house only, so there is no plot benchmark there. comps_get-comparables accepts urban_plot and rural_plot, but then requires plot_area unless you give a cadastral reference.

Does fair_market_price account for condition and floor?

fair_market_price is calculated from the comparables found, so it reflects condition and floor as far as your request filters the comparable set by them. Pass condition, floors or floor_number, and characteristics in the comps_get-comparables call to get comparables that match. The docs do not describe how the estimate weights each attribute, so read similarity_score on each result.

All use cases

Tip: add .md to any URL to read it as Markdown.