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

Claro AI · FAQ

Claro API and MCP: developer FAQ

Answers to what developers, product managers and AI agent builders ask about Claro AI. Every answer links the page that documents it.

What is the base URL of the Claro API, and where is the OpenAPI description?

Claro's REST base URL is https://platform.casafari.com/v1/claro and its MCP endpoint is https://platform.casafari.com/mcp/claro, both live; Claro's own OpenAPI description, readable without a key, is at https://expansion.casafari.com/v1/openapi.json.

Claro, Casafari's business expansion search agent, searches commercial premises across Spain (product page). Casafari is the AI agent-native real estate data intelligence platform. Every Claro REST path on the platform is Claro's own path with /claro after /v1:

  • POST /v1/search is POST /v1/claro/search (search)
  • GET /v1/premises/{id} is GET /v1/claro/premises/{id} (get one premises)
  • PUT /v1/stores is PUT /v1/claro/stores (stores)

Claro's own definition files use Claro's own paths without /claro: the OpenAPI description, the tool list at https://expansion.casafari.com/v1/tools and the status at https://expansion.casafari.com/v1/health. A client generated from the OpenAPI description needs the platform base URL and /claro after /v1 in each path. The app at https://expansion.casafari.com is for managers.

bash
curl -X POST https://platform.casafari.com/v1/claro/search \
  -H "Authorization: Bearer $CASAFARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"brief": "burger restaurant, corner unit 120-250 m², smoke outlet, busy street, Madrid, rent under 8,000 a month", "check": false}'

Client setup: connect.

How do I get access to the Claro API and the Claro MCP?

Create a platform API key in your account on https://platform.casafari.com, like every other Casafari API and MCP key, or sign in with the platform's OAuth; Claro is a platform product, so you never hold a Claro pk_ key.

Platform API keys start with cf_sk_; you create them in the console at https://platform.casafari.com/console and revoke them in your account (platform API keys, OAuth). The platform terms at /legal/platform-terms apply. Keep the key server side: never in a browser, a URL or a repository. Account-level settings of Claro (its own limits, location data access) are set on the organisation's Claro account behind the platform. Calls are metered in platform units; per-call unit prices for Claro's tools and operations are not published yet (limits). Client configuration: connect.

Which header carries the key, and does Claro need its own `pk_` key?

Send the platform API key or OAuth access token as Authorization: Bearer $CASAFARI_API_KEY on every REST call and every MCP request; no Claro pk_ key is involved, because the platform calls Claro with the organisation's own Claro account and never shows that key.

The platform also accepts the key as X-API-Key. It checks the key or token and the Claro scope, meters the call and forwards it unchanged to Claro. A missing or invalid platform key or token is a 401 from the platform, in RFC 9457 problem+json, not Claro's {"error":{"code":"...","message":"..."}} body. A bearer token from the Casafari REST API (POST /login on api.casafari.com) does not work on Claro.

http
GET https://platform.casafari.com/v1/claro/premises/{id}
Authorization: Bearer $CASAFARI_API_KEY

See connect and platform API keys.

How does a call travel through the platform to Claro, what passes through unchanged, and what is the direct route?

The platform checks the key or token and the Claro scope, meters the call in platform units and forwards it unchanged to Claro; the Claro-Version header and Claro's own error bodies come back unchanged.

The routes are live: REST under https://platform.casafari.com/v1/claro, MCP at https://platform.casafari.com/mcp/claro. What the platform refuses before Claro is answered in its own format, RFC 9457 problem+json: 401 for a missing or invalid platform key or token, 402 payment_required with an upgrade_url, 429 request_limit_reached naming the window (limits). Everything that reaches Claro is answered by Claro: counts, results, Claro-Version, and {"error":{"code":"...","message":"..."}} on a refusal. Per-call unit prices for Claro's tools and operations are not published yet. The direct route, Claro's own host https://expansion.casafari.com with https://expansion.casafari.com/v1 and https://expansion.casafari.com/mcp and a direct pk_ key, remains only for Casafari's own products and for pilot customers who already hold a key; it is not the public route. Tools and operations on both doors: parity.

What transport does the Claro MCP use, and do I have to keep a session?

The Claro MCP at https://platform.casafari.com/mcp/claro uses Streamable HTTP with JSON responses and is stateless: there is no session to keep, and every request carries the platform key or token.

It serves tools only, no resources and no prompts. serverInfo.name is casafari-claro, the title is "Claro AI by Casafari", and serverInfo.version is the Claro-Version date (2026-10-15 at the snapshot). The 7 tools, with no prefix, are search_premises, refine_search, get_premises, compare_premises, market_snapshot, explain_location and brief_from_file. Carry search_id and next_cursor between calls as ordinary tool arguments. A refusal arrives as a tool error with Claro's error body. A client that cannot do OAuth sends the key in a header:

json
{
  "mcpServers": {
    "casafari-claro": {
      "url": "https://platform.casafari.com/mcp/claro",
      "headers": { "Authorization": "Bearer $CASAFARI_API_KEY" }
    }
  }
}

The tool definitions are readable without a key at https://expansion.casafari.com/v1/tools. See connect and parity.

What URL and sign-in does a claude.ai or ChatGPT connector need for the Claro MCP?

Give the connector the MCP URL https://platform.casafari.com/mcp/claro and sign in with your platform account: the platform's OAuth signs the client in, so no key is pasted into the connector.

The connector then lists the 7 Claro tools by their own names, such as search_premises and get_premises, and each call is metered in platform units on your account. A client that cannot do OAuth, such as a server-side agent loop, sends the platform key as Authorization: Bearer $CASAFARI_API_KEY instead (OAuth, connect). The content terms apply to what the assistant shows (content terms).

Is Claro one of the tools on the Casafari MCP gateway at mcp.casafari.com?

No: Claro is not on the Casafari MCP gateway; it has its own MCP address on the platform, https://platform.casafari.com/mcp/claro, tools with their own names and no prefix, and a platform API key or the platform's OAuth.

The gateway (connect, authentication) uses OAuth 2.1 with a Casafari account and serves the property graph products as <product>_<tool>: comparables, valuations, area statistics, transactions, alerts. Claro searches commercial premises with a brief in plain words or criteria as data (product page). The Casafari REST API on api.casafari.com, with a bearer token from POST /login, is separate too: that token does not work on Claro, whose REST prefix on the platform is /v1/claro. Claro's two doors share one error handler, because a refused MCP tool call carries the same {"error":{"code":"...","message":"..."}} body as REST (parity).

Do the Claro MCP tools and the REST operations do the same things?

Yes: the 7 MCP tools and the REST operations are one to one, with the same jobs, and only two REST operations have no tool, PUT /v1/stores and GET /v1/health.

  • search_premises = POST /v1/claro/search (tool, REST)
  • refine_search = POST /v1/claro/search/refine (tool, REST)
  • get_premises = GET /v1/claro/premises/{id} (tool, REST)
  • compare_premises = POST /v1/claro/compare (tool, REST)
  • explain_location = POST /v1/claro/location (tool, REST)
  • market_snapshot = POST /v1/claro/market (tool, REST)
  • brief_from_file = POST /v1/claro/brief/file (tool, REST)
  • REST only: PUT /v1/claro/stores (stores) and GET /v1/health (health)

The one difference in shape: brief_from_file takes the file as base64 in content_base64, where POST /v1/claro/brief/file takes a multipart upload. Ids and error bodies are the same on both doors. Full table: parity.

What does an error body look like, and which statuses can a call return?

A call that reaches Claro answers an error with {"error":{"code":"...","message":"..."}} and one of 400, 401, 403, 413, 429, 500 or 503; over MCP the same body arrives as a tool error, so one handler covers both doors.

  • 400: invalid request
  • 401: missing or invalid key
  • 403: the account has no access to this
  • 413: a brief file above 10 MB (POST /v1/claro/brief/file, brief file)
  • 429: rate limited or quota reached, with the code rate_limited or quota_reached (429 handling)
  • 503: the database cannot be read (GET /v1/health, ok false, health)
  • 500: an error on Claro's side
json
{"error":{"code":"quota_reached","message":"..."}}

A call the platform refuses before Claro (no valid key or token, no credit, a platform limit) is answered by the platform in RFC 9457 problem+json: 401 for a missing or invalid platform key or token, 402 payment_required with an upgrade_url, 429 request_limit_reached naming the window (limits). Tell them apart by shape: Claro's body is the error object with a code and a message; the platform's is problem+json.

How do I tell a per-minute 429 from a daily 429, and what limits does Claro keep?

Read the code in Claro's error body on a 429: rate_limited means calls came too fast for the per-minute limit, so wait and retry; quota_reached means a daily limit is used up until it resets, and for the live-location limit the message names live_location_points_daily.

The platform's units and plan limits come first and answer in their own format: 429 request_limit_reached names the window, 402 payment_required carries an upgrade_url (limits). Behind them Claro keeps per-account limits: calls a minute (the plans are 10, 30, 60, 120 or 300), calls a day, max_results (a page, up to 200) and live_location_points_daily. Your account's own values are set on the organisation's Claro account behind the platform. No retry header is documented: back off on rate_limited; on quota_reached stop the calls it covers, such as point calls to explain_location, until the reset.

json
{"error":{"code":"rate_limited","message":"..."}}
{"error":{"code":"quota_reached","message":"... live_location_points_daily ..."}}

What is the `Claro-Version` header, and how are changes to `v1` published?

Every Claro response carries a Claro-Version: YYYY-MM-DD header (2026-10-15 at the snapshot), passed through the platform unchanged; changes within v1 are dated additive changes, published with each Claro-Version.

Because changes are additive, a client should ignore fields it does not know. Log the header per call to see when the date changes. Over MCP the same date is serverInfo.version of the server casafari-claro. The OpenAPI description is at https://expansion.casafari.com/v1/openapi.json and the tool list at https://expansion.casafari.com/v1/tools.

http
Claro-Version: 2026-10-15

What both doors share: parity.

What does `GET /v1/health` return, and what does `active_listings` count?

GET /v1/health returns Claro's status and data vintages without a key, and active_listings counts the active commercial premises across Spain, for rent and for sale: on 15 October 2026 it reports 230,403 (last sync 2026-10-15 01:40 UTC).

The answer is cached 60 seconds and comes back 503 with ok false when the database cannot be read, which makes it a gate for a pipeline. On 15 October 2026 it names the vintages footfall estimates 2024, sociodemographics 2024 and places release 2026-09-23.1. It is at Claro's own host, https://expansion.casafari.com/v1/health; whether the platform also exposes health is not published yet. It has no MCP tool (health, parity). Date every figure you quote from it. Product page: Claro AI.

Are the counts a search returns exact, and what do `matching` and `all_criteria_stated` mean?

Yes: a search returns exact counts, matching (the premises that match), all_criteria_stated (those for which every criterion is stated) and the number for which some criteria are not stated.

Beside the counts, search_premises / POST /v1/claro/search returns the criteria as understood, a search_id kept 24 hours for refine_search, and page with offset, limit and the total ranked. Reconcile a load against matching after walking every page. With insights set to true you also get, per criterion, how many matches meet it, leave it unsaid or contradict it, and how many would state everything without it, each as a chart as data with a one-sentence answer, its count, scope, source and date. market_snapshot counts each unit once and gives no figures below 10 premises.

Can my client treat `not_stated` as false?

No: every criterion on a result is met, not met or not_stated, each with the sentence it rests on, and nothing is inferred, so not_stated means unknown and must never become false, 0 or "no" in your client.

Model it as three states. Hard criteria (operation, cities, types, price, area) exclude results outside them; every other criterion excludes only a premises whose text states the opposite, so a result that says nothing about smoke_extraction or corner stays in matching and counts among those with some criteria not stated. With districts, a premises outside them is out and one that does not state its district stays, marked not stated. A price_m2_max on a premises with no stated area is not stated. In get_premises, energy ratings, timeline fields and location figures are null when not stated or unknown, and footfall or population is never 0 for unknown. The fit verdict read from the text is one input; every raw figure is there for your own scoring (search_premises).

Are premises ids stable across REST and MCP, and can I parse them?

Premises ids are opaque pm_ tokens, the same on every door (REST, MCP, the embeddable box), and carry no readable meaning: use them as given, as keys, and never derive anything from them.

Casafari's internal ids never appear. An id from a search result feeds get_premises (GET /v1/claro/premises/{id}), compare_premises (2 to 10 ids) and explain_location (id instead of lat and lon; only a point that is not a premises counts against live_location_points_daily). search_id is a different token: it identifies a search for 24 hours and serves refine_search and paging (paging).

How do I page through every match: `limit`, `next_cursor`, `cursor` and `page`?

Send limit (up to 200 a page, 50 with a publishable key), then send each next_cursor back as cursor, alone or with limit, without brief, criteria or change; the order is fixed for the search's 24 hours, so pages never repeat or skip a premises.

page gives offset, limit and the total ranked, so you know when you are done. A search lives 24 hours under its search_id; after that, run it again and page the new one. refine_search makes a new search with its own search_id and pages. check runs only for pages of up to 50: it is forced off above 50 a page and on later pages (check).

bash
curl -X POST https://platform.casafari.com/v1/claro/search \
  -H "Authorization: Bearer $CASAFARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cursor": "'"$NEXT_CURSOR"'", "limit": 200}'

Pages: search_premises, POST /v1/claro/search.

What does `check` do, when does it run, and how do I keep a model out of a search?

check reads the top results' text against the brief with a model, adds a few seconds, and runs only for pages of up to 50: it is forced off above 50 results a page and on later pages.

Its output, a fit verdict read from the text, is one input; every raw figure and the sentence each criterion rests on are in the result with or without it. A pipeline that pages 200 at a time sends "check": false and expects no verdict from it. To run a search with no model at all, send no brief, only criteria (with criteria.keywords for text) and "check": false (keyword search). On get_premises, assessment set to true together with insights adds site_assessment, written by a model from that premises' own figures only. Page: search_premises.

Send criteria.keywords (groups of phrases, written as Spanish agencies do) and/or exclude_keywords, plus any data criteria, with no brief and "check": false; each keyword group met carries the matching sentence as evidence and the phrase as matched.

A premises meets a group when its text holds any phrase in it: put synonyms and the Catalan form in one group, and each requirement in its own group. exclude_keywords leaves out a premises whose text holds any of its phrases.

bash
curl -X POST https://platform.casafari.com/v1/claro/search \
  -H "Authorization: Bearer $CASAFARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"criteria": {"operation": "rent", "cities": ["madrid"], "keywords": [["salida de humos", "sortida de fums"], ["esquina", "chaflán"]], "exclude_keywords": ["se traspasa"]}, "check": false}'

The same body works as the arguments of search_premises over MCP. Counts, paging and not_stated work as for any search (REST).

Which criteria need location data access, and what is `not_applied`?

Eight criteria need location data access on the organisation's Claro account: footfall_min, pop_walk10_min, pop_drive10_min, transit_min, competitors_max, spending_per_resident_min, high_income_share_min and age_shares_min; without access the search names them in not_applied rather than ignoring them.

Without access the location figures are also left out of results. Read not_applied on every answer: a criterion named there was not applied to the results. The sort value supply_gap (with gap_segment or one competitors_near_max segment, and gap_radius_m 600 by default or 300; unscored premises go last) and explain_location also need location access. Two criteria depend on the pin instead: own_stores_away_m needs an exact pin (stores), and competitors_near_max (at most max businesses of a segment or of the named chains within within_m straight-line metres, 1 to 1,000) gives no verdict to a premises with an approximate pin. Location access is set on the account behind the platform (search_premises).

Where do I find the unit, source and vintage of each location figure, and why is footfall `null` rather than 0?

Each location figure carries its unit, source and vintage, and when it was computed, in location_sources; a footfall or population figure of 0 is unknown and comes back null, never 0, while a count of nearby places can be 0.

On 15 October 2026, GET /v1/health reports the vintages footfall estimates 2024, sociodemographics 2024 and places release 2026-09-23.1. Label each figure from its unit in location_sources: the footfall figure is an index of pedestrian activity within a 5-minute walk that compares places with each other, so a chart never labels it people or visitors a day. The location block and location_sources come with search_premises, get_premises and explain_location when the account has location data access. Branch on null in scoring code so an unknown never enters an average as 0.

How do I upload my own stores and keep candidates away from them?

PUT /v1/claro/stores replaces the account's store list (CSV with a header naming name, lat and lon, or JSON {"stores": [{"name", "lat", "lon"}]}, up to 5,000 stores in Spain), and the criterion own_stores_away_m then requires no store within that many straight-line metres (1 to 1,000).

The list holds coordinates only. A GET on the same path returns it and a DELETE removes it. Only the account's searches read it. The criterion needs an exact pin. The operation is REST only, with no MCP tool (stores, parity); the criterion goes in criteria of search_premises.

bash
curl -X PUT https://platform.casafari.com/v1/claro/stores \
  -H "Authorization: Bearer $CASAFARI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stores": [{"name": "Plaza Mayor", "lat": 40.4155, "lon": -3.7074}]}'

What may my product display from a Claro answer?

Name the agency and link only the page the answer gives in agency_links, show the excerpt and matched sentences rather than the full description, and show photos only as the Casafari thumbnails in photos; never name a private seller, never name or link a portal.

  • agency_links lists every agency's own page for a premises, earliest first. Only where the agency has no page of its own is the link a portal's page, and then the portal's name is never shown.
  • An answer names an agency, never a private person selling.
  • Republish the excerpt and the sentence each criterion rests on (fact_sources in get_premises), not the full advertisement text.
  • photos: the first 4 Casafari thumbnails in a search result, up to 8 in get_premises; photo_source names the advertisement they come from. Do not fetch, copy or show an original image or any portal's image host.
  • Coordinates are exact, snapped to the centre of a ~500 m cell and marked approximate when the agency hides the address, or absent when there is no pin: show an approximate pin as approximate.

The API enforces these terms in what it returns; they apply to anything built on the answers. The platform Terms of Use at /legal/platform-terms apply until a Claro annex exists.

How does the embeddable search box authenticate, and what are its limits?

The embeddable search box, loaded from /embed.js, sends a publishable pub_ key in the request body of POST /v1/embed/search and POST /v1/embed/premises with Content-Type text/plain, works only from the origins the key was issued for, and returns at most 50 results a page.

A pub_ key does not work on any other operation, so everything else runs on your server with the platform key, never in the browser. The two embed operations have no reference page on the hub. How an organisation on the platform gets a pub_ key, and whether the box is served through the platform, is not published yet. Ids are the same pm_ tokens on every door, so a server-side GET /v1/claro/premises/{id} can follow a click. The content terms apply to the page (content terms); the pattern is in the use case embed the search box.

Claro AI use cases · Claro reference

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