One JSON object describes one listing, for sale or for rent. PUT /connections/{id}/listings/{external_id} takes it as the body; POST …/batches takes { "listings": [ … ] }; POST /validate takes { "listing": … } or { "listings": [ … ] }.
A complete sale listing#
json Copy
{
"external_id": "REAA-12345",
"transaction": "sale",
"category": "apartment",
"state": "active",
"title": {
"sr": "Trosoban stan, Vračar",
"en": "Three-room flat, Vračar"
},
"description": {
"sr": "Svetao trosoban stan na Vračaru, renoviran 2024, na pet minuta od Hrama Svetog Save.",
"en": "A bright three-room flat in Vračar, renovated in 2024, five minutes from the Temple of Saint Sava."
},
"price": {
"amount": 245000,
"currency": "EUR",
"negotiable": false
},
"location": {
"place_id": "b2c0d6a4-3f1e-4d2a-9c8b-7e6f5a4b3c2d",
"address_line": "Njegoševa 10",
"neighbourhood": "Crveni krst",
"lat": 44.8012,
"lng": 20.4722
},
"structure": {
"area_sqm": 78.5,
"rooms": 3,
"bedrooms": 2,
"bathrooms": 1,
"floor": 3,
"total_floors": 5,
"levels": 1,
"year_built": 1968,
"building_group": "old",
"condition": "renovated",
"furnished": "semi_furnished",
"heating": "district",
"elevator": true,
"parking": false,
"garage": false,
"balcony": true,
"terrace": false,
"air_conditioning": true,
"basement": false
},
"legal": {
"ownership_status": "uknjizen",
"legalization_status": "legalizovan",
"mortgage_eligible": true,
"mandate_attested": true,
"exclusive": false,
"broker_license_no": null,
"tapija_note": null
},
"commission": {
"buyer_pct": 2,
"seller_pct": 2
},
"amenities": [
"built_in_wardrobes",
"intercom"
],
"attributes": {},
"photos": [
{
"url": "https://cdn.reaa.rs/listings/12345/1.jpg",
"caption": null,
"room_type": "living_room",
"digest": null
},
{
"url": "https://cdn.reaa.rs/listings/12345/2.jpg",
"caption": null,
"room_type": "kitchen",
"digest": null
},
{
"url": "https://cdn.reaa.rs/listings/12345/3.jpg",
"caption": null,
"room_type": "bedroom",
"digest": null
}
],
"video_url": null,
"virtual_tour_url": null,
"agent": {
"email": "milan@agencija.rs",
"name": "Milan Petrović",
"phone": "+381601234567"
},
"source": {
"updated_at": "2026-10-12T08:00:00Z",
"url": "https://agencija.rs/nekretnine/12345"
},
"digest": null
}
The rent shape is on Sale versus rent shapes .
General rules#
Every object is strict. An unknown key is 422 validation_failed with the detail code unknown_field at its pointer, so a typo never passes silently.
Strings are trimmed. An empty string is an error where a value is required and reads as absent where the field is optional. Send null for an empty URL (an empty URL string is invalid_format).
Text lengths count Unicode code points (an emoji is one, a combining mark is one), like the JSON Schema maxLength in the reference . Titles are also capped at 120 UTF-16 units , where an emoji counts as two — so a title of 120 characters with emoji in it can be too long.
external_id matches ^[A-Za-z0-9._:-]{1,64}$ and is your stable id for the listing. On a PUT it must equal the {external_id} in the path.
Optional fields accept null or may be left out.
Top level#
Field
Type
Required
Rules
external_id
string
Yes
1–64 of A–Z a–z 0–9 . _ : -; equal to the path on PUT
transaction
"sale" | "rent"
Yes
picks the shape
category
string
Yes
one of the 21 categories (Serbian specifics )
state
"active" | "inactive"
Yes
active publishes, inactive keeps the listing but not live (States )
title
object
Yes
below
description
object
Yes
below
price
object
sale only
below
rent
object
rent only
Sale versus rent shapes
location
object
Yes
below
structure
object
Yes
below; rentals take fewer fields
legal
object
Yes
below; a rental's is { mandate_attested }
commission
object
No
below
amenities
string[]
No
at most 40 keys from GET /vocabularies
attributes
object
No
the per-category attributes listed by GET /vocabularies
photos
object[]
No
0–64; below and Photos and the fetcher
video_url
string | null
No
a public https:// URL, up to 2,048 characters
virtual_tour_url
string | null
No
as video_url; needs the agency's plan (Mapping notes )
agent
object
No
below
source
object
Yes
below
house_rules
object
rent only
{} allowed
digest
string | null
No
up to 128 characters; your own hash, echoed back, never interpreted
title and description#
Field
Required
Rules
title.sr
Yes
5–120 characters (and at most 120 UTF-16 units)
title.en, title.tr, title.de, title.ru
No
up to 120 characters (and 120 UTF-16 units)
description.sr
Yes
20–8,000 characters
description.en, .tr, .de, .ru
No
up to 8,000 characters
The Serbian text is required: Novi Kvadrat's source language is Serbian.
price (sale)#
Field
Type
Required
Rules
amount
number
Yes
greater than 0
currency
"EUR" | "RSD"
Yes
negotiable
boolean
No
We store the price in RSD too, derived as amount × 117 (rounded) for EUR. That value must fit our integer column, so a sale price is refused with too_big at /price/amount when it would exceed 2,147,483,647 RSD — about 18,354,561 EUR.
location#
Field
Type
Required
Rules
place_id
uuid
Yes
a city or municipality id from GET /places ; unknown → unknown_place
address_line
string
Yes
3–200 characters
neighbourhood
string
No
up to 120 characters, free text
lat
number
No
−90 to 90
lng
number
No
−180 to 180
structure#
Field
Type
Rules
area_sqm
number
greater than 0, at most 99,999.99
rooms
number
sale only; 0–99
bedrooms, bathrooms
number
0–99
floor
integer
−5 to 60
total_floors
integer
sale only; 0–60
levels
integer
1–6
year_built
integer
sale only; 1800–2035
building_group
"new" | "old"
sale only
condition
lux, renovated, original, renovation_required
sale only
furnished
furnished, semi_furnished, unfurnished
heating
string
one of the heating values (Serbian specifics )
elevator, parking, balcony, terrace, air_conditioning
boolean
garage, basement
boolean
sale only
Every structure field is optional in the schema; what a category needs before it can publish is checked when the listing is imported (missing_required, States ).
legal#
Field
Type
Required
Rules
mandate_attested
boolean
Yes
must be true when state is active: you confirm a signed brokerage contract for this listing (Legal )
ownership_status
uknjizen, nije_uknjizen, u_postupku
No
sale only
legalization_status
legalizovan, u_postupku, nelegalizovan
No
sale only
mortgage_eligible
boolean
No
sale only
exclusive
boolean
No
sale only
broker_license_no
string
No
sale only; up to 64 characters; defaults to the agency's Register of Brokers number
tapija_note
string
No
sale only; up to 500 characters
commission#
Field
Shape
Rules
buyer_pct, seller_pct
sale
0–100
landlord_pct, agency_fee_pct
rent
0–100
Accepted for agencies and solo agents; for a bank it is dropped with the warning commission_dropped.
photos[]#
Field
Type
Required
Rules
url
string
Yes
a public https:// URL: no IP address, no local host name, no port other than 443, no credentials, up to 2,048 characters; listed once (duplicate_photo_url)
caption
string
No
up to 500 characters
room_type
string
No
exterior, living_room, kitchen, bedroom, bathroom, balcony, hallway, view, garage, other
digest
string
No
your SHA-256 of the image, 64 lower-case hex characters
The first photo is the cover. See Photos and the fetcher .
agent#
Field
Type
Required
Rules
email
string
Yes (when agent is sent)
a valid email, up to 254 characters; must be an active member of the agency, else the warning agent_not_member
name
string
No
up to 120 characters; not stored
phone
string
No
up to 32 characters; not stored
source#
Field
Type
Required
Rules
updated_at
string
Yes
RFC 3339 date-time with an offset, e.g. 2026-10-12T08:00:00Z; an older value than the one we hold answers unchanged
url
string | null
No
the listing on your or the agency's site; a public https:// URL
Detail codes#
A 422 validation_failed (or a rejected item in a batch) lists details: { pointer, code, message }, one per problem.
Code
Meaning
Example pointer
unknown_field
a key the schema doesn't know
/structure/balcon
required
a required field is missing
/title/sr
invalid_type
the wrong JSON type
/structure/rooms
too_small
below a minimum (length or value)
/description/sr
too_big
above a maximum (length or value)
/price/amount
invalid_enum
a value outside the vocabulary
/structure/heating
invalid_format
a malformed id, URL, date or pattern
/photos/0/url
unknown_place
place_id is not a city or municipality we know
/location/place_id
duplicate_external_id
one external_id twice in one batch
/listings/3/external_id
duplicate_photo_url
one photo URL listed twice
/photos/2/url
Warnings do not stop an item; they arrive in the item result and in webhooks:
Warning
Meaning
agent_not_member
agent.email is not an active member of the agency; the listing has no agent
virtual_tour_dropped
the agency's plan has no virtual tours; virtual_tour_url was not stored
commission_dropped
the agency is a bank; commission was not stored
field_not_applicable
a field that does not apply to the category was ignored
coordinates_outside_place
lat / lng fall outside the place's boundary (kept; boundaries are imperfect)
photo_cap_applied
more photos than the agency's plan allows; the extra ones were dropped