Skip to content

Developer Portal

Access Luxembourg real estate intelligence via our public API

Getting Started

Step 1 — Get your API key

Create a free account at chathome.lu/developers to get your API key. Keys are shown once at creation — store them securely.

API Key Formatbash
# Your API key looks like this:\nch_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

Step 2 — Authenticate

Pass your API key in every request using the X-API-Key header.

Authenticationbash
curl -X GET 'https://chathome.lu/api/public/v1/communes' \
  -H 'X-API-Key: ch_your_api_key_here' \
  -H 'Accept: application/json'

Step 3 — Make your first request

Try the communes endpoint to get all Luxembourg communes with their liveability scores.

Try It

Parameters

Request

curl -X GET \
  '/api/public/v1/communes?locale=en' \
  -H 'Accept: application/json'

Endpoints

GET

/communes

Returns all 100 Luxembourg communes. Liveability is null and marked withheld unless every weighted dimension has complete evidence.

Parameters

NameTypeDescription
localestringLocale for commune namese.g. en

Response

{
  "communes": [
    {
      "slug": "luxembourg",
      "name": "Luxembourg City",
      "liveabilityScore": null,
      "liveabilityScoreStatus": "withheld"
    }
  ],
  "total": 100,
  "locale": "en"
}

Error Codes

500

INTERNAL_ERROR

Failed to fetch communes from the server.

Try It

Parameters

Request

curl -X GET \
  '/api/public/v1/communes?locale=en' \
  -H 'Accept: application/json'
GET

/communes/{slug}/intelligence

Returns source-status-aware commune intelligence. Missing evidence is null, never a neutral default.

Parameters

NameTypeDescription
slug*stringCommune URL sluge.g. luxembourg

Response

{
  "communeSlug": "luxembourg",
  "communeName": {
    "en": "Luxembourg City",
    "fr": "Luxembourg-Ville",
    "de": "Luxemburg-Stadt",
    "lu": "Lëtzebuerg-Stad",
    "pt": "Cidade do Luxemburgo"
  },
  "liveabilityScore": null,
  "liveabilityScoreStatus": "withheld",
  "dimensions": {
    "price": {
      "score": null,
      "trend": "down",
      "trendPercent": -2.1,
      "sparkline": [
        7100,
        7200,
        7050,
        6980
      ],
      "dataAvailability": "partial"
    },
    "safety": {
      "score": null,
      "trend": "flat",
      "trendPercent": null,
      "sparkline": [],
      "dataAvailability": "partial"
    }
  },
  "summary": {
    "en": "Luxembourg City's verified commune profile is currently partial. Dimensions without full source coverage are withheld rather than estimated."
  },
  "lastUpdated": "2026-03-01T00:00:00Z"
}

Error Codes

404

NOT_FOUND

No intelligence data for this commune.

401

UNAUTHORIZED

Invalid or missing API key.

Try It

Parameters

Request

curl -X GET \
  '/api/public/v1/communes/{slug}/intelligence?slug=luxembourg' \
  -H 'Accept: application/json'
GET

/market-index

Returns the latest immutable ChatHome asking-price snapshot. It is a current listing-sample median, not a transaction-price index or a momentum forecast.

Response

{
  "sourceIssueId": "2026-08",
  "month": "2026-08",
  "saleMedianPerSqm": 8000,
  "rentMedianPerSqm": 30,
  "saleSampleSize": 250,
  "rentSampleSize": 400,
  "activeListingCount": 650,
  "communeCount": 75,
  "computedAt": "2026-08-31T00:00:00.000Z",
  "publishedAt": "2026-08-31T01:00:00.000Z",
  "methodologyVersion": "v1",
  "methodologyUrl": "https://chathome.lu/en/prices/methodology",
  "canonicalUrl": "https://chathome.lu/en/prices/index/2026-08",
  "sources": [
    "Active ChatHome listing inventory"
  ]
}

Error Codes

500

INTERNAL_ERROR

Failed to compute market index.

Try It

Request

curl -X GET \
  '/api/public/v1/market-index' \
  -H 'Accept: application/json'

MCP Agent Tools

Connect AI agents (Claude Desktop, ChatGPT, Cursor, and others) directly to chathome via MCP. Public property discovery needs no account; scopes apply when authenticating for higher limits or protected tools.

Available Scopes

listings:read

Read listing access for search, interactive cards, get_listing, and get_listing_truth.

market:read

Read market access for context, valuation, affordability, and commute tools.

inquiries:write

Write access to create inquiry conversations on behalf of the authenticated user. Required for submit_inquiry.

inquiries:read

Read access to inquiry conversations created by this API key. Required for get_inquiry_status.

Agent-to-agent flow

  1. Search listings with search_rentals or search_for_sale
  2. Enrich with get_listing, get_market_context, and get_commute_times
  3. Validate price with get_price_estimate
  4. Contact the owner via submit_inquiry
  5. Poll for replies with get_inquiry_status
search_vibelistings:readNo account

Preferred natural-language search for lifestyle, proximity, mood, and other soft preferences.

Example input
{
  "query": "quiet two-bedroom near Kirchberg with a balcony",
  "intent": "rent"
}
search_rentalslistings:readNo account

Search rental properties by commune, price range, bedrooms, and more.

Example input
{
  "commune": "luxembourg",
  "maxPrice": 2500,
  "beds": 2
}
search_for_salelistings:readNo account

Search properties for sale across Luxembourg and its cross-border commuter belt.

Example input
{
  "commune": "esch-sur-alzette",
  "propertyType": "apartment"
}
render_listing_cardslistings:readNo account

Render exact results from a prior search as a cross-platform MCP Apps property carousel.

Example input
{
  "listings": [
    {
      "id": "abc123",
      "score": 91,
      "matchReasons": [
        "Near Kirchberg"
      ]
    }
  ],
  "heading": "Quiet homes near Kirchberg",
  "locale": "en"
}
get_listinglistings:readNo account

Retrieve the full fail-closed listing-page record plus normalized costs, property checks, station frequency, and commute context.

Example input
{
  "id": "abc123",
  "locale": "en"
}
compare_listingslistings:readNo account

Compare two to eight homes through one normalized response with costs, property checks, mobility, value context, and evidence.

Example input
{
  "ids": [
    "abc123",
    "def456"
  ],
  "locale": "en"
}
get_listing_truthlistings:read

Inspect the full listing-page record, normalized decision facts, sources, freshness, unknowns, and privacy-safe correction summaries.

Example input
{
  "id": "abc123",
  "locale": "en"
}
get_market_contextmarket:readNo account

Get real estate market data and liveability scores for a Luxembourg commune.

Example input
{
  "commune": "kirchberg",
  "locale": "en"
}
get_town_agenciesmarket:read

Get every agency with active listings in a supported Luxembourg town, including current buy/rent stock and asking-price ranges.

Example input
{
  "commune": "mersch",
  "locale": "en"
}
resolve_locationmarket:read

Resolve supported place names, aliases and IDs, without guessing ambiguous names.

Example input
{
  "query": "Kirchberg",
  "country": "LU",
  "locale": "en"
}
get_quick_estimatemarket:read

Numeric estimate with ranges, scope and limitations; no AI-written report or account.

Example input
{
  "commune": "mersch",
  "propertyType": "apartment",
  "listingType": "sale",
  "areaSqm": 85,
  "hasGarage": false
}
get_price_estimatemarket:read

AI-powered price estimate for a property using the chathome valuation model.

Example input
{
  "propertyType": "apartment",
  "listingType": "sale",
  "commune": "luxembourg",
  "areaSqm": 85,
  "bedrooms": 2,
  "constructionYear": 2010,
  "energyClass": "B"
}
get_affordabilitymarket:read

Calculate Luxembourg purchasing power with a complete or known-cost-only acquisition-cost basis. Bëllegen credit requires explicit residence use, eligible acquirer count, and confirmed unused balance; isFirstTimeBuyer is deprecated compatibility only.

Example input
{
  "annualNetIncome": 84000,
  "maxDebtToIncomeRatio": 0.35,
  "monthlyDebts": 500,
  "savingsAvailable": 100000,
  "householdType": "couple",
  "employmentType": "salaried",
  "residencyType": "luxembourg",
  "residenceUse": "primary",
  "bellegenAktEligibleBuyers": 2,
  "bellegenAktCreditAvailable": 80000,
  "isFirstTimeBuyer": false
}
calculate_mortgagemarket:read

Anonymous loan payment and repayment calculation from explicit assumptions. No user profile or loan application is saved.

Example input
{
  "principal": 400000,
  "annualRatePct": 3,
  "years": 25,
  "extraMonthlyPayment": 0
}
calculate_buy_vs_rentmarket:read

Anonymous buy-versus-rent scenario from explicit price, rent and rate; the winner stays null until acquisition costs are complete.

Example input
{
  "price": 650000,
  "monthlyRent": 2400,
  "mortgageRatePct": 3.2
}
calculate_acquisition_costsmarket:read

Anonymous Luxembourg purchase duties and notary costs; Bëllegen Akt only when confirmed.

Example input
{
  "price": 750000,
  "residenceUse": "primary",
  "eligibleAcquirerCount": 1
}
calculate_rent_subsidymarket:read

Anonymous indicative rent subsidy with dated thresholds; never an entitlement.

Example input
{
  "householdSize": 2,
  "monthlyNetIncome": 3500,
  "monthlyRent": 1600
}
calculate_rent_ceilingmarket:read

Anonymous legal rent ceiling (5% rule) for a rent or the owner's purchase figures.

Example input
{
  "monthlyRent": 1800,
  "price": 600000,
  "purchaseYear": 2015,
  "buildingYear": 1995
}
check_plotmarket:read

Anonymous Luxembourg plot check from a cadastral reference or precise point; information, not legal advice.

Example input
{
  "lat": 49.6116,
  "lng": 6.1319
}
get_mortgage_ratesmarket:read

Anonymous source-dated full-term fixed market observations; unavailable data never becomes a current personal bank quote.

Example input
{}
get_commute_timesmarket:read

Commute time estimates from a commune to Luxembourg City, Kirchberg, or Belval.

Example input
{
  "commune": "differdange",
  "destination": "belval",
  "mode": "transit"
}
submit_inquiryinquiries:write

Send an inquiry about a listing. Creates a conversation thread for chathome-hosted listings, or returns external agency contact details.

Example input
{
  "listingId": "abc123",
  "message": "My client is interested in this apartment. Is it still available?",
  "agentName": "Acme Property AI",
  "contactEmail": "ai@acme.example"
}
get_inquiry_statusinquiries:read

Poll an MCP agent inquiry conversation for replies. Only accessible for conversations created via the MCP agent channel.

Example input
{
  "conversationId": "conv_xyz789"
}

Claude Agent Skill

A packaged skill teaches Claude when and how to use these tools — search workflows, budget-fit annotation, and Luxembourg market context. Upload the zip in claude.ai under Settings → Capabilities, or install the Claude Code plugin (bundles the MCP server too).

Rate Limits

Rate limits are applied per API key using a 24-hour sliding window. Limits vary by tier:

Free

100/day

Free forever

Basic

1,000/day

€49/month

Premium

10,000/day

€199/month

Note: When a rate limit is exceeded, the API returns a 429 Too Many Requests response with a Retry-After header indicating when you can retry.

Error Codes

401

UNAUTHORIZED

Invalid API key

429

RATE_LIMIT_EXCEEDED

Rate limit exceeded

404

NOT_FOUND

Resource not found

400

BAD_REQUEST

Invalid request parameters

500

INTERNAL_ERROR

An unexpected server error occurred

Code Examples

bash

# Install jq for JSON formatting
# List all communes
curl -X GET \
  'https://chathome.lu/api/public/v1/communes?locale=en' \
  -H 'X-API-Key: ch_your_api_key_here' \
  -H 'Accept: application/json' | jq .

javascript

// Fetch commune intelligence
const response = await fetch(
  'https://chathome.lu/api/public/v1/communes/luxembourg/intelligence',
  {
    headers: {
      'X-API-Key': 'ch_your_api_key_here',
      'Accept': 'application/json',
    },
  }
);

const data = await response.json();
console.log(
  'Liveability:',
  data.liveabilityScoreStatus === 'available'
    ? data.liveabilityScore
    : 'withheld'
);

python

import requests

headers = {
    'X-API-Key': 'ch_your_api_key_here',
    'Accept': 'application/json',
}

# Get source-status-aware commune intelligence
response = requests.get(
    'https://chathome.lu/api/public/v1/communes/luxembourg/intelligence',
    headers=headers
)

data = response.json()
status = data.get('liveabilityScoreStatus', 'withheld')
print(f"Liveability evidence status: {status}")