The property JSON object
This is the reference for what you send: which fields the property object has, which ones are required, which values each one accepts, and how to say "I don't have this data".
It is a reference for the object, not for an endpoint. To understand how a property is identified —why type and operation are stable keys and where their values come from— read The property model first.
The fields and ranges on this page come from measuring 220 real properties, not from assumptions. Wherever the real data contradicts the rule, it is noted.
38 fields are enough
You don't need to send the complete object. If you send the 38 fields below with the
attributes array filled in, the response returns the entire property —82 fields— with
everything else reconstructed.
you send 38 fields → we return 82
you send 84 fields → we return 82 (the same ones, identical)
It works because 46 of those 82 fields are already stated in attributes. When you send the
entry {"id": "alarm", "key_legacy": "alarm", "selected": true}, you don't also need to send
"alarm": true: it is the same data twice.
And the 46 come back to their place, mixed in with the rest of the property — not to a separate block.
Start with the minimal object. It is less code on your side, fewer chances to contradict yourself, and the result is exactly the same.
The minimal object
{
"code": "DEV-V3-909181",
"title": "Hermoso departamento luminoso con excelente ubicación y amenities completos",
"address": "Av. Santa Fe 4271",
"zone0": 1,
"zone1": 2,
"zone2": 189,
"zone3": 973,
"description": "Excelente propiedad ubicada en zona premium…",
"descriptionFormatted": "Excelente propiedad ubicada en zona premium…",
"zipcode": "1684",
"betweenStreets": "Entre Av. Pueyrredón y Av. Coronel Díaz",
"urlWebExternal": "https://ejemplo-inmobiliaria.com/propiedad-dev",
"conditions": "Contado",
"yearsOld": 16,
"totalFloors": 16,
"apartmentsPerFloor": 3,
"floors": 2,
"buildingArea": 126,
"landArea": 189,
"ambiences": 2,
"rooms": 3,
"dependencies": 1,
"bathrooms": 1,
"toilettes": 2,
"garage": 2,
"currency": "USD",
"price": 142410,
"rented": false,
"sold": false,
"reserved": false,
"suspended": false,
"occupancy": null,
"images": [
{ "url": "https://cdn.ejemplo.com/propiedades/909181/frente.jpg", "description": "Frente", "main": true },
{ "url": "https://cdn.ejemplo.com/propiedades/909181/living.jpg", "description": "Living comedor", "main": false }
],
"blueprint": { "url": "https://cdn.ejemplo.com/propiedades/909181/plano.pdf" },
"videos": [
{ "source": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "description": "Recorrido" }
],
"prices": {
"expensesCurrency": "ARS", "expensesPrice": 500,
"taxCurrency": "ARS", "taxPrice": 200,
"paymentPeriod": 1
},
"altPrices": [
{ "description": "Alquiler por semana", "currency": "ARS", "price": 100000 }
],
"attributes": [ … ]
}
Photos, floor plan, videos and additional prices
They travel in the same JSON. They are not property fields —internally they go elsewhere— but you send a single object and the routing is on us.
| Block | Shape | Note |
|---|---|---|
images | list of {url, description, main} | the URL, not the file: binaries don't travel in a JSON |
blueprint | {url} | only one per property |
videos | list of {source, description} | source is the YouTube or Matterport URL |
prices | {expensesCurrency, expensesPrice, taxCurrency, taxPrice, paymentPeriod} | maintenance fees and taxes, amounts as numbers |
altPrices | list of {description, currency, price} | alternative prices |
⚠️ The property's price and currency do not go here: they are fields of the property itself
and are already listed above.
The attributes array
This is where most of the property lives. Each entry describes one piece of data, and there are four families: the type, the operation, the status, and the individual attributes.
[
{
"id": "individualGarage", "label": "Cochera",
"group": "type", "group_sub": "propertyType",
"type": "string", "key_legacy": "6", "value": 6,
"locale": "es_AR", "country": "ar"
},
{
"id": "rent", "label": "Alquiler",
"group": "operation", "group_sub": "propertyOperation",
"type": "string", "key_legacy": "2", "value": 2,
"locale": "es_AR", "country": "ar"
},
{
"id": "statusAverage", "label": "Estado Regular",
"group": "propertyStatus", "group_sub": "status",
"type": "list", "key_legacy": "8", "value": 8,
"locale": "es_AR", "country": "ar"
},
{
"id": "accessible", "label": "Accesible",
"group": "propertyAttribute", "group_sub": "label", "group_subtype": "ammenities",
"type": "bool", "key_legacy": "accessible",
"selected": true, "status": true,
"locale": "es_AR", "country": "ar"
}
]
The value you send is key_legacy: it is the same across all 15 countries. The id, the label
and the value come from your country's catalog — where to get them is explained further down.
attributes is required, and an empty list is valid. If the property has no attributes at all,
send "attributes": []; what you cannot do is omit the key. Omitting it returns:
{"error": "ATTRIBUTES_REQUIRED",
"description": "Falta la lista de atributos de la propiedad. El campo \"attributes\" es obligatorio: enviá la lista de atributos, o una lista vacía ([]) si la propiedad no tiene ninguno."}
But sending it empty has a cost: you lose the minimal object. The 46 fields that the array reconstructs are then up to you to send, one by one.
Required fields
There are 12, plus the attributes array. Everything else is optional.
Ten of them are individual fields:
| Field | Type | Rule |
|---|---|---|
code | string | 1–30, no spaces: ^[A-Za-z0-9._-]{1,30}$ |
title | string | 1–150 characters |
description | string | at least 1 character |
price | integer | 1 or more |
currency | string | USD or ARS |
zone0 | integer | country |
zone1 | integer | province |
zone2 | integer | district or department |
zone3 | integer | locality or neighborhood |
address | string | at least 1 character |
And the two that complete the 12 are the type and the operation, which can be sent in two ways:
| Field | How to send it |
|---|---|
| type | an attributes entry with group: "type" ← preferred · or the individual field type |
| operation | an attributes entry with group: "operation" ← preferred · or the individual field operation |
Either form is enough. If you send both and they don't match, the array wins and the individual field is ignored.
Optional fields
| Field | Type | Range | Note |
|---|---|---|---|
mapLatitude · mapLongitude | number | ±90 / ±180 | see the warning below |
buildingArea | integer | 1 – 100,000 m² | |
landArea | integer | 1 – 10,000,000 m² | |
rooms | integer | 0 – 50 | ⚠️ these are bedrooms, not rooms in general |
ambiences | integer | 0 – 50 | the rooms |
bathrooms | integer | 0 – 50 | |
toilettes | integer | 0 – 10 | |
garage | integer | 0 – 100 | |
floors | integer | 0 – 10 | floors of the unit |
totalFloors | integer | 0 – 200 | floors of the building |
apartmentsPerFloor | integer | 0 – 50 | |
dependencies | integer | 0 – 10 | |
yearsOld | integer | 0 – 300 | age in years |
occupancy | integer | 1 – 50 | |
zipcode | string | 1–10: ^[A-Za-z0-9-]{1,10}$ | |
betweenStreets | string | free | |
conditions · urlWebExternal · descriptionFormatted | string | free |
mapLatitude and mapLongitude go as numbers, not as text, and 0 is not "no
coordinate": it is a real point in the Gulf of Guinea. If you don't have the coordinate, send
null or don't send the field — but never 0.
How to say "I don't have this data"
An optional field you don't have is sent as null or not sent at all: for us it is the same
thing.
What is not the same is sending 0. A 0 in an area, in the age, in the number of occupants or in
a coordinate is stored as the number zero — that is, as data.
Where the values come from
The enumerated fields —type, operation, status, orientations, amenities— deliberately have no fixed list on this page: they depend on the country and change whenever a new one is added.
You request them here:
GET /property-attributes?country=AR
See the full endpoint reference
It returns, for that country, everything it accepts: the text key (pa_key), the value you send
(pa_key_legacy), the translated label (pa_label) and the family (pa_group_subtype).
Request the catalog for the property's country, not just one: a type may be enabled in Argentina and not in Peru.
And keep this in mind: we do not validate values against the catalog. A value that the country does not use is not rejected: it is stored, and the property ends up published with the wrong data. The catalog is not a suggestion — it is the only way to know what to send.
Verify before publishing
You can send your object and see what it produces, without creating anything:
POST /property-v1/properties/verify
It tells you whether the object is valid, which rules it breaks field by field, and what would happen on each portal. It is the same body as the creation call, without the write.
See also
- The property model — stable keys, per-country catalog, what you send and what we set
- GET /property-attributes — the value catalog
- POST /property/post — the creation endpoint