Keep an agency’s listings on Lagoniko, straight from its CRM.
The Partner API creates, updates and removes listings with their photos. Connect an existing listingSync bridge by changing its URL, or build on a modern JSON API.
config/portals.php-define('LISTINGSYNC_URL', 'https://webservices.example.gr/listingSync/v1_0');+define('LISTINGSYNC_URL', 'https://api.lagoniko.ai/listingSync/v1_0');
Connect an existing bridge
Same XML-RPC methods and listing structure your CRM already sends.
Build on the JSON API
Listings by your own id, batches of 100, photos by URL.
Test before going live
Run the whole catalogue with test credentials. Nothing goes public.
From credentials to your first listing on Lagoniko.
Get test credentials
Lagoniko issues credentials for each agency. Start with test credentials: listings you send with them are processed like live ones and never shown on the portal. Write to support@lagoniko.ai with the agency’s name.
You receive a username and a secret for the listingSync bridge, and an API key for the JSON API.
Send a listing
Address the listing by your own id. The first request creates it; the same request again changes nothing.
PUT /v1/listings/DOCS-1curl -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();Check it, then go live
The response says where the listing is:
live, orpendingwithblockedBylisting what it still needs. When your catalogue comes back the way you expect, ask for live credentials and switch the key.
There is one endpoint. Which credentials you call it with decides whether listings reach the portal.
| Credentials | API key | Listings |
|---|---|---|
| Test | lag_test_… | Stored and processed, photos included. Never public, never sent to buyers, no public URL. |
| Live | lag_live_… | Published on lagoniko.ai once complete. |
Listings made with test credentials stay private and are not carried over to live. Responses for them carry "test": true.
Send the API key in the Authorization header of every JSON request. On the listingSync bridge, the username and secret go in the parameters of each call, as the interface defines.
Authorization: Bearer lag_test_iF8voc2q00jAfxKT_iVAsclWsRQz8K3uFay2tSs…
The secret is shown once when it is issued. Keep it out of source control and logs; if it leaks, Lagoniko replaces it and the old one stops working at once. Credentials can be limited to fixed IP addresses. Ten failed logins in a minute block an address for 15 minutes.
If your system already sends listings to Greek property portals through the listingSync v1.0 web service, it can send them to Lagoniko unchanged. Lagoniko answers the same XML-RPC methods, with the same parameters and the same listing structure.
- Point the bridge at
https://api.lagoniko.ai/listingSync/v1_0. - Use the agency’s Lagoniko username and secret. The application key parameter is accepted as it is.
- Store the id
sync.newListingreturns, as you store portal ids today.
Send listingDetails.brokerListingID with every listing. It lets Lagoniko recognise a listing you send again: a repeated sync.newListing updates it and returns the same id. The listing’s area is read from latitude and longitude, and a title is written in Greek and English from the type, size and area. See the listingSync reference.
Partner listings follow the same rules as listings agents create in Lagoniko. A listing marked published goes live when it has:
- a property type, a price (or price on request) and a size; plot size for land
- coordinates that place it in an area
- a description in at least one language
- for buildings, the construction year and the energy class
Until then it is pending, and blockedBy says what is missing. Send the missing fields and it goes live on that update.
An agency’s first import does not notify buyers: its catalogue is not new to the market. Lagoniko turns alerts on once the import is done, and later listings are announced like any other.
Send the property’s coordinates. Lagoniko places them in its area taxonomy (region, municipality, neighbourhood) without calling an external service, and the listing appears in searches at every level of that area. GET /v1/locate shows where a point lands.
The coordinates are kept for search. precision decides what buyers see: exact shows the point and the street address; approximate, the default, shows only the area.
Send photos as URLs. The call returns at once and Lagoniko downloads them in the background, usually within seconds, into four sizes for the portal. Each photo’s status is in GET …/photos, with the reason when one fails.
A URL already on the listing is not downloaded again, and the same image is stored once however many times it is sent, so a sync that sends every photo on every edit costs nothing extra. JPEG, PNG, WebP, GIF or AVIF, up to 25 MB, 99 per listing. Image hosts must be reachable on the public internet.
The API is built for catalogues of hundreds of thousands of listings.
- Run the whole catalogue with test credentials first and look at what comes back
pending. - Keep several requests in flight: 8 to 16 connections, or batches of 100 with
/v1/listings/batchorsystem.multicall. - Ask for a higher rate limit for the first import.
For the daily sync, an unchanged listing is recognised and nothing is written, so sending the whole catalogue every day is cheap.
| Requests per credential | 1,200 a minute, shared by both interfaces; raised on request |
| Burst | A tenth of the minute’s allowance at once |
| Headers | X-RateLimit-Limit is the allowance a minute, X-RateLimit-Remaining the calls you can send right now. A refused call answers 429 with Retry-After in seconds, or fault 140 on listingSync. |
| Batch | 100 listings, or 200 calls in system.multicall |
| Request body | 10 MB JSON, 40 MB listingSync |
| Photos | 99 per listing, 25 MB each |
| Text | Title 160 characters, description 20,000, per language |
- First release: the listingSync bridge, the JSON API with batches and photos, test credentials.