DEVELOPING

Pagar por posición

GET /property-v1/properties/{paId}/publication/{portal}/positioning
PUT /property-v1/properties/{paId}/publication/{portal}/positioning

Algunos portales dejan pagar para que un aviso aparezca más arriba en el listado. El GET te da todo lo que hace falta para decidir cuánto; el PUT lo ejecuta.

Es un paso aparte de publicar. Un aviso se publica igual sin esto; lo que cambia es dónde aparece.

Cada portal lo cobra distinto

El campo mechanism te dice qué esperar, así que un mismo código sirve para todos:

PortalQué se pagamechanismspend es
Argenproppuntos de un abono mensual, que la inmobiliaria reparte entre sus avisospointsun número
Zonapropel plan del avisotierel nombre de un nivel
MercadoLibreel tipo de publicacióntierel nombre de un nivel
Cabaprop— no cobra por posición——

Hoy sólo Argenprop está habilitado. Los otros reciben 501, y el mensaje distingue los dos casos: "el portal no tiene posicionamiento pagado" no es lo mismo que "todavía no lo tenemos habilitado por acá".


El GET: el contexto para decidir

GET …/positioning?simulate=5,100&visits=1-10
ParámetroQué haceDefault
simulatelista de valores a simular: "¿en qué posición quedaría si gasto esto?"vacío
visitsrango de posiciones para traer cuántas visitas recibe cada unavacío

simulate cuesta una llamada de la cuota de tu cliente POR CADA VALOR, y visits cuesta dos. Por eso no vienen por default: los pedís cuando los necesitás. Hasta 10 valores en simulate y 25 posiciones en visits.

Qué te contesta

{ "portal": "argenpropapi",
  "mechanism": "points",
  "paId": "01M5375T7YS6PC223DWC0YQPGS",
  "current": { "position": 794, "byBedrooms": 310, "indexed": true },
  "bounds":  { "min": 0, "max": 94518 },
  "balance": { "available": 10, "total": 10, "hasSubscription": true,
               "asOf": "2026-10-16T21:10:00Z" },
  "segment": { "propertyType": 1, "operation": 1, "locality": 2102,
               "neighborhood": 20, "bedrooms": 2 },
  "simulation": [ { "spend": 5, "position": 332 }, { "spend": 100, "position": 185 } ],
  "visitsByPosition": [ { "position": 1, "visits": 7991 }, { "position": 2, "visits": 3673 } ],
  "thresholds": [ { "position": 1, "points": 90018 }, { "position": 2, "points": 90017 } ],
  "calls": 9 }
CampoQué es
currentDónde está el aviso hoy. indexed: false significa que el portal todavía no lo indexó
boundsEl piso y el techo de lo que podés gastar en este aviso
balanceLo que le queda a tu cliente, con la fecha en que lo leímos
segmentContra qué conjunto de avisos se está comparando
simulationUna entrada por cada valor que pediste
visitsByPositionCuántas visitas recibe cada posición
thresholdsCuántos puntos tiene quien está en cada posición
callsCuántas llamadas a la cuota de tu cliente costó esta consulta

🔴 El dato que hace útil a todo lo demás

visitsByPosition cruzado con simulation te dice cuántas visitas compra cada punto:

posición 1 → 7991 visitas          con   5 puntos → posición 332
posición 2 → 3673 visitas          con 100 puntos → posición 185

La posición 2 recibe menos de la mitad que la 1. Y la curva no es lineal: los primeros 5 puntos movieron 462 posiciones, los 5 siguientes movieron 22.


🔴 Lo que no pudimos averiguar te lo decimos

Un bloque que no se pudo leer no viene en null: no viene, y su motivo está en unavailable.

{ "current": { "position": 794, "indexed": true },
  "balance": { "available": 10, "asOf": "…" },
  "unavailable": [
    { "block": "simulation", "reason": "no_indexado" },
    { "block": "visitsByPosition", "reason": "no_indexado" } ] }

La diferencia importa: un campo que existe y dice null significaría "el portal dice que no hay nada". Un campo ausente con su motivo significa "no pudimos averiguarlo", que es lo que pasó.

reasonQué pasóQué hacer
no_indexadoel aviso se publicó hace muy poco y el portal todavía no lo indexóesperar y volver a pedir. No es un error
segmento_sin_datos_suficientesel mercado de esa propiedad es muy chico para que el portal tenga estadísticasnada: no hay números honestos que dar
segmento_incompletofalta la ubicación de la propiedadcompletar la zona y republicar
un código del portalel portal rechazó esa lecturaleelo: es el mismo tipo de código que en el publish

current y el PUT siguen funcionando aunque falte todo lo demás. Las estadísticas y la simulación salen de una parte del portal que puede no estar disponible; publicar, consultar tu posición y asignar no dependen de ella.

Si el segmento se ensanchó, te lo decimos

Cuando el segmento de tu propiedad tiene pocos avisos, el portal compara contra uno más amplio. Si eso pasa, lo declaramos:

{ "segment": { "propertyType": 1, "operation": 1, "locality": 2102,
               "neighborhood": 20, "bedrooms": null },
  "segmentWidened": ["bedrooms"] }

Rotulá el dato con el segmento que viene, no con el que pediste. Una curva de "Palermo, cualquier cantidad de dormitorios" no es la misma que una de "Palermo, 2 dormitorios".


El PUT: asignar

{ "spend": 50 }
{ "portal": "argenpropapi", "mechanism": "points",
  "paId": "01M5375T7YS6PC223DWC0YQPGS",
  "spend": 50, "position": 185, "calls": 3 }

position es la posición REAL que leímos, no la que la simulación prometía: la consultamos después de asignar.

Pero el portal tarda en recalcularla. Medido: asignando 5 puntos la simulación daba posición 463 y la lectura inmediata seguía devolviendo la anterior. No es un error y no significa que no se aplicó — los puntos ya se descontaron del saldo. Si querés mostrar el efecto, volvé a pedir el GET un rato después; si querés mostrarlo al instante, usá el número de la simulación y decí que es una estimación.

spend: 0 es válido — es "dejá de pagar". Por eso no hay un DELETE acá: dar de baja la publicación es otra cosa.

Si el portal todavía no reindexó, position no viene y te lo decimos:

{ "spend": 50, "positionUnavailable": "el portal todavía no reindexó el aviso" }

No validamos tu spend contra el max que leímos. Ese número viene de una lectura que puede estar vieja, y rechazarte por eso sería que nuestra copia decida en lugar del portal. El portal es la autoridad sobre lo que acepta: si lo rechaza, te devolvemos su motivo.

Errores

CódigoQué pasó
400Un parámetro mal formado. Se valida antes de llamar al portal, así que no te gasta cuota
409 portal_not_connectedEsa cuenta no está conectada al portal
409 no_publicadoLa propiedad no está publicada ahí: no hay posición que consultar
404Esa propiedad no está en tu inventario
501Ese portal no tiene posicionamiento pagado habilitado acá
502 portal_rechazoEl portal rechazó la asignación. reason trae su código

Relacionado