API reference
Two interfaces onto the same listings. The JSON API is for new integrations; the listingSync bridge answers the XML-RPC calls existing CRMs already make.
Every request on this page can be run with your API key. Use test credentials: they never publish anything.
The listing object
What you send to create or replace a listing, and what every response returns. Responses add the fields below the line.
Attributes
transactionstringRequiredOne ofsale,rent.propertyTypestringRequiredOne ofapartment,maisonette,house,villa,studio,loft,building,commercial_building,office,shop,business_premises,showroom,warehouse,industrial_space,craft_space,hotel,parking,land,agricultural_land,island,other_land.pricenumberRequiredIn euros. Monthly rent for a rental. Optional whenpriceOnRequestis true.locationobjectRequiredWhere the property is. The area is read from the coordinates.Show 5 child attributes
latnumberRequiredLatitude, WGS84.lngnumberRequiredLongitude, WGS84.precisionstringexactshows the point and the street address.approximate(default) shows only the area.addressstring or objectStreet address, or{"el", "en"}. Shown only withexact.postalCodestring
sizenumberRequiredFloor area in m². For land,0withplotSize.plotSizenumberPlot area in m².roomsobjectShow 4 child attributes
bedroomsintegerbathroomsintegerwcintegerSeparate WCs.livingRoomsinteger
floorobjectShow 4 child attributes
numberintegerGround floor is0, basement-1.tointegerTop floor of a duplex.categorystringbasement,semi_basement,ground,elevated_ground,1to4,5_plus,penthouse. Derived fromnumberwhen left out.totalFloorsintegerFloors in the building.
yearBuiltintegerNeeded to go live, except for land and parking.yearRenovatedintegerenergyClassstringA+,A,B+,B,C,D,E,F,G,H,exempt,pending. Needed to go live, except for land and parking.conditionstringnewly_built,under_construction,unfinished,excellent,good,needs_renovation,renovated.heatingobjectShow 2 child attributes
systemstringnone,central,autonomous,underfloor,air_conditioning,heat_pump,fireplace.mediumstringnatural_gas,oil,electric,wood,pellet,solar,district,lpg.
coolingstringnone,air_conditioning,central_ac,heat_pump_reversible,ceiling_fan.furnishingstringfurnished,semi_furnished,unfurnished.kitchenstringseparate,open,american,none.parkingSpacesintegercommonExpensesnumberMonthly building charges in euros.availableFromdateYYYY-MM-DD.featuresarray of stringselevator,storage,parking,air_conditioning,balcony,sea_view,garden,pool,fireplace,security_door,double_glazing,solar_water_heater,alarm,pets_allowed,bbq,attic,playroom,underground_parking,ev_charger. Unknown codes are ignored with a warning.titleobject{"el", "en"}, up to 160 characters each. Written for you from the type, size and area when left out.descriptionobject{"el", "en"}. Needed to go live. One language is shown in both until you send the other.mediaobjectShow 2 child attributes
videoUrlstringvirtualTourUrlstring
agentRefstringYour id for the agent handling the listing.priceOnRequestbooleanHide the price on the portal.priceNegotiablebooleanstatusstringpublished(default) orhidden.photosarray of stringsImage URLs in display order, up to 99. Replaces the listing’s photos when present.
Returned by Lagoniko
idintegerLagoniko’s listing id. The listingSync methods use the same number.externalIdstringYour id.statestringliveon the portal,pendinguntil whatblockedBynames is sent,hidden, orarchivedafter a delete.blockedByarray of stringsWhat the listing needs before it can go live, for exampleenergyClass.urlstring or nullThe public page while the listing is live. Alwaysnullwith test credentials.testbooleanTrue for a listing made with test credentials.referenceCodestringThe agency’s code for the listing in the Lagoniko workspace.location.areastringThe Lagoniko area the coordinates were placed in.photosintegerPhotos ready on the listing.warningsarrayFields that were ignored, such as an unknown feature code.
{
"id": 1000214,
"externalId": "DOCS-1",
"state": "live",
"blockedBy": [],
"test": false,
"url": "https://lagoniko.ai/en/property/apartment-85-m-glyfada-1000214",
"referenceCode": "KAL-0042",
"status": "published",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
"area": "glyfada",
"address": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
"wc": null,
"livingRooms": null
},
"floor": {
"number": 3,
"to": null,
"category": "3",
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"features": [
"balcony",
"elevator",
"parking"
],
"title": {
"el": "Διαμέρισμα 85 τ.μ., Γλυφάδα",
"en": "Apartment, 85 m², Glyfada"
},
"photos": 0,
"warnings": [],
"createdAt": "2026-10-09T16:12:41.090Z",
"updatedAt": "2026-10-09T16:12:41.090Z"
}/v1/listings/{externalId}Create or replace a listing
Creates the listing the first time and replaces it after that. Sending the same body again changes nothing, so a request that timed out can simply be retried.
Fields left out are reset, as with any full replace. To change a few fields, use PATCH.
Path
externalIdstringRequiredYour id for the listing. Letters, digits and. _ : / @ # -, up to 64 characters.
Body
The listing object. Required fields:
transactionstringRequiredOne ofsale,rent.propertyTypestringRequiredOne ofapartment,maisonette,house,villa,studio,loft,building,commercial_building,office,shop,business_premises,showroom,warehouse,industrial_space,craft_space,hotel,parking,land,agricultural_land,island,other_land.pricenumberRequiredIn euros. Monthly rent for a rental. Optional whenpriceOnRequestis true.locationobjectRequiredWhere the property is. The area is read from the coordinates.Show 5 child attributes
latnumberRequiredLatitude, WGS84.lngnumberRequiredLongitude, WGS84.precisionstringexactshows the point and the street address.approximate(default) shows only the area.addressstring or objectStreet address, or{"el", "en"}. Shown only withexact.postalCodestring
sizenumberRequiredFloor area in m². For land,0withplotSize.
Returns
The listing, with 201 when it was created and 200 otherwise.
curl -X PUT https://api.lagoniko.ai/v1/listings/DOCS-1 \
-H "Authorization: Bearer $LAGONIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate"
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1
},
"floor": {
"number": 3,
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"heating": {
"system": "autonomous",
"medium": "natural_gas"
},
"features": [
"elevator",
"balcony",
"parking"
],
"description": {
"el": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.",
"en": "Bright 85 m² apartment in Glyfada, 300 metres from the sea."
}
}'$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'transaction' => 'sale',
'propertyType' => 'apartment',
'price' => 245000,
'location' => [
'lat' => 37.865,
'lng' => 23.755,
'precision' => 'approximate',
],
'size' => 85,
'rooms' => [
'bedrooms' => 2,
'bathrooms' => 1,
],
'floor' => [
'number' => 3,
'totalFloors' => 5,
],
'yearBuilt' => 2004,
'energyClass' => 'B',
'heating' => [
'system' => 'autonomous',
'medium' => 'natural_gas',
],
'features' => ['elevator', 'balcony', 'parking'],
'description' => [
'el' => 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.',
'en' => 'Bright 85 m² apartment in Glyfada, 300 metres from the sea.',
],
], JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.put(
"https://api.lagoniko.ai/v1/listings/DOCS-1",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
json={
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
},
"floor": {
"number": 3,
"totalFloors": 5,
},
"yearBuilt": 2004,
"energyClass": "B",
"heating": {
"system": "autonomous",
"medium": "natural_gas",
},
"features": ["elevator", "balcony", "parking"],
"description": {
"el": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.",
"en": "Bright 85 m² apartment in Glyfada, 300 metres from the sea.",
},
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
transaction: 'sale',
propertyType: 'apartment',
price: 245000,
location: {
lat: 37.865,
lng: 23.755,
precision: 'approximate',
},
size: 85,
rooms: {
bedrooms: 2,
bathrooms: 1,
},
floor: {
number: 3,
totalFloors: 5,
},
yearBuilt: 2004,
energyClass: 'B',
heating: {
system: 'autonomous',
medium: 'natural_gas',
},
features: ['elevator', 'balcony', 'parking'],
description: {
el: 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.',
en: 'Bright 85 m² apartment in Glyfada, 300 metres from the sea.',
},
}),
});
const data = await response.json();{
"id": 1000214,
"externalId": "DOCS-1",
"state": "live",
"blockedBy": [],
"test": false,
"url": "https://lagoniko.ai/en/property/apartment-85-m-glyfada-1000214",
"referenceCode": "KAL-0042",
"status": "published",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
"area": "glyfada",
"address": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
"wc": null,
"livingRooms": null
},
"floor": {
"number": 3,
"to": null,
"category": "3",
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"features": [
"balcony",
"elevator",
"parking"
],
"title": {
"el": "Διαμέρισμα 85 τ.μ., Γλυφάδα",
"en": "Apartment, 85 m², Glyfada"
},
"photos": 0,
"warnings": [],
"createdAt": "2026-10-09T16:12:41.090Z",
"updatedAt": "2026-10-09T16:12:41.090Z"
}/v1/listings/{externalId}Update a listing
Changes the fields in the body and keeps the rest. Objects are merged field by field: {"location": {"precision": "exact"}} keeps the coordinates. features and photos are replaced as a whole.
Path
externalIdstringRequiredYour id for the listing.
Returns
The updated listing.
curl -X PATCH https://api.lagoniko.ai/v1/listings/DOCS-1 \
-H "Authorization: Bearer $LAGONIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"price": 239000,
"features": [
"elevator",
"balcony",
"parking",
"sea_view"
]
}'$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'price' => 239000,
'features' => ['elevator', 'balcony', 'parking', 'sea_view'],
], JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.patch(
"https://api.lagoniko.ai/v1/listings/DOCS-1",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
json={
"price": 239000,
"features": ["elevator", "balcony", "parking", "sea_view"],
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
price: 239000,
features: ['elevator', 'balcony', 'parking', 'sea_view'],
}),
});
const data = await response.json();{
"id": 1000214,
"externalId": "DOCS-1",
"state": "live",
"blockedBy": [],
"test": false,
"url": "https://lagoniko.ai/en/property/apartment-85-m-glyfada-1000214",
"referenceCode": "KAL-0042",
"status": "published",
"transaction": "sale",
"propertyType": "apartment",
"price": 239000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
"area": "glyfada",
"address": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
"wc": null,
"livingRooms": null
},
"floor": {
"number": 3,
"to": null,
"category": "3",
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"features": [
"balcony",
"elevator",
"parking",
"sea_view"
],
"title": {
"el": "Διαμέρισμα 85 τ.μ., Γλυφάδα",
"en": "Apartment, 85 m², Glyfada"
},
"photos": 0,
"warnings": [],
"createdAt": "2026-10-09T16:12:41.090Z",
"updatedAt": "2026-10-09T16:12:41.090Z"
}/v1/listings/{externalId}Retrieve a listing
Returns a listing by your id. Deleted listings answer 404.
Path
externalIdstringRequiredYour id for the listing.
curl https://api.lagoniko.ai/v1/listings/DOCS-1 \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/listings/DOCS-1",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"id": 1000214,
"externalId": "DOCS-1",
"state": "live",
"blockedBy": [],
"test": false,
"url": "https://lagoniko.ai/en/property/apartment-85-m-glyfada-1000214",
"referenceCode": "KAL-0042",
"status": "published",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
"area": "glyfada",
"address": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
"wc": null,
"livingRooms": null
},
"floor": {
"number": 3,
"to": null,
"category": "3",
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"features": [
"balcony",
"elevator",
"parking"
],
"title": {
"el": "Διαμέρισμα 85 τ.μ., Γλυφάδα",
"en": "Apartment, 85 m², Glyfada"
},
"photos": 0,
"warnings": [],
"createdAt": "2026-10-09T16:12:41.090Z",
"updatedAt": "2026-10-09T16:12:41.090Z"
}/v1/listingsList listings
Your listings, most recently changed first.
Query
limitinteger1 to 500. Default 50.cursorstringThenextCursorof the previous page.statestringComma-separatedlive,pending,hidden,archived. All but archived by default.updatedSincedate-timeOnly listings changed since then.externalIdstringComma-separated ids.
curl https://api.lagoniko.ai/v1/listings?limit=20&state=live%2Cpending \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/listings?limit=20&state=live%2Cpending');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/listings",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
params={
"limit": 20,
"state": "live,pending",
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings?limit=20&state=live%2Cpending', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"data": [
{
"id": 1000214,
"externalId": "DOCS-1",
"state": "live",
"blockedBy": [],
"test": false,
"url": "https://lagoniko.ai/en/property/apartment-85-m-glyfada-1000214",
"referenceCode": "KAL-0042",
"status": "published",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
"area": "glyfada",
"address": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
"wc": null,
"livingRooms": null
},
"floor": {
"number": 3,
"to": null,
"category": "3",
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"features": [
"balcony",
"elevator",
"parking"
],
"title": {
"el": "Διαμέρισμα 85 τ.μ., Γλυφάδα",
"en": "Apartment, 85 m², Glyfada"
},
"photos": 0,
"warnings": [],
"createdAt": "2026-10-09T16:12:41.090Z",
"updatedAt": "2026-10-09T16:12:41.090Z"
}
],
"nextCursor": "MTc2MDAyNjM2MTA5MC42OGU3Y2UzOWE1YjY0ZjAw"
}/v1/listings/{externalId}Delete a listing
Takes the listing off the portal. A later PUT with the same id brings it back under the same id.
Path
externalIdstringRequiredYour id for the listing.
curl -X DELETE https://api.lagoniko.ai/v1/listings/DOCS-1 \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.delete(
"https://api.lagoniko.ai/v1/listings/DOCS-1",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
response.raise_for_status()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});204 No Content/v1/listings/{externalId}/renewRenew a listing
Confirms the listing is still available. Buyers see how recently a listing was confirmed.
Path
externalIdstringRequiredYour id for the listing.
curl -X POST https://api.lagoniko.ai/v1/listings/DOCS-1/renew \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1/renew');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.post(
"https://api.lagoniko.ai/v1/listings/DOCS-1/renew",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
response.raise_for_status()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1/renew', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});204 No Content/v1/listings/{externalId}/photosSet photos
Makes the listing’s photos exactly these URLs, in this order. Unchanged URLs keep their photo; new ones are downloaded in the background, usually within seconds.
JPEG, PNG, WebP, GIF or AVIF, up to 25 MB each, 99 per listing. The same image sent again is stored once.
Path
externalIdstringRequiredYour id for the listing.
Body
photosarray of stringsRequiredImage URLs in display order.
Returns
Each slot with its status: pending, processing, ready or failed with an error that says why.
curl -X PUT https://api.lagoniko.ai/v1/listings/DOCS-1/photos \
-H "Authorization: Bearer $LAGONIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"photos": [
"https://images.your-crm.gr/listings/2041/1.jpg",
"https://images.your-crm.gr/listings/2041/2.jpg"
]
}'$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1/photos');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'photos' => ['https://images.your-crm.gr/listings/2041/1.jpg', 'https://images.your-crm.gr/listings/2041/2.jpg'],
], JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.put(
"https://api.lagoniko.ai/v1/listings/DOCS-1/photos",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
json={
"photos": ["https://images.your-crm.gr/listings/2041/1.jpg", "https://images.your-crm.gr/listings/2041/2.jpg"],
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1/photos', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
photos: ['https://images.your-crm.gr/listings/2041/1.jpg', 'https://images.your-crm.gr/listings/2041/2.jpg'],
}),
});
const data = await response.json();{
"data": [
{
"slot": 1,
"status": "ready",
"source": "https://images.your-crm.gr/listings/2041/1.jpg",
"url": "https://lagoniko.ai/uploads/properties/apartment-85-m-glyfada-1000214/2026/10/26070ac7954b1cdd-detail.webp"
},
{
"slot": 2,
"status": "pending",
"source": "https://images.your-crm.gr/listings/2041/2.jpg",
"url": null
}
]
}/v1/listings/{externalId}/photosList photos
The listing’s photo slots and their status.
Path
externalIdstringRequiredYour id for the listing.
curl https://api.lagoniko.ai/v1/listings/DOCS-1/photos \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/listings/DOCS-1/photos');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/listings/DOCS-1/photos",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/DOCS-1/photos', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"data": [
{
"slot": 1,
"status": "failed",
"source": "https://images.your-crm.gr/listings/2041/1.jpg",
"url": null,
"error": "The image URL answered 404."
}
]
}/v1/listings/batchBatch create or replace
Up to 100 listings in one request, each with its externalId. Each succeeds or fails on its own; the response has one result per listing, in order.
Body
listingsarray of objectsRequiredListings, each withexternalId.
curl -X POST https://api.lagoniko.ai/v1/listings/batch \
-H "Authorization: Bearer $LAGONIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"listings": [
{
"externalId": "DOCS-1",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate"
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1
},
"floor": {
"number": 3,
"totalFloors": 5
},
"yearBuilt": 2004,
"energyClass": "B",
"heating": {
"system": "autonomous",
"medium": "natural_gas"
},
"features": [
"elevator",
"balcony",
"parking"
],
"description": {
"el": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.",
"en": "Bright 85 m² apartment in Glyfada, 300 metres from the sea."
}
},
{
"externalId": "DOCS-2",
"transaction": "rent",
"propertyType": "studio",
"price": 650,
"location": {
"lat": 37.8186,
"lng": 23.7745
},
"size": 32,
"yearBuilt": 1998,
"energyClass": "D",
"description": {
"el": "Γκαρσονιέρα στη Βούλα."
}
}
]
}'$ch = curl_init('https://api.lagoniko.ai/v1/listings/batch');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'listings' => [
[
'externalId' => 'DOCS-1',
'transaction' => 'sale',
'propertyType' => 'apartment',
'price' => 245000,
'location' => [
'lat' => 37.865,
'lng' => 23.755,
'precision' => 'approximate',
],
'size' => 85,
'rooms' => [
'bedrooms' => 2,
'bathrooms' => 1,
],
'floor' => [
'number' => 3,
'totalFloors' => 5,
],
'yearBuilt' => 2004,
'energyClass' => 'B',
'heating' => [
'system' => 'autonomous',
'medium' => 'natural_gas',
],
'features' => ['elevator', 'balcony', 'parking'],
'description' => [
'el' => 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.',
'en' => 'Bright 85 m² apartment in Glyfada, 300 metres from the sea.',
],
],
[
'externalId' => 'DOCS-2',
'transaction' => 'rent',
'propertyType' => 'studio',
'price' => 650,
'location' => [
'lat' => 37.8186,
'lng' => 23.7745,
],
'size' => 32,
'yearBuilt' => 1998,
'energyClass' => 'D',
'description' => [
'el' => 'Γκαρσονιέρα στη Βούλα.',
],
],
],
], JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.post(
"https://api.lagoniko.ai/v1/listings/batch",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
json={
"listings": [
{
"externalId": "DOCS-1",
"transaction": "sale",
"propertyType": "apartment",
"price": 245000,
"location": {
"lat": 37.865,
"lng": 23.755,
"precision": "approximate",
},
"size": 85,
"rooms": {
"bedrooms": 2,
"bathrooms": 1,
},
"floor": {
"number": 3,
"totalFloors": 5,
},
"yearBuilt": 2004,
"energyClass": "B",
"heating": {
"system": "autonomous",
"medium": "natural_gas",
},
"features": ["elevator", "balcony", "parking"],
"description": {
"el": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.",
"en": "Bright 85 m² apartment in Glyfada, 300 metres from the sea.",
},
},
{
"externalId": "DOCS-2",
"transaction": "rent",
"propertyType": "studio",
"price": 650,
"location": {
"lat": 37.8186,
"lng": 23.7745,
},
"size": 32,
"yearBuilt": 1998,
"energyClass": "D",
"description": {
"el": "Γκαρσονιέρα στη Βούλα.",
},
},
],
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/listings/batch', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
listings: [
{
externalId: 'DOCS-1',
transaction: 'sale',
propertyType: 'apartment',
price: 245000,
location: {
lat: 37.865,
lng: 23.755,
precision: 'approximate',
},
size: 85,
rooms: {
bedrooms: 2,
bathrooms: 1,
},
floor: {
number: 3,
totalFloors: 5,
},
yearBuilt: 2004,
energyClass: 'B',
heating: {
system: 'autonomous',
medium: 'natural_gas',
},
features: ['elevator', 'balcony', 'parking'],
description: {
el: 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα, 300 μέτρα από τη θάλασσα.',
en: 'Bright 85 m² apartment in Glyfada, 300 metres from the sea.',
},
},
{
externalId: 'DOCS-2',
transaction: 'rent',
propertyType: 'studio',
price: 650,
location: {
lat: 37.8186,
lng: 23.7745,
},
size: 32,
yearBuilt: 1998,
energyClass: 'D',
description: {
el: 'Γκαρσονιέρα στη Βούλα.',
},
},
],
}),
});
const data = await response.json();{
"results": [
{
"index": 0,
"externalId": "DOCS-1",
"ok": true,
"created": false,
"id": 1000214,
"state": "live",
"blockedBy": [],
"warnings": []
},
{
"index": 1,
"externalId": "DOCS-2",
"ok": true,
"created": true,
"id": 1000215,
"state": "live",
"blockedBy": [],
"warnings": []
}
],
"summary": {
"received": 2,
"succeeded": 2,
"failed": 0
}
}/v1/meCheck your credentials
The credential you are calling with and its limits. A quick way to test a key.
curl https://api.lagoniko.ai/v1/me \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/me');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/me",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/me', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"credential": {
"name": "Kalimera Homes",
"keyId": "iF8voc2q00jAfxKT",
"username": "kalimera-homes",
"mode": "test"
},
"limits": {
"requestsPerMinute": 1200,
"batchSize": 100,
"photosPerListing": 99
},
"announceNewListings": false
}curl https://api.lagoniko.ai/v1/locate?lat=37.865&lng=23.755 \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/locate?lat=37.865&lng=23.755');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/locate",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
params={
"lat": 37.865,
"lng": 23.755,
},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/locate?lat=37.865&lng=23.755', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"area": {
"slug": "glyfada",
"name": {
"el": "Γλυφάδα",
"en": "Glyfada"
}
}
}/v1/referenceAccepted values
Every accepted value of every coded field. Use it to check your mappings.
curl https://api.lagoniko.ai/v1/reference \
-H "Authorization: Bearer $LAGONIKO_API_KEY"$ch = curl_init('https://api.lagoniko.ai/v1/reference');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('LAGONIKO_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);import os
import requests
response = requests.get(
"https://api.lagoniko.ai/v1/reference",
headers={"Authorization": f"Bearer {os.environ['LAGONIKO_API_KEY']}"},
timeout=30,
)
data = response.json()const response = await fetch('https://api.lagoniko.ai/v1/reference', {
headers: {
Authorization: `Bearer ${process.env.LAGONIKO_API_KEY}`,
},
});
const data = await response.json();{
"transaction": [
"sale",
"rent"
],
"energyClass": [
"A+",
"A",
"B+",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"exempt",
"pending"
],
"...": "..."
}Errors
Every error has the same shape. code is stable and safe to branch on, details names each refused field, and requestId is what support asks for.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request is malformed: not JSON, a bad query parameter or id. |
| 401 | unauthorized | No API key, a wrong one, or a revoked one. |
| 403 | forbidden | The key is not allowed from this address. |
| 404 | not_found | No such listing or endpoint. |
| 413 | payload_too_large | Over 10 MB. Send fewer listings per batch. |
| 422 | validation_failed | Invalid fields; details names each one. |
| 429 | rate_limited | Over the limit. Wait Retry-After seconds. |
| 500 | internal_error | Nothing changed. Retry. |
Responses carry X-RateLimit-Limit and X-RateLimit-Remaining; a 429 carries Retry-After.
{
"error": {
"code": "validation_failed",
"message": "The listing has 2 problems.",
"details": [
{
"field": "propertyType",
"code": "invalid_value",
"message": "propertyType must be one of: apartment, maisonette, …"
},
{
"field": "location.lat",
"code": "out_of_range",
"message": "location.lat must be at most 42.5."
}
],
"requestId": "req_1B-PuDAe5Aak"
}
}listingSync bridge
The listingSync v1.0 interface, answered by Lagoniko: XML-RPC over HTTPS at https://api.lagoniko.ai/listingSync/v1_0. A bridge written for it works unchanged.
Protocol
- Every call carries the application key, the username and the secret. The application key is accepted as it is.
- Method names are case-insensitive. UTF-8, ISO-8859-7 and Windows-1253 bodies are read by their XML declaration.
- Every answer is HTTP 200; an error is a fault with a code.
- Listing ids are Lagoniko’s integers.
brokerListingIDis yours and makes a repeatedsync.newListingan update. - Bodies up to 40 MB, enough for a photo sent as
base64.
<?xml version="1.0"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params/>
</methodCall><?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><array><data>
<value><string>sync.newListing</string></value>
<value><string>sync.editListing</string></value>
<value><string>sync.deleteListing</string></value>
<value><string>…</string></value>
<value><string>system.multicall</string></value>
</data></array></value></param>
</params>
</methodResponse>sync.newListingsync.newListing
sync.newListing(appKey, brokerId, username, password, listing)Creates a listing and returns its id. When listingDetails.brokerListingID matches a listing you already sent, that listing is updated instead and its id is returned, so a bridge that lost a response can send the listing again.
Parameters
appKeystringRequiredAny value. Kept for compatibility.brokerIdintRequiredYour id for the agent.0when you have none.usernamestringRequiredpasswordstringRequiredThe secret.listingstructRequiredThe listing structure.
Returns int listing id
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.newListing</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><int>0</int></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><struct>
<member><name>location</name><value><struct>
<member><name>latitude</name><value><string>37.8650</string></value></member>
<member><name>longitude</name><value><string>23.7550</string></value></member>
<member><name>geocodeType</name><value><string>offset</string></value></member>
</struct></value></member>
<member><name>propertyDetails</name><value><struct>
<member><name>category</name><value><string>residential</string></value></member>
<member><name>propertyType</name><value><string>apartment</string></value></member>
<member><name>livingArea</name><value><int>85</int></value></member>
<member><name>rooms</name><value><int>2</int></value></member>
<member><name>fullBathrooms</name><value><int>1</int></value></member>
<member><name>yearBuilt</name><value><int>2004</int></value></member>
</struct></value></member>
<member><name>detailedCharacteristics</name><value><struct>
<member><name>floorNumber</name><value><int>3</int></value></member>
<member><name>energyClass</name><value><string>b</string></value></member>
<member><name>heatingController</name><value><string>autonomous</string></value></member>
<member><name>elevator</name><value><string>yes</string></value></member>
<member><name>balcony</name><value><string>yes</string></value></member>
</struct></value></member>
<member><name>listingDetails</name><value><struct>
<member><name>listingType</name><value><string>for sale</string></value></member>
<member><name>price</name><value><int>245000</int></value></member>
<member><name>currency</name><value><string>eur</string></value></member>
<member><name>published</name><value><string>yes</string></value></member>
<member><name>brokerListingID</name><value><string>DOCS-1</string></value></member>
<member><name>description</name><value><struct>
<member><name>gr</name><value><string>Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα.</string></value></member>
<member><name>en</name><value><string>Bright 85 m² apartment in Glyfada.</string></value></member>
</struct></value></member>
</struct></value></member>
</struct></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.newListing(
"APP-KEY",
0,
"your-username",
os.environ["LAGONIKO_SECRET"],
{
"location": {
"latitude": "37.8650",
"longitude": "23.7550",
"geocodeType": "offset",
},
"propertyDetails": {
"category": "residential",
"propertyType": "apartment",
"livingArea": 85,
"rooms": 2,
"fullBathrooms": 1,
"yearBuilt": 2004,
},
"detailedCharacteristics": {
"floorNumber": 3,
"energyClass": "b",
"heatingController": "autonomous",
"elevator": "yes",
"balcony": "yes",
},
"listingDetails": {
"listingType": "for sale",
"price": 245000,
"currency": "eur",
"published": "yes",
"brokerListingID": "DOCS-1",
"description": {
"gr": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα.",
"en": "Bright 85 m² apartment in Glyfada.",
},
},
},
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.newListing', array_map([$encoder, 'encode'], [
'APP-KEY',
0,
'your-username',
getenv('LAGONIKO_SECRET'),
[
'location' => [
'latitude' => '37.8650',
'longitude' => '23.7550',
'geocodeType' => 'offset',
],
'propertyDetails' => [
'category' => 'residential',
'propertyType' => 'apartment',
'livingArea' => 85,
'rooms' => 2,
'fullBathrooms' => 1,
'yearBuilt' => 2004,
],
'detailedCharacteristics' => [
'floorNumber' => 3,
'energyClass' => 'b',
'heatingController' => 'autonomous',
'elevator' => 'yes',
'balcony' => 'yes',
],
'listingDetails' => [
'listingType' => 'for sale',
'price' => 245000,
'currency' => 'eur',
'published' => 'yes',
'brokerListingID' => 'DOCS-1',
'description' => [
'gr' => 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα.',
'en' => 'Bright 85 m² apartment in Glyfada.',
],
],
],
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><int>1000214</int></value></param>
</params>
</methodResponse>sync.editListingsync.editListing
sync.editListing(appKey, username, password, listingId, listing)Changes the groups and fields present in the struct and keeps everything else.
Returns boolean true
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.editListing</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
<param><value><struct>
<member><name>listingDetails</name><value><struct>
<member><name>price</name><value><int>239000</int></value></member>
</struct></value></member>
</struct></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.editListing(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
{
"listingDetails": {
"price": 239000,
},
},
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.editListing', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
[
'listingDetails' => [
'price' => 239000,
],
],
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><boolean>1</boolean></value></param>
</params>
</methodResponse>sync.getListingsync.getListing
sync.getListing(appKey, username, password, listingId)The listing in the same groups you send, plus listingDetails.listingID, url and lastModified.
Returns struct listing
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.getListing</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.getListing(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.getListing', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><struct>
<member><name>listingDetails</name><value><struct>
<member><name>listingID</name><value><int>1000214</int></value></member>
<member><name>brokerListingID</name><value><string>DOCS-1</string></value></member>
<member><name>listingType</name><value><string>for sale</string></value></member>
<member><name>price</name><value><int>245000</int></value></member>
<member><name>currency</name><value><string>eur</string></value></member>
<member><name>published</name><value><string>yes</string></value></member>
</struct></value></member>
</struct></value></param>
</params>
</methodResponse>sync.deleteListingsync.deleteListing
sync.deleteListing(appKey, username, password, listingId)Takes the listing off the portal. Sending it again with the same brokerListingID restores it under the same id.
Returns boolean true
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.deleteListing</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.deleteListing(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.deleteListing', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><boolean>1</boolean></value></param>
</params>
</methodResponse>sync.renewListingsync.renewListing
sync.renewListing(appKey, username, password, listingId)Confirms the listing is still available.
Returns boolean true
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.renewListing</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.renewListing(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.renewListing', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><boolean>1</boolean></value></param>
</params>
</methodResponse>sync.searchListingssync.searchListings
sync.searchListings(appKey, username, password, criteria?)Your listings, most recently changed first. Optional criteria: brokerListingID, page from 1, perPage 1 to 500.
Returns array of struct
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.searchListings</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><struct>
<member><name>page</name><value><int>1</int></value></member>
<member><name>perPage</name><value><int>100</int></value></member>
</struct></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.searchListings(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
{
"page": 1,
"perPage": 100,
},
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.searchListings', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
[
'page' => 1,
'perPage' => 100,
],
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><array><data>
<value><struct>
<member><name>listingID</name><value><int>1000214</int></value></member>
<member><name>brokerListingID</name><value><string>DOCS-1</string></value></member>
<member><name>published</name><value><string>yes</string></value></member>
<member><name>status</name><value><string>published</string></value></member>
</struct></value>
</data></array></value></param>
</params>
</methodResponse>sync.addImagesync.addImage
sync.addImage(appKey, username, password, listingId, slot, image)Puts a photo in a slot from 1 to 99; slot 1 is shown first. Send a URL, and the call returns at once while the photo is processed in the background, or the image itself as base64, processed during the call. A slot that already has the same URL is left as it is.
Returns int slot
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.addImage</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
<param><value><int>1</int></value></param>
<param><value><string>https://images.your-crm.gr/listings/2041/1.jpg</string></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.addImage(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
1,
"https://images.your-crm.gr/listings/2041/1.jpg",
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.addImage', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
1,
'https://images.your-crm.gr/listings/2041/1.jpg',
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><int>1</int></value></param>
</params>
</methodResponse>sync.deleteImagesync.deleteImage
sync.deleteImage(appKey, username, password, listingId, slot)Removes the photo in a slot.
Returns boolean true
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.deleteImage</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
<param><value><int>1</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.deleteImage(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
1,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.deleteImage', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
1,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><boolean>1</boolean></value></param>
</params>
</methodResponse>sync.getImageListsync.getImageList
sync.getImageList(appKey, username, password, listingId)Each slot with the URL its photo is served from, or the URL you sent while it is still processing.
Returns struct slot to URL
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.getImageList</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.getImageList(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.getImageList', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><struct>
<member><name>1</name><value><string>https://lagoniko.ai/uploads/properties/apartment-85-m-glyfada-1000214/2026/10/26070ac7954b1cdd-detail.webp</string></value></member>
</struct></value></param>
</params>
</methodResponse>sync.getImagesync.getImage
sync.getImage(appKey, username, password, listingId, slot)One slot’s status, pending, ready or failed, with the reason when it failed.
Returns struct
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sync.getImage</methodName>
<params>
<param><value><string>APP-KEY</string></value></param>
<param><value><string>your-username</string></value></param>
<param><value><string>your-secret</string></value></param>
<param><value><int>1000214</int></value></param>
<param><value><int>1</int></value></param>
</params>
</methodCall>import os
import xmlrpc.client
api = xmlrpc.client.ServerProxy("https://api.lagoniko.ai/listingSync/v1_0")
result = api.sync.getImage(
"APP-KEY",
"your-username",
os.environ["LAGONIKO_SECRET"],
1000214,
1,
)// composer require phpxmlrpc/phpxmlrpc
use PhpXmlRpc\Client;
use PhpXmlRpc\Encoder;
use PhpXmlRpc\Request;
$client = new Client('https://api.lagoniko.ai/listingSync/v1_0');
$encoder = new Encoder();
$response = $client->send(new Request('sync.getImage', array_map([$encoder, 'encode'], [
'APP-KEY',
'your-username',
getenv('LAGONIKO_SECRET'),
1000214,
1,
])));
if ($response->faultCode()) {
throw new RuntimeException($response->faultString(), $response->faultCode());
}
$result = $encoder->decode($response->value());<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param><value><struct>
<member><name>slot</name><value><int>1</int></value></member>
<member><name>status</name><value><string>ready</string></value></member>
<member><name>url</name><value><string>https://lagoniko.ai/uploads/…-detail.webp</string></value></member>
<member><name>source</name><value><string>https://images.your-crm.gr/listings/2041/1.jpg</string></value></member>
</struct></value></param>
</params>
</methodResponse>system.multicallsystem.multicall
Up to 200 calls in one request, run in order, so a listing and its photos can travel together. The answer has one entry per call: a one-element array for a result, a fault struct for a failure. Each call counts towards the rate limit.
batch = xmlrpc.client.MultiCall(api)
for listing in changed_listings:
batch.sync.newListing("APP-KEY", 0, "your-username", secret, listing)
for result in batch(): # raises Fault at the first failed call
print(result)Listing structure
A listing is a struct of groups. Send the whole listing to sync.newListing, only what changed to sync.editListing. Flags are the strings "yes" and "no". Fields the interface defines and Lagoniko does not use are accepted and ignored.
location
latitude, longitudestring or doubleRequiredThe area is read from them.geocodeTypestringexactshows the point and the street address.offset(default) andhiddenshow only the area.streetAddressstruct{gr, en}, orstreetAddressGR/streetAddressEN.displayAddressyes / nono: the street address is not stored.zipstringgeographyIdintRead only when there are no coordinates. AlsoareaID.
propertyDetails
categorystringRequiredresidential,commercial,land.propertyTypestringRequiredResidential:apartment,studio,maisonette,detached,villa,loft,bungalow,building. Commercial:office,store,warehouse,industrial,craft space,hotel,showroom,building,parking. Land:plot,parcel,island. Anything else is stored as the general type of its category.livingAreaintRequiredm².0for land, withlotSize.lotSizeintPlot, m².roomsintBedrooms.fullBathrooms, halfBathroomsintBathrooms and WCs.yearBuiltintNeeded to go live, except for land and parking.
listingDetails
listingTypestringRequiredfor sale,for rent.priceintRequiredEuros. Monthly rent for a rental.currencystringeur. Other currencies are refused.brokerListingIDstringYour id, up to 64 characters. Send it: it turns a repeatedsync.newListinginto an update.publishedyes / nonokeeps the listing off the portal.descriptionstruct{gr, en}, ordescriptionGR/descriptionEN. Needed to go live.titlestruct{gr, en}. Written for you when left out.dateAvailablestringdd/mm/yyyy.newDevelopmentyes / noNewly built.
detailedCharacteristics
floorNumberint or stringA number, orbasement,semi-basement,ground floor,elevated ground floor.energyClassstringap,a,bp,b,ctog,excemption,under_issuance. Needed to go live, except for land and parking.heatingControllerstringautonomous,central,none. AlsoheatingSystem.heatingMediumstringpetrol,natural gas,gas,current,pellet,wood,stove,infrared,fan coil,teleheating,geothermal energy.renovationYear, livingRooms, commonExpensesintfurnished, renovated, requiresRenovation, unfinished, penthouseyes / noelevator, garage, storageSpace, balcony, garden, swimmingPool, fireplace, airConditioning, secureDoor, solarWaterHeating, alarm, doubleGlass, petsAllowed, attic, playroomyes / noAmenities.storageSpacemay be a size in m².
links
virtualTourURL, videoURLstring
listing = {
"location": {
"latitude": "37.8650",
"longitude": "23.7550",
"geocodeType": "offset",
},
"propertyDetails": {
"category": "residential",
"propertyType": "apartment",
"livingArea": 85,
"rooms": 2,
"fullBathrooms": 1,
"yearBuilt": 2004,
},
"detailedCharacteristics": {
"floorNumber": 3,
"energyClass": "b",
"heatingController": "autonomous",
"elevator": "yes",
"balcony": "yes",
},
"listingDetails": {
"listingType": "for sale",
"price": 245000,
"currency": "eur",
"published": "yes",
"brokerListingID": "DOCS-1",
"description": {
"gr": "Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα.",
"en": "Bright 85 m² apartment in Glyfada.",
},
},
}$listing = [
'location' => [
'latitude' => '37.8650',
'longitude' => '23.7550',
'geocodeType' => 'offset',
],
'propertyDetails' => [
'category' => 'residential',
'propertyType' => 'apartment',
'livingArea' => 85,
'rooms' => 2,
'fullBathrooms' => 1,
'yearBuilt' => 2004,
],
'detailedCharacteristics' => [
'floorNumber' => 3,
'energyClass' => 'b',
'heatingController' => 'autonomous',
'elevator' => 'yes',
'balcony' => 'yes',
],
'listingDetails' => [
'listingType' => 'for sale',
'price' => 245000,
'currency' => 'eur',
'published' => 'yes',
'brokerListingID' => 'DOCS-1',
'description' => [
'gr' => 'Φωτεινό διαμέρισμα 85 τ.μ. στη Γλυφάδα.',
'en' => 'Bright 85 m² apartment in Glyfada.',
],
],
];Faults
| Code | Meaning | What to do |
|---|---|---|
100 | Authentication failed | Check the username and password. |
101 | Credentials revoked | Ask Lagoniko for new credentials. |
102 | Address not allowed | Call from an address registered for the credential. |
103 | Too many failed logins | Wait 15 minutes. |
110 | Listing not found | The id is not one of your listings, or it was deleted. |
120 | Invalid listing data | The message names the fields. |
121 | Invalid photo | Send an http(s) URL or base64 image data. |
122 | Invalid parameter | A listing id or slot is out of range. |
140 | Too many requests | Retry after the seconds in the message. |
500 | Internal error | Nothing changed. Retry. |
620 | Unknown method | Check the method name. |
623 | Wrong parameters | The number or types of the parameters do not match. |
634 | Not a method call | The body is not an XML-RPC method call. |
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault><value><struct>
<member><name>faultCode</name><value><int>110</int></value></member>
<member><name>faultString</name><value><string>Listing 1000214 was not found.</string></value></member>
</struct></value></fault>
</methodResponse>