# Airbnb & Booking Spain Tourist License Checker (`nice_dev/airbnb-spain-license-checker`) Actor

Check the tourist licence number of Airbnb listings and Booking.com properties in Spain against the official regional registers (Catalonia, Andalusia, Valencia, Mallorca, Canary Islands, Madrid): verdict, registered holder, floor, door, authorised places — plus listing and host details.

- **URL**: https://apify.com/nice\_dev/airbnb-spain-license-checker.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Travel, Real estate, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.39 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 🏠 What is Airbnb & Booking Spain Tourist License Checker?

**Airbnb & Booking Spain Tourist License Checker** checks the **tourist licence number** that each Airbnb listing and each Booking.com property in Spain displays against the **official register of its region** — Catalonia, Andalusia, the Valencian Community, Mallorca, the Canary Islands and the Community of Madrid — and tells you whether it is **valid, never issued, withdrawn, used for another property or for more guests than allowed**. Each result also gives what the register knows (**registered holder, address, floor, door, cadastral reference, authorised places**) and the listing's own details (host, business details of professional hosts, capacity, rating, amenities, price).

Type a **place** (`Barcelona, Spain`) and choose where to search it — **Airbnb, Booking.com or both** — draw a **map area**, paste **Airbnb URLs**, **listing IDs** or **Booking.com URLs** — or just give **licence numbers** — click **Start**, and download the results in JSON, CSV or Excel. No login, nothing to set up. Built for city councils, tourism inspectors, property managers, landlords' associations, investors and journalists.

### 📋 What data can you extract from Airbnb, Booking.com and the Spanish tourist registers?

One item per listing checked (or per licence number given alone), 109 fields:

| Category | What you get |
| --- | --- |
| ✅ **Verdict** | valid, never issued, not in the register, other property, over capacity, national number mismatch, exempt claimed… with the reasons and one plain sentence |
| 🪪 **Licence numbers** | the regional and national (NRUA) numbers shown on the listing, the issuing authority, the exemption claimed, other listings of the run showing the same number — on Airbnb or on Booking.com (the same home listed on both shows up there) |
| 🏛️ **Official register** | registered holder and its tax ID (companies' CIF; the operator in Mallorca with its NIF / NIE), address, floor, door, postal code, municipality, cadastral reference, registration date, authorised places, bedrooms, surface, position and distance to the listing, source and date of the register |
| 🏠 **Listing** | title, type of place, city, position, guests, bedrooms, beds, bathrooms, rating and sub-ratings, badges, highlights, house rules, where you'll sleep, description, amenities, photos; on Booking.com: street address, every licence number and sentence shown, professional or private host |
| 👤 **Host** | name, profile link, business or individual, verified, years hosting, response rate and time, rating, reviews, co-hosts, how many listings they manage |
| 🏢 **Business host details** | business name, business registry, tax identification number, e-mail, phone and address that professional hosts must show in the EU |
| 💶 **Price** | total price of the stay, price per night, price breakdown, dates (from the search results) |

Every field, with an example, is listed in the **Output** section below.

### ✅ Why use Airbnb & Booking Spain Tourist License Checker?

- 🏛️ **The official registers, not a format check**: each number is looked up in the open data of its regional government, downloaded fresh at each run.
- 🔎 **Finds what a glance cannot**: numbers higher than any number ever issued, numbers of another region, numbers of another flat or town and listings taking more guests than the licence allows (where the register gives the position and the places: see **What the registers can and cannot say**), national numbers quoting another licence.
- 🗂️ **Seven ways in, in one run**: places (on Airbnb, Booking.com or both), map areas, search URLs, listing URLs, listing IDs, Booking.com URLs and licence numbers alone.
- 🏨 **Airbnb and Booking.com side by side**: the same registers and the same verdicts for both sites; a licence number shown by listings of both sites is flagged in `numberSharedWith`; Booking.com properties that say they need no licence are flagged `exempt_claimed`.
- 🗺️ **Whole cities**: Airbnb shows 270 listings per search; a full area is cut into smaller ones automatically to reach the others.
- 🏢 **Professional hosts identified**: business name, tax ID, e-mail and phone of the hosts who declare themselves as a business.
- 🔔 **Monitoring built in**: tick **Only new listings**, schedule the Actor, and each run returns (and charges) only the listings it has never delivered before.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to check Airbnb tourist licences in Spain

1. Create a free Apify account.
2. Open **Airbnb & Booking Spain Tourist License Checker** and type a **Place** (e.g. `Barcelona, Spain`).
3. Choose the **Sites searched** for your places: Airbnb (default), Booking.com, or both.
4. Or paste **Airbnb URLs** (search results or single listings), **listing IDs**, **Booking.com URLs**, **map areas**, or **licence numbers to check** without any listing.
5. Set **Max results** (100 by default, 0 = no limit) — and keep only the suspicious listings with **Keep only these verdicts** — then click **Start**.
6. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to check Airbnb licences?

This Actor uses **pay per event** pricing: a price per result (a listing checked, or a licence number checked), a small fee per run start, a fee per **host profile** (**Host's listing count**, one per host whose profile reaches your results, not per listing), a fee per **Booking.com property** checked (`booking-listing`, on top of the result, charged only once the **Pricing** tab lists it) and a lower fee for each listing a filter of the Actor drops — `filter-check` when the search results are enough to drop it (minimum rating, host type, excluded words), `detail-filter-check` when its page and register had to be read (verdicts, regions; also a licence number given alone that these filters drop). A listing you keep costs its normal price: filtering never costs more than taking everything. With a maximum cost per run, the listings already kept are paid first: the filter checks only spend what they leave. The prices are shown on the **Pricing** tab of this page. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

A place, only the listings whose licence does not match the register:

```json
{
    "location": "Barcelona, Spain",
    "maxItems": 200,
    "onlyVerdicts": ["never_issued", "not_in_register", "other_property", "over_capacity", "national_mismatch"]
}
```

Several places and a map area, a cap per search, only the listings not delivered before:

```json
{
    "locations": ["Málaga", "Marbella"],
    "boundingBoxes": ["41.37, 2.15, 41.40, 2.19"],
    "maxItems": 300,
    "maxItemsPerQuery": 100,
    "onlyNew": true,
    "memoryName": "costa-del-sol"
}
```

Both sites for the same places, Booking.com apartments and holiday homes only:

```json
{
    "locations": ["Valencia", "Málaga"],
    "sources": ["airbnb", "booking"],
    "bookingPropertyTypes": ["apartment", "holiday_home"],
    "maxItems": 400,
    "maxItemsPerQuery": 100
}
```

Your own URLs, listing IDs, Booking.com URLs, and licence numbers alone:

```json
{
    "startUrls": [
        { "url": "https://www.airbnb.com/s/Palma--Spain/homes?adults=2" },
        { "url": "https://www.airbnb.es/rooms/846093318788581404" }
    ],
    "listingIds": ["16592830"],
    "bookingUrls": ["https://www.booking.com/hotel/es/la-roqueta.html"],
    "licenseNumbers": ["HUTB-064079", "VFT/MA/48725", "CV-VUT0052409-V", "ETV/9960"],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `startUrls` | Airbnb search results pages (filters kept, pagination and map splitting automatic) or single listing pages, any Airbnb domain. |
| `listingIds` | Airbnb listing IDs alone (the digits after `/rooms/`). Checked in the order given and before any search: with `maxItems`, the first ones are the ones saved. |
| `bookingUrls` | Booking.com property pages (e.g. `https://www.booking.com/hotel/es/la-roqueta.html`, any language version), checked in the order given, like listing IDs. |
| `location`, `locations` | Places as typed in the site's search box (e.g. `Barcelona, Spain`, `Málaga`); one search each and per site. |
| `sources` | Sites the places are searched on: `airbnb` (default), `booking`, or both. Airbnb URLs, IDs and map areas always go to Airbnb, Booking.com URLs to Booking.com. |
| `boundingBoxes` | Map rectangles `south, west, north, east` (e.g. `41.37, 2.15, 41.40, 2.19`): the surest way to cover a whole city. |
| `licenseNumbers` | Licence numbers checked against the registers without any listing (e.g. `HUTB-064079`, `VFT/MA/48725`, `ETV/9960`). One result per licence: two spellings of the same number (`HUTB-064079`, `hutb 64079`) are checked once. |
| `maxItems` | Stop after this many results for the whole run (`0` = unlimited). |
| `maxItemsPerQuery` | Cap for EACH search (place and site, map area or search URL). 0 = no per-search cap — except when the run searches both sites (places on both, or a place on Booking.com next to an Airbnb map area or search URL): `maxItems` (less the licence numbers checked alone) is then shared evenly between the searches. |
| `splitMap` | Cut a full Airbnb search (270 listings) into smaller map areas, and read a Booking.com search over 1,000 properties district by district, to reach the others (default on). |
| `checkIn`, `checkOut`, `adults`, `children`, `infants`, `pets` | Travel dates and guests sent to Airbnb's search (e.g. `2026-10-12`, today or later in Spain); the price of the stay then follows them, and `checkIn` / `checkOut` of each listing are these dates. |
| `currency`, `priceMin`, `priceMax` | Currency of the prices (`EUR`, `USD`, `GBP`; a pasted search URL keeps its own currency, and each row names it) and price per night bounds. |
| `roomTypes` | Airbnb's type of place: `entire_home`, `private_room`, `shared_room`, `hotel_room`. |
| `propertyTypes` | Airbnb's property type: `house`, `apartment`, `guesthouse`, `hotel`. |
| `minBedrooms`, `minBeds`, `minBathrooms`, `superhostOnly` | Airbnb's own filters: at least this many rooms, Superhosts only. |
| `bookingPropertyTypes` | Kinds of Booking.com properties searched: `apartment`, `holiday_home`, `villa` (default), also `chalet`, `country_house`, `farm_stay`, `guesthouse`, `homestay`, `bed_and_breakfast`, `condo_hotel`, `hotel`, `hostel`. |
| `onlyVerdicts` | Keep only the listings with these verdicts (e.g. `never_issued`, `not_in_register`, `over_capacity`). |
| `regions` | Keep only the listings of these regions (`catalonia`, `andalusia`, `valencia`, `mallorca`, `canary_islands`, `madrid`, `other`). |
| `hostType` | `any`, `business` or `individual` hosts (Booking.com: professional or private, as the host declared). |
| `minRating`, `excludeKeywords` | Minimum rating out of 5 (Booking.com scores out of 10 are halved); words that drop a listing whose title contains them. |
| `gpsToleranceKm` | Distance above which a register record is another property (`1.5` km by default: Airbnb blurs positions). |
| `includeHostProfile` | Read each Airbnb host's profile once for the number of listings they manage (default off: tick it to get the host's listing count and listing IDs, charged per host profile). |
| `onlyNew`, `memoryName`, `resetState` | Monitoring: only the listings never delivered under this memory name; `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A real result (shortened: some fields and list entries left out):

```json
{
    "id": "846093318788581404",
    "source": "airbnb_listing",
    "url": "https://www.airbnb.com/rooms/846093318788581404",
    "title": "Cozy apartaments Plaza España, 3 bedrooms",
    "verdict": "valid",
    "verdictReasons": [],
    "verdictNote": "HUTB-064079 is in the Catalan register (Barcelona, floor 3, door 1).",
    "isSuspicious": false,
    "region": "catalonia",
    "registrationText": "Spain – National registration number | ESFCTU0AD10806700043751100000000000000HUTB-064079-092 | Barcelona – …",
    "licenseAuthority": "Barcelona",
    "regionalNumber": "HUTB-064079",
    "regionalNumberNormalized": "HUTB-064079",
    "nationalNumber": "ESFCTU0AD10806700043751100000000000000HUTB-064079-092",
    "nationalNumberType": "tourist",
    "nationalNumberQuotes": "HUTB-064079",
    "licenseNumbersShown": [],
    "licensePhrases": [],
    "numberSharedWith": [],
    "registerFound": true,
    "registerNumber": "HUTB-064079",
    "registerType": "Habitatges d'ús turístic",
    "registerHolder": "INMOBILIARIA GALLARDO SL",
    "registerHolderTaxId": "B08109563",
    "registerAddress": "Carrer TARRAGONA 84 esc. A",
    "registerFloor": "3",
    "registerDoor": "1",
    "registerPostalCode": "08015",
    "registerMunicipality": "Barcelona",
    "registerPlaces": 5,
    "distanceKm": 0.1,
    "registerHighestNumber": 79999,
    "registerSource": "Generalitat de Catalunya — Registre de Turisme de Catalunya (t2h3-cgys)",
    "registerUpdatedAt": "2026-07-31",
    "propertyType": "Entire serviced apartment",
    "roomType": "Entire home/apt",
    "city": "Barcelona",
    "latitude": 41.37953,
    "longitude": 2.1466,
    "personCapacity": 5,
    "bedrooms": 3,
    "rating": 4.73,
    "reviewsCount": 3145,
    "badges": [],
    "highlights": [
        "Top rated by guests from Ireland",
        "Self check-in"
    ],
    "houseRules": [
        "Check-in after 16:00",
        "Checkout before 11:00"
    ],
    "sleepingArrangements": [
        "Bedroom 1: 1 double bed",
        "Bedroom 2: 1 single bed"
    ],
    "amenities": [
        "Hairdryer",
        "Shampoo"
    ],
    "images": [
        "https://a0.muscache.com/im/pictures/miso/Hosting-846093318788581404/original/d5cf8e54-a36d-4d1f-9240-d858df276b3c.jpeg",
        "https://a0.muscache.com/im/pictures/miso/Hosting-846093318788581404/original/8e1af549-52fd-4f66-9bc7-9e44f7078e40.jpeg"
    ],
    "hostName": "Arrivo Apartments",
    "hostType": "business",
    "coHosts": [],
    "hostListingsCount": 1,
    "hostListingIds": [
        "846093318788581404"
    ],
    "businessName": "ASPASION SHORT RENTALS",
    "businessEmail": "arrivostay@aspasios.com",
    "businessPhone": "+34 635 79 03 53",
    "priceDetails": [],
    "scrapedAt": "2026-09-25T22:13:53.262Z"
}
```

A Booking.com property (shortened: the Airbnb-only fields are `null` or empty):

```json
{
    "id": "booking-2652492",
    "source": "booking_listing",
    "url": "https://www.booking.com/hotel/es/la-roqueta.html",
    "title": "La Roqueta completo en pleno centro",
    "verdict": "valid",
    "verdictReasons": [],
    "verdictNote": "CV-VUT0041701-V is in the Valencian register (VALÈNCIA, floor 02, door 02). The register gives no position for it: the distance to the listing was not checked. Booking does not give the number of guests: it was not compared with the 4 authorised places.",
    "isSuspicious": false,
    "region": "valencia",
    "registrationText": "License number: ESFCTU0000460630002825710000000000000CV-VUT0041701-V7, VT-41701-V",
    "regionalNumber": "VT-41701-V",
    "regionalNumberNormalized": "VUT41701-V",
    "nationalNumber": "ESFCTU0000460630002825710000000000000CV-VUT0041701-V7",
    "licenseNumbersShown": ["ESFCTU0000460630002825710000000000000CV-VUT0041701-V7", "VT-41701-V"],
    "licensePhrases": ["License number: ESFCTU0000460630002825710000000000000CV-VUT0041701-V7, VT-41701-V"],
    "numberSharedWith": [],
    "registerFound": true,
    "registerNumber": "CV-VUT0041701-V",
    "registerFloor": "02",
    "registerDoor": "02",
    "registerMunicipality": "VALÈNCIA",
    "registerPlaces": 4,
    "propertyType": "Apartment",
    "city": "València",
    "address": "Carrer de Cervantes 6, 2",
    "latitude": 39.4682238806482,
    "longitude": -0.381170315667987,
    "rating": 4.5,
    "reviewsCount": 113,
    "hostType": "individual",
    "isTrader": false,
    "bookingId": "2652492",
    "bookingPageName": "la-roqueta",
    "searchUrl": "https://www.booking.com/searchresults.html?ss=Valencia&nflt=ht_id%3D201%3Bht_id%3D220%3Bht_id%3D213&page=1",
    "scrapedAt": "2026-09-26T12:44:17.918Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel. The **Overview**, **Register record** and **Hosts and business details** views of the Output tab show the main columns.

#### All 109 fields

| Fields | What you get |
| --- | --- |
| `id`, `source`, `url`, `title` | **Result**: Airbnb id, `booking-` + the Booking.com property id (or the licence number given alone), `airbnb_listing` / `booking_listing` / `license_number`, listing link, title |
| `verdict`, `verdictReasons`, `verdictNote`, `isSuspicious`, `region` | **Verdict**: see the table of verdicts below; the reasons found; one sentence; the region |
| `registrationText`, `licenseAuthority`, `regionalNumber`, `regionalNumberNormalized`, `nationalNumber`, `nationalNumberType`, `nationalNumberQuotes`, `exemption`, `numberSharedWith` | **Licence numbers**: the block as shown, the authority, the regional number as written and the key it is looked up under (the register's own spelling is `registerNumber`), the national number, `tourist` / `non_tourist`, the regional number it quotes (without its check digit), the exemption claimed, the other listings of the run (Airbnb or Booking.com) with the same number |
| `licenseNumbersShown`, `licensePhrases` | **Booking.com**: every licence number the property shows (a building of flats may show several), and the sentences shown with them |
| `registerFound`, `registerNumber`, `registerType`, `registerHolder`, `registerHolderTaxId`, `registerName` | **Register**: found or not, the number, the category, the holder (companies with their CIF, the operator in Mallorca with its NIF / NIE), the registered name |
| `registerAddress`, `registerFloor`, `registerDoor`, `registerPostalCode`, `registerMunicipality`, `registerCadastralRef` | **Where the licence is**: `Carrer TARRAGONA 84`, `3`, `1`, `08015`, `Barcelona`, cadastral reference |
| `registerDate`, `registerPlaces`, `registerBedrooms`, `registerSurfaceM2` | registration date, authorised places, bedrooms, surface in m² |
| `registerLatitude`, `registerLongitude`, `distanceKm`, `registerHighestNumber`, `registerSource`, `registerUpdatedAt` | position of the registered property, km to the listing, highest number issued in its numbering (a Catalan HUT or LL number: every territory), source and date of the register |
| `propertyType`, `roomType`, `city`, `address`, `latitude`, `longitude`, `locationText`, `locationDescription` | **Listing**: `Entire serviced apartment`, `Entire home/apt`, city, street address (Booking.com), position (blurred by Airbnb), area, the host's text about the neighbourhood |
| `personCapacity`, `bedrooms`, `beds`, `bathrooms`, `sleepingArrangements` | guests, rooms, where you'll sleep |
| `rating`, `reviewsCount`, `ratingAccuracy`, `ratingCheckin`, `ratingCleanliness`, `ratingCommunication`, `ratingLocation`, `ratingValue` | ratings out of 5 (Booking.com's score out of 10, halved) and number of reviews |
| `isSuperhost`, `isGuestFavorite`, `isNewListing`, `homeTier`, `badges`, `highlights` | Superhost, Guest favourite, new listing, tier, search badges, highlights |
| `description`, `descriptionLanguage`, `houseRules`, `amenities`, `images` | full description, its language, house rules, amenities, photo links |
| `hostId`, `hostName`, `hostUrl`, `hostType`, `isTrader`, `hostIsVerified`, `hostYearsHosting` | **Host**: id, name, profile link, `business` / `individual`, professional host on Booking.com, verified, years hosting |
| `hostResponseRate`, `hostResponseTime`, `hostRating`, `hostReviewsCount`, `hostAbout`, `hostPictureUrl`, `hostLocation`, `coHosts` | response rate and time, rating and reviews over all their listings, presentation, photo, where they live, co-hosts |
| `hostListingsCount`, `hostListingIds` | how many listings they manage, the IDs their profile shows (10 at most) |
| `businessName`, `businessRegistry`, `businessTaxId`, `businessEmail`, `businessPhone`, `businessAddress` | **Business host details** (EU rule for professional hosts) |
| `price`, `currency`, `pricePerNight`, `priceText`, `priceDetails`, `priceNights`, `checkIn`, `checkOut` | **Price** from the search results: total, currency, per night, as shown, breakdown, nights, dates |
| `bookingId`, `bookingPageName` | **Booking.com**: the property id and the name of its page |
| `searchUrl`, `scrapedAt` | the search the listing was found by, time of the check |

### 💡 Tips

#### What each verdict means

When a listing has several problems, its verdict is the highest one in this table, and `verdictReasons` lists them all.

| Verdict | Meaning |
| --- | --- |
| `valid` | The number is in the register of its region; the registered property is near the listing and the guests fit the authorised places, when the register gives them (otherwise `verdictNote` says what could not be checked). |
| `never_issued` | Not in the register, and higher than any number its numbering has reached, even counting what it may have issued since the register's copy (a Catalan family such as HUT or LL is numbered across all its territories): an invented number. |
| `not_in_register` | Well-formed but not in the register: withdrawn, or invented below the highest number (the open registers list active licences only) — or just above it, issued after the register's copy (`verdictNote` says so). |
| `other_property` | The number belongs to a property far from the listing (more than **Distance tolerance**), or to another region. |
| `over_capacity` | The listing takes more guests than the licence allows. |
| `national_mismatch` | The national (NRUA) number quotes another regional number, or is not a real one. |
| `unknown_format` | The text shown is not a licence number of the region (a declaration file number, free text). |
| `no_number` | The listing shows no registration number, or only a national number without any regional number in it. |
| `exempt_claimed` | The listing says it is exempt (hotel, hostel, seasonal rental…; on Booking.com, that it needs no short-term rental licence). |
| `other_register` | A hotel or apartment-block number of a register this Actor does not read (the Mallorca, Valencian and Canary Islands files list tourist homes only). |
| `region_not_covered` | Outside the six regions checked (e.g. Ibiza, the Basque Country, Murcia), or outside Spain (a French or Portuguese home next to the border): the number is given, not checked. |
| `register_unavailable` | The register could not be downloaded in this run (or what came back was not the register: a web page, a file without any licence number). |

#### How to cover a whole city

Airbnb shows at most 270 listings per search. With **Split full areas** on (default), a full search is cut into 4 smaller map areas, again and again, until each shows everything. A place's area is Airbnb's own map of it, which does not always cover the whole town: add **Map areas** (`boundingBoxes`) to be sure.

Booking.com shows at most 1,000 properties per search. With **Split full areas** on, a bigger search is also read district by district (Booking.com's own districts, which overlap and do not cover every street: 87 % of Barcelona's 2,723 apartments with half of its districts, September 2026). A place is Booking.com's own match for what you type: prefer a town (`Palma de Mallorca`, `Sóller`) to an island or a region.

#### Airbnb and Booking.com

Tick both sites in **Sites searched** to check the same places on both: every place gives one Airbnb search and one Booking.com search, each with its own **Max listings per search** — left at 0, **Max results** (less the licence numbers checked alone) is shared evenly between them. A Booking.com property goes through the same registers and verdicts as an Airbnb listing, with what Booking.com shows instead of Airbnb's page:

- **Several numbers**: a building of flats may show one licence per flat. The first regional number is checked; all are in `licenseNumbersShown`, and `verdictNote` says so.
- **No licence needed**: a property that says it needs no short-term rental licence, or shows `Exempt` / `Exento` in place of a number, gets `exempt_claimed` (in Valencia, 1 apartment in 5 said so in September 2026).
- **A check digit too many** (on Airbnb listings too): a regional number copied from the national one keeps its last digit (`VFT/MA/279277` for `VFT/MA/27927`): checked without it, and `verdictNote` says so — only when the national number shown ends with that digit, words typed after it aside (a Valencian one puts it after the province: its numbers are never shortened).
- **No number of guests**: Booking.com does not give it, so `over_capacity` is never raised for a Booking.com property.
- **No host profile**: the host's name, profile and business details are Airbnb's; Booking.com gives only whether the host is a professional (`isTrader`).

Booking.com properties are far more often listed with a valid number than Airbnb listings (in Barcelona, all 61 numbers read in September 2026 were in the register, against 58 of 83 on Airbnb).

#### How to reduce costs

The levers are `maxItems`, `maxItemsPerQuery`, **Keep only these verdicts** (a listing dropped costs only the filter fee), leaving **Host's listing count** off (the default), `onlyNew` for recurring runs (you never pay twice for the same listing), and searching only the site you need.

#### Several searches in one run

Fill `locations` and / or `boundingBoxes`, or paste several search URLs: one search each (up to 100 places, 100 map areas and 1,000 URLs per run). A listing found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap.

#### Monitoring: only the new listings

Tick **Only new listings** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the listings already delivered: they are not saved, not charged, and their page is not even opened. The memory lives in a named key-value store of your account (`airbnb-spain-license-checker-seen`, up to 150,000 listings per memory name) and is only updated with listings that really reached the dataset. Give each schedule its own `memoryName` (**Memory name**), and tick `resetState` once to start over. A listing whose licence changed is not returned again, even if you paste its URL or ID: to re-check known listings, give their IDs in a run without **Only new listings** (or with another `memoryName` (**Memory name**)).

#### What the registers can and cannot say

The open registers list the active licences: a number that is absent was withdrawn or never registered, and the register does not say which (`not_in_register`); a number above the highest ever issued was never issued (`never_issued`) — unless it is close enough above to have been issued since the register's copy (the Catalan register is published monthly, sometimes late): it is then `not_in_register`, and `verdictNote` gives the copy's date. Positions are blurred by Airbnb by a few hundred metres: `other_property` is only raised past **Distance tolerance**. The Community of Madrid gives few numbers on Airbnb (many listings claim a seasonal-rental exemption). The national register (NRUA) is compared with the regional number it quotes; it is not checked on its own.

The registers do not all say the same things. A number of another flat or town is found only where the register gives a position, too many guests only where it gives the places (share of the records, registers of September 2026):

| Register | Position (distance check) | Authorised places (guests check) | Holder named | Registration date |
| --- | --- | --- | --- | --- |
| Catalonia | Barcelona city only (1 tourist home in 10) | 1 tourist home in 3 | companies only (1 in 5): people are masked | no |
| Andalusia | 94 % | all | 17 % | yes |
| Valencian Community | no | all | no | yes |
| Mallorca | 1 in 2 | 97 % | 91 % | yes |
| Canary Islands | 2 in 3 | all | no | no |
| Community of Madrid | no | no | no | no |

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per Airbnb listing or Booking.com property checked (or per licence number). Actor id: `nice_dev/airbnb-spain-license-checker`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/airbnb-spain-license-checker`.

Smallest input, for a cheap first call:

```json
{
    "licenseNumbers": ["HUTB-064079"],
    "location": "Sóller, Mallorca",
    "maxItems": 5
}
```

Key output fields: `url`, `verdict`, `verdictNote`, `regionalNumber`, `registerHolder`, `registerPlaces`, `personCapacity`, `hostName`.

Cost: see the prices on the Pricing tab. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Airbnb and Booking.com and use the registers?

The Actor only reads what Airbnb and Booking.com show publicly to any anonymous visitor, and the open data the regional governments publish for reuse (Catalonia, Andalusia, the Valencian Community, Mallorca, the Canary Islands, the Community of Madrid; each result names its source and date, as their licences ask). It logs in to nothing. Results can contain personal data (host names, the holder named by the Andalusia register, the operator named by the Mallorca register with the NIF / NIE that register publishes), which is protected by GDPR: do not store it without a legitimate reason. You are responsible for using the data in compliance with Airbnb's and Booking.com's Terms of Service and applicable law. This Actor is not affiliated with Airbnb, Booking.com or any Spanish administration.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Is the data safe to open in Excel or to show on a web page?

Titles and descriptions are the hosts' own words, copied as they are; the register fields are copied as each register writes them. A text can begin with `-`, `+`, `=` or `@` (a phone number starts with `+`, a floor in the Catalan register can be `-1` or `+1`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number, and a postal code such as `08015` loses its first `0`. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- The price comes from Airbnb's search results: listings given by URL or ID, and Booking.com properties, have none.
- A Booking.com property pasted by URL gets its position, address and photo from one more request: if it fails, the property is still checked, and `verdictNote` says the distance could not be.
- The business details are shown by Airbnb only for the hosts who declare themselves as a business.
- A host profile shows 10 of its listings at most: `hostListingsCount` is the full count, `hostListingIds` the first ones.
- `onlyNew` remembers listing ids, not their content: a listing whose licence changed is not returned again.
- Ibiza, Menorca, the regions outside the six covered and the homes outside Spain get `region_not_covered`. The region of a listing without Airbnb's label comes from its position, read against the limits of the autonomous communities (© Instituto Geográfico Nacional, CC BY 4.0); a home a kilometre or so from a regional limit can land on the wrong side, and one across the French or Portuguese border is told apart by its page (Airbnb) or its address (Booking.com).

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options). At the pace measured on the platform (1,000 listings in under 10 minutes), an hour holds several thousand listings.

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the listings already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved once a minute: resurrect the stopped run and the listings it had saved meanwhile join the memory; leave it stopped for good, and the next run may return up to a minute of them once more.

#### Something doesn't work?

The last line of the log counts the results saved, filtered out and no longer on Airbnb or Booking.com (removed while the run was reading them), and the requests that failed after every retry. Those requests and the removed listings are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause. A register that cannot be downloaded does not fail the run: its listings get `register_unavailable`. A register is downloaded when the first listing of its region comes (Andalusia's takes about 80 seconds): meanwhile the log shows `… waits for a register still downloading: read again` for the listings of that region — they are read again once it is there, nothing is lost or charged twice.

If Airbnb or Booking.com changes its pages, you are told instead of paying for blank rows. A results page that gives no listing the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No listings found". If the first 20 listings read all lack their title, position, guests, room type, host, photos or registration block (hotels aside) — on Booking.com: their title, position, registration block (hotels and guest houses aside), property type or host type — the run saves nothing more, stops and fails, and its last message names the missing field: at most those first listings are charged. A listing a filter drops because such a field is missing counts among those 20. If Booking.com's API no longer accepts the Actor's request (a field it renamed), or no longer answers at its address, the run stops at once and fails with that reason, instead of trying every property again.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `startUrls` (type: `array`):

Airbnb listing URLs (`https://www.airbnb.com/rooms/12345`, any Airbnb domain such as airbnb.es) and/or Airbnb search URLs (`https://www.airbnb.com/s/Barcelona--Spain/homes`, map searches included: their filters are kept, pagination and map splitting are automatic). Max 1 000 URLs.

## `listingIds` (type: `array`):

Airbnb listing IDs alone (the digits after `/rooms/`), one per line — checked in this order, before any search: with a result limit, the first ones are the ones saved. Max 1 000.

## `bookingUrls` (type: `array`):

Booking.com property pages to check, e.g. `https://www.booking.com/hotel/es/la-roqueta.html` (any language version). Read whatever Sites searched says.

## `location` (type: `string`):

A place in Spain, as typed in Airbnb's or Booking.com's search box (e.g. `Barcelona, Spain`, `Málaga`, `Palma de Mallorca`), searched on the **Sites searched**. Each listing found is opened and its licence checked.

## `locations` (type: `array`):

Several places in one run (one search each and per site, max 100). Added to **Place**; a listing found by several searches is saved once.

## `sources` (type: `array`):

The sites each place (Place, More places) is searched on: Airbnb, Booking.com, or both (one search per place and site, each with its own Max listings per search). Airbnb URLs, listing IDs and map areas always go to Airbnb, Booking.com URLs to Booking.com.

## `boundingBoxes` (type: `array`):

Map rectangles to search, one per line as `south latitude, west longitude, north latitude, east longitude` (e.g. `41.37, 2.15, 41.40, 2.19` for central Barcelona). The surest way to cover a whole city: Airbnb returns at most 270 listings per search, and a full area is split into smaller ones automatically.

## `licenseNumbers` (type: `array`):

Tourist licence numbers checked against the registers without any Airbnb listing (e.g. `HUTB-064079`, `VFT/MA/48725`, `CV-VUT0052409-V`, `ETV/9960`, `VV-35-2-0001234`, `VT-14529`). One result per licence (two spellings of one number are checked once). Max 1 000.

## `maxItems` (type: `integer`):

Maximum number of results (listings checked + licence numbers) to save for the whole run, after filters. 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (place and site, map area or search URL), so that the first search cannot use up the whole **Max results** budget. 0 = no per-search cap — except when the run searches both sites (places on both, or a Booking.com place next to an Airbnb map area or search URL): **Max results** is then shared evenly between the searches (a Booking.com page brings 100 properties at once and would take every slot).

## `splitMap` (type: `boolean`):

Airbnb shows at most 270 listings (15 pages) per search: a full search has its map area cut into 4 smaller areas, again and again. Booking.com shows at most 1 000 properties per search: a bigger one is also read district by district. Off = the first 270 (Airbnb) or 1 000 (Booking.com) of each search only.

## `checkIn` (type: `string`):

Travel dates of the search (`2026-10-12`). Airbnb then shows the listings free on those dates and their price for the stay; empty = Airbnb's own flexible dates. Today or later (the day in Spain): Airbnb shows nothing for a stay that has begun.

## `checkOut` (type: `string`):

End of the stay (`2026-10-15`), after the check-in date.

## `adults` (type: `integer`):

Number of guests: only the listings that can host them. 0 = any.

## `children` (type: `integer`):

Children aged 2-12 travelling (Airbnb filter). 0 = none.

## `infants` (type: `integer`):

Infants under 2 travelling (Airbnb filter). 0 = none.

## `pets` (type: `integer`):

Pets travelling: only listings that allow pets. 0 = none.

## `currency` (type: `string`):

Currency of the prices shown on the search results.

## `priceMin` (type: `integer`):

Lowest price per night, in the currency above. 0 = no minimum.

## `priceMax` (type: `integer`):

Highest price per night, in the currency above. 0 = no maximum.

## `roomTypes` (type: `array`):

Only these kinds of place. Empty = all.

## `propertyTypes` (type: `array`):

Only these kinds of property (Airbnb filter). Empty = all.

## `minBedrooms` (type: `integer`):

Only listings with at least this many bedrooms. 0 = any.

## `minBeds` (type: `integer`):

Only listings with at least this many beds. 0 = any.

## `minBathrooms` (type: `integer`):

Only listings with at least this many bathrooms. 0 = any.

## `superhostOnly` (type: `boolean`):

Only listings of Superhosts (Airbnb filter).

## `bookingPropertyTypes` (type: `array`):

Kinds of properties a Booking.com search lists. Default: apartments, holiday homes and villas — the homes a tourist licence is for (a hotel has a licence of another register).

## `onlyVerdicts` (type: `array`):

Save only the listings whose verdict is one of these — e.g. `never_issued`, `not_in_register`, `other_property`, `over_capacity`, `national_mismatch`, `unknown_format` and `no_number` to get the suspicious listings only (the verdicts marked isSuspicious). Empty = every listing.

## `regions` (type: `array`):

Only listings located in these regions. Empty = all.

## `hostType` (type: `string`):

Only listings of business hosts (companies and professionals, who must show their business details) or of individual hosts, as Airbnb labels them on the search results (on the listing page for listing URLs and IDs) — on Booking.com, as the host declared itself (professional or private).

## `minRating` (type: `number`):

Only listings rated at least this much out of 5 (Booking.com's score out of 10 is halved; listings without a rating are dropped). 0 = any.

## `excludeKeywords` (type: `array`):

Drop the listings whose title contains one of these words (case and accents ignored).

## `gpsToleranceKm` (type: `number`):

Airbnb blurs a listing's position by a few hundred metres. A register record farther than this from the listing (or the Booking.com property) gives the verdict `other_property` (registers that give a position: Andalusia, Mallorca, Canary Islands, Barcelona city).

## `includeHostProfile` (type: `boolean`):

Airbnb: open the host's public profile (1 extra request per host, not per listing) for the number of listings they manage and their IDs. Charged per host profile (see Pricing). Booking.com shows no host profile.

## `onlyNew` (type: `boolean`):

Skip the listings (and licence numbers) that a previous run (same **Memory name**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new.

## `memoryName` (type: `string`):

Name of the memory used by **Only new listings**. Give each schedule / task its own name (e.g. `barcelona-weekly`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory name** before the run: this run returns (and charges) every listing again. Untick it afterwards.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies, for the Airbnb and Booking.com pages. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor. The official registers are downloaded directly.

## `maxConcurrency` (type: `integer`):

Maximum number of Airbnb pages processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to Airbnb and Booking.com in any 60 seconds, all of them counted: search pages, listing pages and host profiles. A budget, not an even pace: up to this many can leave at once when the minute starts (spread them with the minimum delay below). Lower it if Airbnb or Booking.com answers HTTP 429 / 403 in the log.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests to Airbnb, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request Airbnb turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "listingIds": [],
  "bookingUrls": [],
  "location": "Barcelona, Spain",
  "locations": [],
  "sources": [
    "airbnb"
  ],
  "boundingBoxes": [],
  "licenseNumbers": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "splitMap": true,
  "adults": 0,
  "children": 0,
  "infants": 0,
  "pets": 0,
  "currency": "EUR",
  "priceMin": 0,
  "priceMax": 0,
  "roomTypes": [],
  "propertyTypes": [],
  "minBedrooms": 0,
  "minBeds": 0,
  "minBathrooms": 0,
  "superhostOnly": false,
  "bookingPropertyTypes": [
    "apartment",
    "holiday_home",
    "villa"
  ],
  "onlyVerdicts": [],
  "regions": [],
  "hostType": "any",
  "minRating": 0,
  "excludeKeywords": [],
  "gpsToleranceKm": 1.5,
  "includeHostProfile": false,
  "onlyNew": false,
  "memoryName": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "location": "Barcelona, Spain",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/airbnb-spain-license-checker").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 = {
    "location": "Barcelona, Spain",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/airbnb-spain-license-checker").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "location": "Barcelona, Spain",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/airbnb-spain-license-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/airbnb-spain-license-checker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/z5RwQlc9nTObPzMN7/builds/46oZnngFeZo1qZ7br/openapi.json
