← DwellIQ

Valuation API

Send the property data you already hold and receive a valuation, an itemised breakdown of every adjustment applied, a rental estimate and a score. No listing URL and no scraping is involved, so results do not depend on a portal being reachable.

This matters for accuracy, not just convenience. Valuing from a listing alone gives a median error near 19% (see methodology), because public data cannot see a property’s floor, condition or the works it needs. You can. Supplying those fields is what closes the gap.

Authentication

Create a key in your dashboard under API keys. Send it as a bearer token. Keys are shown once at creation and stored only as a hash, so a lost key must be replaced rather than recovered. API access requires the Agency plan and is limited to 60 requests per minute.

Authorization: Bearer dq_live_xxxxxxxxxxxxxxxx

Value a property

POST https://dwelliq.io/api/public/v1/value

Minimal request

curl -X POST https://dwelliq.io/api/public/v1/value \
  -H "Authorization: Bearer $DWELLIQ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "city": "Tours",
    "surface": 70,
    "price": 180000
  }'

With the characteristics you hold

curl -X POST https://dwelliq.io/api/public/v1/value \
  -H "Authorization: Bearer $DWELLIQ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "MANDAT-2026-0412",
    "city": "Tours",
    "surface": 70,
    "price": 180000,
    "rooms": 3,
    "property_type": "apartment",
    "condition": "to_renovate",
    "dpe": "G",
    "floor": 5,
    "total_floors": 5,
    "has_lift": false,
    "outdoor": "none",
    "parking_spaces": 1,
    "works_budget": 40000
  }'

Request fields

city*stringCommune name. Arrondissements are understood (“Paris 15e”, “Lyon 3”), as are postal codes (“75015”).
surface*numberLiving area in m². Pass surface_unit: "sqft" to send square feet.
pricenumberAsking price. Omit it to get a valuation without a price comparison.
referencestringYour own mandate or listing reference, echoed back so you can reconcile results.
property_typestringapartment or house. Selects the comparable set and the rent series.
roomsintegerPrincipal rooms. Determines which official rent series applies.
conditionstringto_renovate, needs_work, refreshed, good, renovated, new. Ignored when works_budget is supplied, to avoid charging for the same defect twice.
dpestringEnergy rating A–G. F and G carry a real penalty because letting them is restricted.
floorinteger0 for ground floor. Combined with has_lift and total_floors.
has_liftbooleanWalk-ups above the second floor are penalised progressively.
outdoorstringnone, balcony, terrace, garden.
parking_spacesintegerAdded at a flat per-space value.
land_m2numberPlot area, valued well below built area per m².
works_budgetnumber€ of works, deducted at cost rather than as a percentage.
down_payment_pct, interest_rate_pct, loan_term_yearsnumberOptional financing inputs; when present the response includes cash-flow modelling.

Response

Every adjustment is itemised. The base value is fitted on recorded sales; the adjustments are conventional market coefficients, not figures fitted from transactions, which is why they are listed separately for you to review or override.

{
  "property": { "city": "Tours", "surface": 70, "price": 180000, ... },
  "market": {
    "market_price_per_m2": 3025,
    "fair_market_value": 102815,
    "fair_value_low": 83000,
    "fair_value_high": 130000,
    "value_method": "size_curve",
    "comparable_count": 2284,
    "data_source": "dvf",
    "estimated_monthly_rent": 776,
    "estimated_monthly_rent_low": 614,
    "estimated_monthly_rent_high": 981,
    "rent_source": "carte_des_loyers",
    "gross_yield_pct": 5.18,
    "price_diff_pct": 75.07
  },
  "adjustments": {
    "base_value": 188709,
    "adjusted_value": 102815,
    "total_pct": -24.3,
    "lines": [
      { "type": "energy", "reason": "Worst energy rating (G) ...", "pct": -14.0, "amount_eur": -26419 },
      { "type": "floor",  "reason": "Floor 5 with no lift",       "pct": -12.0, "amount_eur": -19475 },
      { "type": "works",  "reason": "Works required (deducted at cost)", "pct": null, "amount_eur": -40000 }
    ],
    "basis": "Base value is fitted on recorded DVF sales..."
  },
  "scores": { "global_score": 27.2, "price_score": ..., "roi_score": ... },
  "recommendation": "AVOID",
  "analysis_id": "clx..."
}

Errors

400Malformed JSON, or a required field is missing.
401Missing, malformed or revoked API key.
403Key is valid but the plan does not include API access.
429Over 60 requests/minute. Honour the Retry-After header.

Notes for integrators