Import a homeowners' association
To register a homeowners' association all you need is its address. In three calls you get the building (finca) reference, all of its dwellings, shops, garages and storage rooms (with use, floor area, participation coefficient, staircase, floor and door) and the detail of any dwelling. Every response in this guide is real, taken from production.
1. From the address to the finca
GET /api/search/address/candidates takes the address as free text (street and number, municipality and, if you have it, postcode) and returns the matching building entrances ranked by confidence, each with its 14-character cadastral reference. It does not depend on the Catastro: it geocodes with CartoCiudad (IGN) and fills in from our copy of the Catastro. It also takes the fields separately: street, number, municipality and postcode.
curl -G "https://api.parcelgps.com/api/search/address/candidates" \
--data-urlencode "q=Avenida José Rodríguez de la Borbolla Camoyán 10, Dos Hermanas" \
--data-urlencode "limit=3" \
-H "X-API-Key: $CATASTROGPS_API_KEY"Real response
200 OK{
"success": true,
"data": {
"consulta": {
"texto": "Avenida José Rodríguez de la Borbolla Camoyán 10, Dos Hermanas",
"calle": "Avenida José Rodríguez de la Borbolla Camoyán",
"numero": 10,
"municipio": "Dos Hermanas"
},
"candidatos": [
{
"refCatastral": "0745901TG4304N",
"pais": "ES",
"direccion": "AVENIDA JOSE RGUEZ BORBOLLA CAMOY 10",
"numero": 10,
"codigoPostal": "41704",
"municipio": "Dos Hermanas",
"provincia": "Sevilla",
"latitud": 37.31947088654626,
"longitud": -5.926788955974699,
"confianza": 0.93,
"coincideNumero": true,
"coincideMunicipio": true,
"enCopia": true,
"direccionCatastro": "AV JOSE RGUEZ BORBOLLA CAMOY 10",
"uso": "Residencial",
"viviendas": 346,
"anioConstruccion": 2023
},
{
"refCatastral": "0747201TG4304N",
"pais": "ES",
"direccion": "BULEVAR JOSE RODRIGUEZ DE LA BORBOLLA CAMOYAN 11",
"numero": 11,
"confianza": 0.55,
"coincideNumero": false,
"coincideMunicipio": true,
"enCopia": true
}
],
"attribution": "CartoCiudad (Instituto Geográfico Nacional) y Dirección General del Catastro"
}
}- With confidence 0.75 or more, number and municipality match: import without asking. Below that, show the candidates to the user.
- In the Basque Country and Navarra the candidate comes with pais PV or NA and the foral reference: step 2 takes it with country=PV or NA and lists the units from our copy of its foral cadastre, without participation coefficient (the foral open data do not publish it).
2. Every unit of the finca
GET /api/catastro//units returns the units in pages of up to 200, ordered by reference. While truncated is true, call again with cursor set to nextCursor. totalUnidadesFinca tells you on the first page how many there are in total.
curl -i "https://api.parcelgps.com/api/catastro/0745901TG4304N/units" \
-H "X-API-Key: $CATASTROGPS_API_KEY"
curl -i "https://api.parcelgps.com/api/catastro/0745901TG4304N/units?cursor=0745901TG4304N0201MZ" \
-H "X-API-Key: $CATASTROGPS_API_KEY"Real response
200 OKHTTP/2 200
etag: "5ab6bd3d95f85dddba81818bcb0cb65e"
cache-control: private, max-age=21600
x-quota-remaining: 2893
{
"success": true,
"data": {
"refCatastral": "0745901TG4304N",
"direccion": "AV JOSE RGUEZ BORBOLLA CAMOY 10",
"codigoPostal": "41704",
"municipio": "DOS HERMANAS",
"provincia": "Sevilla",
"usoGeneral": "Residencial",
"superficieTotal": 32789,
"anioConstruccion": 2023,
"totalUnidades": 200,
"totalUnidadesFinca": 365,
"unidades": [
{
"refCatastral": "0745901TG4304N0002KH",
"escalera": "",
"planta": "01",
"puerta": "B",
"uso": "Residencial",
"superficie": 155,
"descripcion": "Planta 01, Pta. B - Residencial",
"participacion": 0.279543,
"anio": 2023,
"direccion": "AV JOSE RGUEZ BORBOLLA CAMOY 10 Pl:01 Pt:B"
},
{
"refCatastral": "0745901TG4304N0003LJ",
"escalera": "",
"planta": "01",
"puerta": "C",
"uso": "Residencial",
"superficie": 163,
"descripcion": "Planta 01, Pta. C - Residencial",
"participacion": 0.293879,
"anio": 2023,
"direccion": "AV JOSE RGUEZ BORBOLLA CAMOY 10 Pl:01 Pt:C"
}
],
"truncated": true,
"nextCursor": "0745901TG4304N0201MZ",
"dataSource": "clone",
"dataDate": "2026-01-23",
"attribution": "Dirección General del Catastro"
},
"searchesRemaining": -1
}- A 365-unit finca is two calls: 200 and 165. The cursor is stable, so an interrupted import can resume.
- Per unit: refCatastral (20 characters), uso, superficie (m²), participacion (coefficient in %, a number at full precision), escalera, planta, puerta, year and address.
- dataSource clone means it comes from our copy of the Catastro, whether or not the Catastro is up; dataDate is the Catastro publication the rows come from. A finca missing from the copy is read live from the Catastro (dataSource catastro).
3. The detail of one dwelling
GET /api/catastro/ returns one dwelling with the same fields as the list, plus coordinates, outline and plot area.
curl "https://api.parcelgps.com/api/catastro/0745901TG4304N0002KH" \
-H "X-API-Key: $CATASTROGPS_API_KEY"Real response
200 OK{
"success": true,
"data": {
"dataSource": "local_clone",
"refCatastral": "0745901TG4304N0002KH",
"pais": "ES",
"direccion": "AV JOSE RGUEZ BORBOLLA CAMOY 10 Pl:01 Pt:B",
"codigoPostal": "41704",
"municipio": "DOS HERMANAS",
"provincia": "Sevilla",
"latitud": 37.3194709,
"longitud": -5.926789,
"uso": "Residencial",
"clase": "Urbano",
"superficieConstruida": 155,
"superficieParcela": 7320,
"anioConstruccion": 2023,
"coefParticipacion": "0.28",
"participacion": 0.279543,
"planta": "01",
"puerta": "B"
}
}- participacion, escalera, planta and puerta have the same names as in the list. superficieConstruida here is superficie in the list. coefParticipacion (text rounded to 2 decimals) stays for compatibility.
Re-import without spending quota
Every units page and every address search carries an ETag. Store the ETag of each page and send it in If-None-Match next time: if nothing changed, the answer is 304 with no body and costs no quota.
curl -i "https://api.parcelgps.com/api/catastro/0745901TG4304N/units" \
-H "X-API-Key: $CATASTROGPS_API_KEY" \
-H 'If-None-Match: "5ab6bd3d95f85dddba81818bcb0cb65e"'
HTTP/2 304
x-quota-remaining: 2929The whole flow, with pagination, retries and ETag
Retries the 429 and 503 answers that carry Retry-After, throws the other errors with their code and reuses the pages that did not change.
const BASE = "https://api.parcelgps.com";
const KEY = process.env.CATASTROGPS_API_KEY;
async function call(path, etag) {
for (let attempt = 0; attempt < 5; attempt++) {
const headers = { "X-API-Key": KEY };
if (etag) headers["If-None-Match"] = etag;
const res = await fetch(BASE + path, { headers });
if (res.status === 304) return { data: null, etag };
const retryAfter = res.headers.get("Retry-After");
if ((res.status === 429 || res.status === 503) && retryAfter) {
await new Promise((resolve) => setTimeout(resolve, Number(retryAfter) * 1000));
continue;
}
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.code}: ${body.error}`);
return { data: body.data, etag: res.headers.get("ETag") };
}
throw new Error("retries exhausted");
}
export async function findFinca(address) {
const { data } = await call("/api/search/address/candidates?q=" + encodeURIComponent(address));
const best = data.candidatos[0];
if (best.confianza < 0.75) throw new Error("Ambiguous address: ask the user to pick a candidate");
return best.refCatastral;
}
export async function importUnits(refcat14, pages = new Map()) {
const units = [];
let cursor = "";
for (;;) {
const cached = pages.get(cursor);
const query = cursor ? "?cursor=" + cursor : "";
const { data, etag } = await call(`/api/catastro/${refcat14}/units${query}`, cached?.etag);
const page = data ?? cached.data;
pages.set(cursor, { etag, data: page });
units.push(...page.unidades);
if (!page.truncated) return { units, pages };
cursor = page.nextCursor;
}
}
const refcat14 = await findFinca("Avenida José Rodríguez de la Borbolla Camoyán 10, Dos Hermanas");
const { units, pages } = await importUnits(refcat14);
console.log(refcat14, units.length);What it costs
| Call | Quota units |
|---|---|
| GET /api/search/address/candidates | 1 per search with results. |
| GET /api/catastro/:refcat14/units | 1 per unit in the page (minimum 1). A 200-unit page is 200. |
| GET /api/catastro/:refcat | 1 per dwelling. |
| 304 / 4xx / 5xx | Free: neither a 304 nor an error spends quota. |
Example: a 365-unit association costs 1 (address) + 200 + 165 (two pages) = 366 units. Re-importing it unchanged costs 0.
Errors
| HTTP | Code | What it means and what to do |
|---|---|---|
| 304 | - | Nothing changed since your ETag. Use what you had. Free. |
| 400 | VALIDATION_ERROR | Malformed parameters (empty address, postcode without 5 digits, cursor from another finca). Fix the request. |
| 401 | KEY_AUTH_001 / 002 / 003 | The API key is missing or invalid. |
| 404 | NOT_FOUND | The address or the reference does not exist. Never used for Catastro outages. |
| 429 | KEY_RATE_002 | Your key went over its requests per minute. Wait the Retry-After seconds. |
| 429 | KEY_AUTH_004 | Quota and balance used up (see X-Quota-Remaining and X-Quota-Reset): top up in the portal or wait for the reset on the 1st. |
| 503 | SERVICE_UNAVAILABLE | The official source (Catastro or CartoCiudad) is down or saturated and our copy does not have the data. Retry after Retry-After. Free. |
{
"success": false,
"code": "NOT_FOUND",
"error": "No building entrance (portal) with a cadastral reference matches that address. Include the street number and the municipality."
}Full reference
Every endpoint, field and code is in the OpenAPI 3.1 specification, which you can import into Postman, Insomnia or a client generator: https://api.parcelgps.com/api/openapi.json