DEVELOPING

Your client's plans on the portal

GET /property-v1/customers/{portal}/account

When you publish, you send a plan. To choose it you need to know which plans your client has and how many publications are still free — and that data lives on the portal, not in our system.

You have it in two places, and it is worth using both:

WhereWhat it gives youCost
GET /property-v1/customersThe last thing we read, with its timestampnothing
GET /property-v1/customers/{portal}/accountWhat the portal says right now, and it refreshes the above1 call from your client's allowance

The cheap route is to request the client — which is free — and look at the plansStale field. If it says true, there was a publication or a takedown after that read; that is when you decide whether to spend the call.

How to request it

curl https://property-api.mapaprop.com/property-v1/customers/zonapropapi/account \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-client-ref: cliente-42"

200 response:

{
  "portal": "zonapropapi",
  "portalAccountId": "17064147",
  "plans": [
    { "plan": "DESARROLLOS_HOME", "planTotal": 5,  "planAvailable": 1 },
    { "plan": "HOME",             "planTotal": 80, "planAvailable": 31 },
    { "plan": "SIMPLE",           "planTotal": 80, "planAvailable": 20 }
  ],
  "plansReadAt": "2026-10-07T19:32:22.975Z",
  "plansStale": false,
  "planExpirations": [
    { "plan": "HOME", "date": "2026-12-31T03:00:00.000Z", "quantity": 85 }
  ],
  "cached": true
}
FieldWhat it is
plans[].planThe plan name, exactly as the portal calls it. It is the value you send as plan when publishing
plans[].planTotalHow many are contracted. It can come back null if the portal does not report it
plans[].planAvailableHow many publications are still free
plansReadAtWhen we read this from the portal
plansStaleAlways false here: we have just read it
planExpirationsWhen they expire and how many. Omitted if the portal reports none
cachedWhether we stored the copy. When false, the free read will keep giving you the previous one

The portal only lists the plans that still have room. A plan your client contracted but already used up does not appear in the list. So "it is not in plans" does not mean "they do not have it contracted": it can mean either, and the portal does not tell them apart.

That is what makes the plan publication error confusing: it says "the company has used up all of its available ones" and reads as if the account had nothing at all.

Where it fits in the flow

1. POST /property-v1/customers                   you declare the client
2. you connect their portal account              ← the plans come in here, at no extra cost
3. GET  /property-v1/customers                   you see them, free, with their timestamp
4. GET  /property-v1/customers/{portal}/account   you refresh them against the portal   (1 call)
5. PUT  …/publication/{portal}                   you publish

Step 2 already stores the plans: the call that verifies your client authorised us is the same one that brings them, so by the time you connect you already have them at no cost.

After every publication or takedown, step 3 returns them with plansStale: true.

Why we do not refresh them on our own

Because every read comes out of your client's monthly allowance on the portal —which each portal defines its own way; on Zonaprop it is 1,500 calls— and it is the same one you use to publish and to fetch enquiries. Refreshing on every publication would double your integration's spend, and the decision about when it is worth it is yours.

And there is a stronger reason: the portal does not tell us which plan a publication consumed. Its response carries the listing code, its id and its errors — no plan. If we subtracted the one you requested we could be subtracting the wrong one, so we would rather tell you the copy went stale than give you a made-up number.

If you never call it

Nothing serious happens: you send your plan when publishing and, if your client does not have it, the publish response tells you which ones they do have in the planIssue block. This endpoint exists so that you can look it up whenever you want, not so that you have to.

Response codes

CodeerrorWhat happened
200—The plans, freshly read from the portal
400portal is requiredThe portal is missing from the path
400clientRef is required (header x-client-ref)The client is not identified
401UnauthorizedToken missing or invalid
403Missing scope: property-api-readYour token cannot read
403Missing required scope: zonapropapi:connectYour token is not enabled for that portal
409portal_not_connectedYour client has no active connection with that portal
409connection_incompleteThe connection exists but is incomplete. Connect it again
501portal_sin_consulta_de_cuentaWe do not look up the account on that portal yet
502portal_no_contestoThe portal did not answer. Retry; we do not overwrite the previous copy

The 502 does not erase what we already had. Storing an empty list because the portal did not answer would say your client has no plans, and that would be false. The previous copy is still available in the client GET with its timestamp.

Portals

Portal{portal}Status
Zonapropzonapropapiavailable
Argenprop · Cabaprop · MercadoLibre—501

A 501 does not mean you got it wrong: it means that portal does not have this lookup on our side yet.