Spanish Judicial Auctions, Enriched (BOE + Catastro)
Pricing
from $200.00 / 1,000 enriched auction lots
Spanish Judicial Auctions, Enriched (BOE + Catastro)
Crawls the Spanish BOE auction portal and returns each property lot enriched with official cadastral data: floor area, year built, use, coordinates, plus normalised encumbrances, occupancy status and per-square-metre arithmetic. No personal data of debtors or any natural person, by design.
Pricing
from $200.00 / 1,000 enriched auction lots
Rating
0.0
(0)
Developer
Ernest Marzá
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Spanish Judicial Auctions, Enriched
Every property at auction in Spain, with the cadastre already joined on.
An Apify Actor. It crawls the Spanish government auction portal (subastas.boe.es), parses each property lot, and enriches it with the official cadastral record: floor area, year built, use, plot size, coordinates. It normalises the two things that actually decide whether a lot is a bargain or a trap — the encumbrances and the occupancy status — and does the euro arithmetic on top.
No personal data. No debtor, no defendant, no owner, no tax ID, not anywhere in the output. That is a design constraint, not a filter. See Legal design.
No API keys. Every source it uses is free and public.
The problem it solves
The data is public. Joining it is the work.
The BOE portal tells you an auction exists and roughly where the property is. It does not tell you how big the property is, when it was built, what it is actually used for, or where it sits on a map. The cadastre knows all of that, but it is keyed on a cadastral reference, and matching a court clerk's free-text address to one is fiddly enough that most people do it by hand, one property at a time.
This Actor does that join for every open auction in a province in about three seconds per lot.
What you get per lot
| Question | Where in the output |
|---|---|
| What is being sold, and by whom? | auction, authority |
| When does bidding close? | auction.endDate (ISO 8601) |
| What does it cost to enter? | valuation.depositAmount, depositVsAuctionValuePercent |
| Where exactly is it? | lot.address, cadastre.normalisedAddress, cadastre.latitude/longitude |
| How big is it, and how old? | cadastre.builtAreaM2, yearBuilt, plotAreaM2, use |
| What is the implied rate? | valuation.auctionValuePerM2 |
| Do I inherit the debts? | lot.encumbrances.priorChargesSurvive, detectedTypes, raw |
| Is somebody living in it? | lot.occupancy, lot.isPrimaryResidence |
| Can I see it before I bid? | lot.visitable |
| What should worry me? | scoring.riskFlags |
| What is missing? | scoring.missingData, scoring.completenessPercent |
| What does it look like? | cadastre.facadePhotoUrl |
Minimal input
{ "province": "Sevilla" }
That returns the next 25 open auctions in Sevilla, every property lot in them, each enriched. Everything else has a default.
Narrower:
{"province": "Madrid","auctionKinds": ["judicial"],"auctionState": "open","maxAuctions": 100,"minAuctionValue": 50000,"maxAuctionValue": 300000,"excludePrimaryResidence": true,"onlyWithCadastralReference": true}
Or skip the search entirely when you already know what you want:
{"auctionIds": ["SUB-JA-2026-263305","https://subastas.boe.es/ds.php?id=SUB-AT-2026-25R3586001052"]}
Identifiers, full detail URLs and the short ds.php?id= permalinks the BOE gazette publishes are all accepted. Supplying identifiers skips the search, which is faster and cheaper.
Full field documentation is in .actor/input_schema.json and renders as a form in the Apify console.
Sample output
One dataset item, abridged:
{"auction": {"auctionId": "SUB-JA-2026-261745","kind": "judicial","state": "open","endDate": "2026-07-27T16:03:40.000Z","auctionValue": 92868.75,"depositAmount": 4643.43,"boeAnnouncementId": "BOE-B-2026-19994"},"authority": {"name": "Unidad de Subastas Judiciales Region de Murcia","email": "subastas.murcia@justicia.es"},"lot": {"assetSubtype": "Vivienda","cadastralReference": "3265201XH6036N0019GF","address": "c/ Pintor Joaquin, 13","locality": "MURCIA","occupancy": "occupied-by-owner","isPrimaryResidence": true,"encumbrances": { "priorChargesSurvive": true, "chargesCancelled": true, "detectedTypes": [] }},"cadastre": {"resolved": true,"matchMethod": "from-portal","use": "Residencial","builtAreaM2": 66,"yearBuilt": 1963,"plotAreaM2": 300,"latitude": 37.9910305277064,"longitude": -1.14259811680837},"valuation": { "auctionValue": 92868.75, "auctionValuePerM2": 1407.1, "depositVsAuctionValuePercent": 5 },"scoring": {"riskFlags": ["prior-charges-survive-the-sale", "property-is-occupied", "primary-residence-of-the-occupant"],"missingData": ["appraisalValue"],"completenessPercent": 90}}
How the cadastral join works
The Actor uses the Spanish cadastre's free public web services (ovc.catastro.meh.es), which need no key and return no owner names — the cadastre classifies ownership as dato protegido and serves it only behind authenticated channels.
Two paths, in order of trust:
-
The portal published a cadastral reference. Roughly nine lots in ten. Looked up directly with
Consulta_DNPRC, giving built area, year, use, participation share, plot area and parcel type.matchMethod: "from-portal". -
No reference, so resolve from the address. This is the hard part. The BOE publishes
"C/CACERES,17"and"CL BENECHARO 49 -1 1"; the cadastre wants a normalised street type, an unaccented uppercase street name and a bare number. The Actor parses the free text into that triple and queriesConsulta_DNPLOC.matchMethod: "from-address".
Coordinates come from Consulta_CPMRC, which only accepts a 14-character parcel reference, so they locate the building rather than the individual unit.
How often it actually works
Measured on live runs, because a resolution rate nobody measured is a marketing claim:
| Auction type | Lots resolved |
|---|---|
| Tax agency (AEAT) | ~100% |
| Judicial | ~58% |
The gap is not a parser weakness — it is what the two authorities publish. The AEAT prints a cadastral reference on essentially every lot. The courts frequently do not, leaving only a clerk's address, and the remaining failures are honest ones:
- Rural lots with no address at all —
"SITIO O PARAJE ALDEA RICA","Corral Roig a la Font de Lloscos". There is nothing to resolve. - Prose instead of an address —
"LOCAL COMERCIAL Nº5 SITO EN PLANTA BAJA DEL CENTRO COMERCIAL...". - A street number the register does not have. Here the Actor says so precisely rather than shrugging: "Street CL FELIX RODRIGUEZ DE LA FUENTE exists in ALGORFA but number 4 is not registered on it. Nearest registered numbers: 1, 2, 3, 5, 7, 9."
Every failure carries its reason in cadastre.error, and scoring.missingData lists what was lost as a result.
Three quirks of the cadastre are worth knowing, since they cost real resolution rate and are not documented anywhere official:
- Its street register stores names in a 25-character field and truncates them.
"FELIX RODRIGUEZ DE LA FUENTE"is filed as"FELIX RODRIGUEZ DE LA FUE", and querying the full name matches nothing. Queries go out truncated to match. - It holds one spelling per bilingual province, and not always the Spanish one:
ALACANT, notAlicante/Alacant. Both sides are tried. - Its "house number not on this street" reply sets an error flag but ships no error block, so anything that only reads the error list mistakes it for a success.
What it does when the address is ambiguous
Honestly, and this is the part worth understanding before trusting the output.
An address without floor and door usually matches every unit in the building — twenty-two of them, for the flat in the sample above. When that happens the Actor resolves the parcel and refuses the unit: you get the cadastral reference, the municipality, the normalised address and the coordinates, but builtAreaM2, yearBuilt and participationPercent are left out, with cadastre.error saying why.
A floor area copied from the wrong flat in the block would look exactly as authoritative as a correct one. Leaving it empty is the only version of this that is safe to act on.
Where the address does resolve uniquely, the full record comes back as if the reference had been published.
Market value: deliberately absent
valuation.estimatedMarketValue is never populated, and every record says so in valuation.marketValueNote.
There is no free, programmatically accessible source of Spanish residential market comparables at the granularity this would need. Both candidates were checked against their live endpoints:
- INE's Índice de Precios de Vivienda is published only at national and autonomous-community level, and it is an index (2015 = 100), not a price per square metre. It can age an existing figure forward. It cannot produce one.
- The Ministry's "valor tasado de vivienda libre" series does give euros per square metre per municipality, quarterly. But it is appraisal value rather than market value, it covers only municipalities above 25,000 inhabitants, it ships as a legacy XLS behind an unversioned URL, and it has no sub-municipal breakdown. A flat on a good street and a flat on a bad street two hundred metres away would receive the same number.
Multiplying a municipal appraisal average by a cadastral floor area produces something that looks like a valuation, carries a euro sign, and is wrong by a margin nobody can see. For someone deciding whether to commit a five-figure deposit, that is worse than an empty field.
So the Actor gives you the inputs a real valuation needs — address, cadastral reference, floor area, year, use, coordinates, encumbrances, occupancy — and leaves the comparable to a source you already trust. If you have one, cadastre.builtAreaM2 and valuation.auctionValuePerM2 are the two fields to multiply against.
Everything else in valuation is arithmetic on published figures, reproducible from the source fields kept beside it.
Scoring
There is no single "deal score", on purpose. Weighing a 40% discount against an occupied flat against a surviving mortgage requires a weighting, and any weighting is an opinion dressed as a measurement.
Instead:
discountVsAppraisalPercent— arithmetic, and usually absent. Most judicial auctions publish an appraisal of0,00 €, which this Actor reads as not supplied rather than as a free property.riskFlags— each one traceable to a specific source field. Includesprior-charges-survive-the-sale,property-is-occupied,partial-share-or-bare-ownership(a 50% indiviso share leaves you co-owning with the previous owner),lot-bundles-multiple-assets,inspection-not-possible.missingDataandcompletenessPercent— how much of the picture the sources actually supplied.
Legal design
No natural persons, by construction
The BOE auction portal is a public register. "Public" is not a lawful basis under the GDPR, and republishing a public register at scale is a distinct processing operation from consulting it.
The reference case is the Polish DPA's 2019 fine against Bisnode, which scraped a public business register and was penalised not for the scraping but for failing to discharge the Article 14 duty to inform the data subjects whose data it had collected indirectly. An Actor that emitted debtor names would put its users in exactly that position, and there is no realistic way for a scraper to notify thousands of foreclosed homeowners.
The response is structural, in four layers:
- The parser reads an allowlist. Only labels on an explicit list in
src/privacy.tsare ever read into a variable. A label naming a person is never parsed at all, so it cannot escape through a forgotten code path, a debug log or a future refactor. A field the BOE adds tomorrow defaults to being ignored, not published. - One portal tab is never requested. The "Relacionados" tab (
ver=4) is the only page carrying aNombre+NIFpair. The Actor has no code path that can construct its URL. - Attached documents are never downloaded. The edicts and registry certificates linked from each auction contain full names and tax IDs. The Actor does not fetch them.
- Free text is redacted. Descriptions and encumbrance notes are written by clerks and sometimes name people inline. Names introduced by Spanish legal markers (
D.,Doña,propiedad de,a favor de), DNI/NIE/NIF/CIF in every format, and email addresses are removed. Party nouns (deudor,demandado,titular) are replaced with[PARTY].
For notarial auctions the managing authority's name is the notary's own — a natural person — so it is dropped, keeping the institutional code and contact route.
Addresses and cadastral references are kept. They identify a property, not a person; they are the subject matter of the auction and the primary key of the public cadastre. What is removed is the link from the property to the human being losing it.
The enforcement lives in tests/privacy.test.js. If those tests fail, the Actor is emitting personal data and must not ship.
robots.txt
https://subastas.boe.es/robots.txt is User-agent: * / Disallow: /. The entire portal is disallowed to all crawlers, with no Allow and no Crawl-delay. Every page also returns X-Robots-Tag: noindex, nofollow.
This is stated plainly because you should decide about it rather than discover it later. There is no technical block — no rate limiting, no captcha on the public paths — but robots.txt is a clear expression of the operator's preference, and it is not this Actor's place to pretend otherwise.
What the Actor does about it:
- The crawl is sequential, never parallel.
- The default delay between requests is 1000 ms, and the input schema says so.
- It fetches three pages per auction and one more per extra lot — the minimum needed — rather than mirroring the site.
- It identifies itself honestly in its
User-Agent.
The cadastre's services are a different matter: they are published for programmatic use, their licence states Fees: No conditions apply, and no rate limit is documented anywhere. They are still called sequentially with a 200 ms spacing.
Data protection basics if you run this
You become the controller for whatever you store. The output is designed to keep that boundary clean, but joining a cadastral reference to a named individual in your own systems is your processing, not the Actor's.
What it cannot do
Stated up front rather than discovered on the first run.
- Concluded auctions cannot be searched. The portal's own public search returns an error for the
CA,PCandFSstates — verified against the live site, not assumed. Closed auctions can only be fetched by identifier. - Current bid amounts are not available. The portal shows them only to registered, logged-in users. The Actor does not authenticate.
- Attached documents are not parsed. The edict PDF often holds detail found nowhere else. It also holds names and tax IDs, so it is not fetched.
- No market value. See above.
minDiscountPercentwill exclude nearly everything judicial, because those auctions publish an appraisal of zero. It is there for tax-agency auctions, which usually publish a real one.
Pricing
Pay per event: one charge per property lot returned. Nothing is charged for auctions that fail to parse, and charging happens after the lot has been pushed, so a run that dies mid-crawl never bills for results you did not receive.
Supplying auctionIds skips the search entirely.
Development
npm installnpm run buildnpm test # parser, privacy filter and address resolutionnpm start # reads storage/key_value_stores/default/INPUT.json
The test suite runs against real pages captured from the portal, in tests/fixtures/. Parsing regressions show up there before they show up in production.
src/main.ts entry point, run summaryinput.ts input coercion and validationengine.ts crawl orchestration and pacingbilling.ts pay-per-event chargingprivacy.ts allowlist, redaction, final scrub gatemodel/parse.ts BOE portal HTML parsingsearch.ts search URL and pagination constructionvaluation.ts euro arithmetic and risk flagsproviders/http.ts fetch with bounded retriescatastro.ts cadastre lookups and address resolution
Sources
All free, all public, no keys.
- Portal de Subastas del BOE —
subastas.boe.es - Dirección General del Catastro, free web services —
ovc.catastro.meh.es. Terms - BOE gazette announcement identifiers, linked but not fetched