# Spanish Judicial Auctions, Enriched (BOE + Catastro) (`ernestmarza/spain-judicial-auctions-enriched`) Actor

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.

- **URL**: https://apify.com/ernestmarza/spain-judicial-auctions-enriched.md
- **Developed by:** [Ernest Marzá](https://apify.com/ernestmarza) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $200.00 / 1,000 enriched auction lots

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## 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](#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

```json
{ "province": "Sevilla" }
````

That returns the next 25 open auctions in Sevilla, every property lot in them, each enriched. Everything else has a default.

Narrower:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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:

1. **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"`.

2. **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 queries `Consulta_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`, not `Alicante/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 of `0,00 €`, which this Actor reads as *not supplied* rather than as a free property.
- `riskFlags` — each one traceable to a specific source field. Includes `prior-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`.
- `missingData` and `completenessPercent` — 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:

1. **The parser reads an allowlist.** Only labels on an explicit list in `src/privacy.ts` are 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.
2. **One portal tab is never requested.** The "Relacionados" tab (`ver=4`) is the only page carrying a `Nombre` + `NIF` pair. The Actor has no code path that can construct its URL.
3. **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.
4. **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`, `PC` and `FS` states — 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.
- **`minDiscountPercent` will 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

```bash
npm install
npm run build
npm test          # parser, privacy filter and address resolution
npm 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 summary
  input.ts              input coercion and validation
  engine.ts             crawl orchestration and pacing
  billing.ts            pay-per-event charging
  privacy.ts            allowlist, redaction, final scrub gate
  model/
    parse.ts            BOE portal HTML parsing
    search.ts           search URL and pagination construction
    valuation.ts        euro arithmetic and risk flags
  providers/
    http.ts             fetch with bounded retries
    catastro.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](https://www.catastro.hacienda.gob.es/ws/Webservices_Libres.pdf)
- **BOE gazette** announcement identifiers, linked but not fetched

# Actor input Schema

## `auctionIds` (type: `array`):

Auction identifiers or detail URLs, one per entry. Accepts 'SUB-JA-2026-261745', a full 'detalleSubasta.php?idSub=...' URL, or the short 'subastas.boe.es/ds.php?id=...' permalink the BOE gazette publishes. When this is set the search is skipped entirely and only these auctions are fetched, which is both faster and cheaper.

## `auctionState` (type: `string`):

Which auctions to search for. The BOE portal's public search only supports auctions that are running or about to open; concluded, cancelled and suspended auctions cannot be enumerated and must be requested by identifier above.

## `auctionKinds` (type: `array`):

Which procedures to include. Judicial auctions come from the courts and are the classic distressed-property source; tax-agency auctions come from the AEAT; notarial auctions are run by a notary, usually out-of-court mortgage enforcement. Leave empty for all four.

## `province` (type: `string`):

Spanish province name or its two-digit INE code. Examples: 'Sevilla', 'Barcelona', '41'. Leave empty to search the whole country.

## `locality` (type: `string`):

Matched against the town recorded on the property. Free text, matched by the portal itself.

## `postalCode` (type: `string`):

Exactly five digits.

## `addressContains` (type: `string`):

Free-text fragment matched against the property address by the portal, for example a street name.

## `maxAuctions` (type: `integer`):

How many auctions to fetch in detail. Each auction costs three page loads plus one more per extra lot, and the crawl is deliberately paced, so this is the main control on how long a run takes.

## `maxLots` (type: `integer`):

Hard ceiling on dataset items. One auction can contain several lots and one lot several assets, so this is not the same as the auction count. You are charged per lot returned.

## `requestDelayMs` (type: `integer`):

Pause between page loads. Lowering this speeds the run up and increases the load placed on a government service that has no commercial interest in absorbing it. 1000 ms is the recommended floor.

## `enrichWithCadastre` (type: `boolean`):

Look each property up in the Spanish cadastre's free public web services to add floor area, year built, use, plot area, number of units, coordinates and the official facade photograph. Adds roughly one second per lot. These services return no owner names.

## `onlyWithCadastralReference` (type: `boolean`):

Drop lots that neither publish a cadastral reference nor could be matched to one from their address. Useful when you intend to cross-reference the results against another property dataset.

## `excludePrimaryResidence` (type: `boolean`):

Skip lots the court has flagged as the occupant's habitual home. These carry the longest eviction timelines and the strongest statutory protections for the occupant.

## `minAuctionValue` (type: `integer`):

Lots whose auction value is below this, or which publish no value at all, are dropped.

## `maxAuctionValue` (type: `integer`):

Lots whose auction value is above this, or which publish no value at all, are dropped.

## `minDiscountPercent` (type: `integer`):

Keep only lots whose auction value is at least this far below the published appraisal. Use with care: most judicial auctions publish an appraisal of 0,00 EUR, which this Actor reads as 'not supplied' rather than as a free property, so a discount filter will exclude nearly all of them.

## `includeRawFields` (type: `boolean`):

Attach the label/value pairs exactly as the portal published them, alongside the normalised record. Useful for auditing the parser or for reaching a field this Actor does not model. Personal fields are excluded from this too.

## Actor input object example

```json
{
  "auctionIds": [],
  "auctionState": "open",
  "auctionKinds": [
    "judicial"
  ],
  "province": "Sevilla",
  "maxAuctions": 25,
  "maxLots": 200,
  "requestDelayMs": 1000,
  "enrichWithCadastre": true,
  "onlyWithCadastralReference": false,
  "excludePrimaryResidence": false,
  "includeRawFields": false
}
```

# Actor output Schema

## `lots` (type: `string`):

The full result set: one item per property lot, with auction terms, managing authority, cadastral record, valuation arithmetic and risk flags. Table views are defined for deal screening, location and legal status.

## `lotsCsv` (type: `string`):

The same items flattened to CSV, for spreadsheet use.

## `summary` (type: `string`):

Counts for the run: auctions matched and fetched, lots parsed and returned, how many cadastral lookups resolved, how many personal fields were dropped before parsing, requests made to the portal, and the filters that were applied.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "auctionIds": [],
    "auctionKinds": [
        "judicial"
    ],
    "province": "Sevilla",
    "maxAuctions": 25,
    "maxLots": 200,
    "requestDelayMs": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("ernestmarza/spain-judicial-auctions-enriched").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "auctionIds": [],
    "auctionKinds": ["judicial"],
    "province": "Sevilla",
    "maxAuctions": 25,
    "maxLots": 200,
    "requestDelayMs": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("ernestmarza/spain-judicial-auctions-enriched").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "auctionIds": [],
  "auctionKinds": [
    "judicial"
  ],
  "province": "Sevilla",
  "maxAuctions": 25,
  "maxLots": 200,
  "requestDelayMs": 1000
}' |
apify call ernestmarza/spain-judicial-auctions-enriched --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ernestmarza/spain-judicial-auctions-enriched",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Spanish Judicial Auctions, Enriched (BOE + Catastro)",
        "description": "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.",
        "version": "0.1",
        "x-build-id": "U80yA0RrjDYNenEJE"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ernestmarza~spain-judicial-auctions-enriched/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ernestmarza-spain-judicial-auctions-enriched",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ernestmarza~spain-judicial-auctions-enriched/runs": {
            "post": {
                "operationId": "runs-sync-ernestmarza-spain-judicial-auctions-enriched",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ernestmarza~spain-judicial-auctions-enriched/run-sync": {
            "post": {
                "operationId": "run-sync-ernestmarza-spain-judicial-auctions-enriched",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "auctionIds": {
                        "title": "Specific auctions",
                        "type": "array",
                        "description": "Auction identifiers or detail URLs, one per entry. Accepts 'SUB-JA-2026-261745', a full 'detalleSubasta.php?idSub=...' URL, or the short 'subastas.boe.es/ds.php?id=...' permalink the BOE gazette publishes. When this is set the search is skipped entirely and only these auctions are fetched, which is both faster and cheaper.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "auctionState": {
                        "title": "Auction state",
                        "enum": [
                            "open",
                            "upcoming",
                            "any"
                        ],
                        "type": "string",
                        "description": "Which auctions to search for. The BOE portal's public search only supports auctions that are running or about to open; concluded, cancelled and suspended auctions cannot be enumerated and must be requested by identifier above.",
                        "default": "open"
                    },
                    "auctionKinds": {
                        "title": "Auction types",
                        "type": "array",
                        "description": "Which procedures to include. Judicial auctions come from the courts and are the classic distressed-property source; tax-agency auctions come from the AEAT; notarial auctions are run by a notary, usually out-of-court mortgage enforcement. Leave empty for all four.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "judicial",
                                "tax-agency",
                                "notarial",
                                "administrative"
                            ],
                            "enumTitles": [
                                "Judicial (court-ordered)",
                                "Tax agency (AEAT)",
                                "Notarial",
                                "Other administrative bodies"
                            ]
                        },
                        "default": []
                    },
                    "province": {
                        "title": "Province",
                        "type": "string",
                        "description": "Spanish province name or its two-digit INE code. Examples: 'Sevilla', 'Barcelona', '41'. Leave empty to search the whole country."
                    },
                    "locality": {
                        "title": "Town or city",
                        "type": "string",
                        "description": "Matched against the town recorded on the property. Free text, matched by the portal itself."
                    },
                    "postalCode": {
                        "title": "Postal code",
                        "type": "string",
                        "description": "Exactly five digits."
                    },
                    "addressContains": {
                        "title": "Address contains",
                        "type": "string",
                        "description": "Free-text fragment matched against the property address by the portal, for example a street name."
                    },
                    "maxAuctions": {
                        "title": "Maximum auctions to open",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "How many auctions to fetch in detail. Each auction costs three page loads plus one more per extra lot, and the crawl is deliberately paced, so this is the main control on how long a run takes."
                    },
                    "maxLots": {
                        "title": "Maximum lots to return",
                        "minimum": 1,
                        "maximum": 20000,
                        "type": "integer",
                        "description": "Hard ceiling on dataset items. One auction can contain several lots and one lot several assets, so this is not the same as the auction count. You are charged per lot returned."
                    },
                    "requestDelayMs": {
                        "title": "Delay between requests (ms)",
                        "minimum": 250,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Pause between page loads. Lowering this speeds the run up and increases the load placed on a government service that has no commercial interest in absorbing it. 1000 ms is the recommended floor."
                    },
                    "enrichWithCadastre": {
                        "title": "Enrich with the cadastre",
                        "type": "boolean",
                        "description": "Look each property up in the Spanish cadastre's free public web services to add floor area, year built, use, plot area, number of units, coordinates and the official facade photograph. Adds roughly one second per lot. These services return no owner names.",
                        "default": true
                    },
                    "onlyWithCadastralReference": {
                        "title": "Only lots with a cadastral reference",
                        "type": "boolean",
                        "description": "Drop lots that neither publish a cadastral reference nor could be matched to one from their address. Useful when you intend to cross-reference the results against another property dataset.",
                        "default": false
                    },
                    "excludePrimaryResidence": {
                        "title": "Exclude primary residences",
                        "type": "boolean",
                        "description": "Skip lots the court has flagged as the occupant's habitual home. These carry the longest eviction timelines and the strongest statutory protections for the occupant.",
                        "default": false
                    },
                    "minAuctionValue": {
                        "title": "Minimum auction value (EUR)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Lots whose auction value is below this, or which publish no value at all, are dropped."
                    },
                    "maxAuctionValue": {
                        "title": "Maximum auction value (EUR)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Lots whose auction value is above this, or which publish no value at all, are dropped."
                    },
                    "minDiscountPercent": {
                        "title": "Minimum discount vs appraisal (%)",
                        "minimum": -100,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Keep only lots whose auction value is at least this far below the published appraisal. Use with care: most judicial auctions publish an appraisal of 0,00 EUR, which this Actor reads as 'not supplied' rather than as a free property, so a discount filter will exclude nearly all of them."
                    },
                    "includeRawFields": {
                        "title": "Include raw portal fields",
                        "type": "boolean",
                        "description": "Attach the label/value pairs exactly as the portal published them, alongside the normalised record. Useful for auditing the parser or for reaching a field this Actor does not model. Personal fields are excluded from this too.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
