trackstone/immo-data-php-sdk

PHP SDK for the Immo Data French real estate API

Maintainers

Package info

github.com/trackstone/immo-data-php-sdk

pkg:composer/trackstone/immo-data-php-sdk

Transparency log

Statistics

Installs: 161

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.0 2026-06-19 14:15 UTC

This package is auto-updated.

Last update: 2026-06-19 14:22:36 UTC


README

Tests

A PHP SDK for the Immo Data API — French real estate data including property valuation, geocoding, geographic boundaries, and market prices.

Requirements

  • PHP 8.3+
  • Guzzle 7.5+

Installation

composer require trackstone/immo-data-php-sdk

Quick Start

use ImmoData\ImmoDataClient;
use ImmoData\Enums\RealtyType;
use ImmoData\Requests\ValuationRequest;

$client = new ImmoDataClient(apiKey: 'your-api-key');

$request = new ValuationRequest(
    longitude: 2.3488,
    latitude: 48.8534,
    realtyType: RealtyType::Apartment,
    nbRooms: 3,
    livingArea: 65.0,
);

$result = $client->valuation()->estimate($request);

echo $result->mainValuation; // 485000.0
echo $result->confidence;    // 4

Configuration

use ImmoData\ImmoDataClient;

// Default (production)
$client = new ImmoDataClient(apiKey: 'your-api-key');

// Custom base URL (staging, etc.)
$client = new ImmoDataClient(
    apiKey: 'your-api-key',
    baseUrl: 'https://staging-api.immo-data.fr',
);

// Custom HTTP client (for testing or custom transport)
$client = new ImmoDataClient(
    apiKey: 'your-api-key',
    httpClient: new YourCustomHttpClient(),
);

Resources

The client exposes the following resources:

Resource Method Description
valuation() estimate() Property price estimation
geocode() search() Location search / autocomplete
geo() region(), department(), city(), district(), subdistrict() Geographic data and boundaries
market() priceHistory(), currentPrice(), saleDurationHistory(), currentSaleDuration() Market price and sale-duration data
transactions() search() Real estate transactions (DVF)
dpe() search(), get() Energy Performance Diagnostics (DPE)

Valuation

Estimate the price of a property based on its characteristics.

use ImmoData\Enums\{RealtyType, Condition, Dpe};
use ImmoData\Requests\ValuationRequest;

$request = new ValuationRequest(
    longitude: 2.3488,
    latitude: 48.8534,
    realtyType: RealtyType::Apartment,
    nbRooms: 3,
    livingArea: 65.0,
    condition: Condition::Excellent,
    bathrooms: 1,
    constructionYear: 1990,
    dpe: Dpe::C,
    floor: 4,
    level: 6,
    elevator: true,
    cellar: true,
    parking: true,
);

$result = $client->valuation()->estimate($request);

$result->mainValuation;  // float — estimated price
$result->upperValuation; // float — upper bound
$result->lowerValuation; // float — lower bound
$result->confidence;     // int   — confidence score (0-5)

Required parameters: longitude, latitude, realtyType, nbRooms, livingArea

Optional parameters:

Parameter Type Description
condition ?Condition Property condition (-1 = to renovate, 0 = standard, 1 = excellent)
landArea ?float Land area in m²
bathrooms ?int Number of bathrooms
constructionYear ?int Year of construction
dpe ?Dpe Energy performance rating (A-G)
level ?int Total number of floors in building
floor ?int Floor the property is on
pool ?bool Has swimming pool
cellar ?bool Has cellar
niceView ?bool Has nice view
parking ?bool Has parking
elevator ?bool Has elevator
patio ?bool Has patio/terrace

Geocode

Search for locations by name. Returns structured geographic results with bounding boxes and center coordinates.

use ImmoData\Enums\GeoLevel;

// Simple search
$results = $client->geocode()->search('Paris');

foreach ($results as $result) {
    echo $result->label;          // "Paris, Ile-de-France"
    echo $result->geoLevel->value; // "city"
    echo $result->inseeCode;      // "75056"
    echo $result->center?->latitude;
    echo $result->center?->longitude;
}

// Filter by geographic level
$results = $client->geocode()->search(
    query: 'Lyon',
    geoLevels: [GeoLevel::City, GeoLevel::District],
    limit: 10,
);

GeocodeResult properties:

Property Type Description
geoLevel GeoLevel Geographic level (region, department, city, district, street, address)
regionName ?string Region name
regionCode ?string Region code
departmentName ?string Department name
departmentCode ?string Department code
cityName ?string City name
inseeCode ?string INSEE code
districtCode ?string District (grand quartier) code
subdistrictCode ?string IRIS code
postCode string[] Post codes
streetCode ?string Street code (street/address results)
streetName ?string Street name (street/address results)
streetType ?string Street type, e.g. Rue, Avenue (street/address results)
streetNumber ?string Street number (address results)
streetSuffix ?string Street number suffix, e.g. bis, ter (address results)
addressId ?string Unique address identifier (address results)
parcelIds string[] Cadastral parcel identifiers (address results)
boundingBox ?BoundingBox Bounding box coordinates
center ?Coordinates Center point (longitude, latitude)
label string Human-readable label

Geographic Data

Retrieve geographic entities with their GeoJSON boundaries.

// Region
$region = $client->geo()->region('11');
echo $region->regionName;  // "Ile-de-France"
$region->boundaries;       // GeoJsonPolygon

// Department
$department = $client->geo()->department('75');
echo $department->departmentName; // "Paris"

// City (by INSEE code)
$city = $client->geo()->city('75056');
echo $city->cityName;       // "Paris"
echo $city->postCode;       // ["75001", "75002", ...]
echo $city->districtCodes;  // ["7501", "7502", ...]

// District
$district = $client->geo()->district('7514');
echo $district->districtName;     // "Observatoire"
echo $district->subdistrictCodes; // ["751401", "751402", ...]

// Subdistrict (IRIS)
$subdistrict = $client->geo()->subdistrict('751104');
echo $subdistrict->subdistrictName;

All geographic entities include a boundaries property containing a GeoJsonPolygon with type and coordinates.

Market Data

Retrieve real estate market data at the department, city, or district level.

use ImmoData\Enums\{GeoLevel, RealtyType};

// Price history for Paris apartments
$history = $client->market()->priceHistory(
    code: '75056',
    geoLevel: GeoLevel::City,
    realtyType: RealtyType::Apartment,
    startDate: '2020-01-01',
    endDate: '2024-12-31',
);

echo $history->metric; // "sqm_price"
foreach ($history->data as $point) {
    echo "{$point->period}: {$point->value} EUR/m²";
}

// Current price for a department
$price = $client->market()->currentPrice(
    code: '75',
    geoLevel: GeoLevel::Department,
    realtyType: RealtyType::Apartment,
);

echo $price->value; // 10234.5 (EUR/m², null if no data available)

Market endpoints only support GeoLevel::Department, GeoLevel::City, and GeoLevel::District. Using other levels will throw an InvalidArgumentException.

Sale Duration

Retrieve how long properties take to sell, at the department, city, or district level. Sale duration is aggregated across all property types and is available from January 2022.

use ImmoData\Enums\{GeoLevel, DurationUnit};

// History of the average sale duration for a city
$history = $client->market()->saleDurationHistory(
    code: '75114',
    geoLevel: GeoLevel::City,
    startDate: '2022-01',
    endDate: '2024-12',
    unit: DurationUnit::Days,
);

echo $history->unit; // "days"
foreach ($history->data as $point) {
    echo "{$point->period}: {$point->value} days";
}

// Current average sale duration for a department
$current = $client->market()->currentSaleDuration(
    code: '75',
    geoLevel: GeoLevel::Department,
    unit: DurationUnit::Months,
);

echo $current->unit;  // "months"
echo $current->value; // 3.0 (null if no data available)

Sale-duration endpoints accept the same GeoLevel::Department, GeoLevel::City, and GeoLevel::District levels as the price endpoints. Dates use the YYYY-MM format. currentSaleDuration()->value is null when no data is available.

DPE (Energy Performance Diagnostics)

Search Energy Performance Diagnostics (DPE). Like transactions, DPE search supports either code + geoLevel or latitude + longitude + radius, and is paginated with a searchAfter cursor.

use ImmoData\Enums\{GeoLevel, Dpe, RealtyType, DpeSortBy, SortOrder};
use ImmoData\Requests\DpeRequest;

$result = $client->dpe()->search(new DpeRequest(
    code: '75114',
    geoLevel: GeoLevel::City,
    dpeRating: [Dpe::F, Dpe::G],          // energy label (étiquette énergie)
    gesRating: [Dpe::E, Dpe::F, Dpe::G],  // climate label (étiquette climat)
    realtyType: [RealtyType::Apartment],
    sortBy: DpeSortBy::Date,
    sortOrder: SortOrder::Desc,
    size: 20,
));

echo $result->total;
foreach ($result->data as $dpe) {
    echo $dpe->dpeNumber;       // "2375E1234567A"
    echo $dpe->dpeRating;       // "D"
    echo $dpe->energyConsFinal; // 180.5 (kWh/m²/an)
    echo $dpe->location?->address?->cityName;
    echo $dpe->realty?->realtyType; // "apartment"
}

// Next page
$next = $client->dpe()->search(new DpeRequest(
    code: '75114',
    geoLevel: GeoLevel::City,
    searchAfter: $result->searchAfter,
));

// Retrieve a single DPE by its ADEME number
$dpe = $client->dpe()->get('2375E1234567A');
echo $dpe->dpeRating; // "D"

DPE search accepts GeoLevel::City, GeoLevel::District, and GeoLevel::Address. The Dpe enum (A-G) is reused for both dpeRating and gesRating filters.

Enums

Enum Values
RealtyType House, Apartment
GeoLevel Region, Department, City, District, Street, Address
Dpe A, B, C, D, E, F, G
Condition ToRenovate (-1), Standard (0), Excellent (1)
MarketType Sales
Interval Monthly
Metric SqmPrice
DurationUnit Days, Months
DpeSortBy Date, LivingArea, EnergyConsFinal

Error Handling

All API errors throw typed exceptions extending ImmoDataException:

use ImmoData\Exceptions\{
    ImmoDataException,
    AuthenticationException,
    ValidationException,
    InsufficientCreditsException,
    ForbiddenException,
    NotFoundException,
    RateLimitException,
    ServerException,
};

try {
    $result = $client->valuation()->estimate($request);
} catch (ValidationException $e) {
    // 400 — invalid parameters
    $errors = $e->errors(); // array of validation errors
} catch (AuthenticationException $e) {
    // 401 — invalid or missing API key
} catch (InsufficientCreditsException $e) {
    // 402 — not enough credits
} catch (ForbiddenException $e) {
    // 403 — forbidden
} catch (NotFoundException $e) {
    // 404 — resource not found
} catch (RateLimitException $e) {
    // 429 — too many requests
} catch (ServerException $e) {
    // 500/502 — server error
} catch (ImmoDataException $e) {
    // catch-all for any API error
    $e->getCode();    // HTTP status code
    $e->errorBody;    // raw error response body
}

Custom HTTP Client

You can provide your own HTTP client for testing or custom transport by implementing HttpClientInterface:

use ImmoData\HttpClient\HttpClientInterface;

class MockHttpClient implements HttpClientInterface
{
    public function get(string $path, array $query = []): array
    {
        return match ($path) {
            '/v1/valuation' => [
                'mainValuation' => 500000,
                'upperValuation' => 550000,
                'lowerValuation' => 450000,
                'confidence' => 90,
            ],
            default => throw new \RuntimeException("Unexpected path: {$path}"),
        };
    }
}

$client = new ImmoDataClient(
    apiKey: 'test',
    httpClient: new MockHttpClient(),
);

Code Style

This package uses PHP CS Fixer with the PER Coding Style preset.

# Fix code style
composer cs

# Check without fixing
composer cs:check

Testing

composer test

License

MIT