# Milanuncios Scraper - Spain Classifieds, Cars & Property (`s-r/milanuncios-scraper`) Actor

Scrape Milanuncios, one of Spain's largest classifieds sites, by keyword, category or search URL. Get prices in EUR, photos, city and province, seller type, dates and item attributes for cars, property and second-hand goods, ready for price research and lead lists.

- **URL**: https://apify.com/s-r/milanuncios-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 listing returneds

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/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

## Milanuncios Scraper

Milanuncios Scraper collects Spanish classified advertisements by keyword, category or a search URL copied from Milanuncios, for price research, market monitoring and lead lists. Export unique listings with prices in EUR, images, locations, seller information and category attributes, covering second-hand goods, cars, property and services across Spain.

### What can you do with the Milanuncios Scraper?

- **Second-hand price research.** Collect asking prices for a product such as `iphone 13` or a bicycle model and compare medians by province, condition or seller type.
- **Car and vehicle market monitoring.** Track used-car listings in a category and province, including mileage, year and fuel attributes as the seller entered them, plus financed prices when supplied.
- **Property and rental tracking.** Follow new flats, rooms or garages in a city, sorted newest first, and spot price drops through `previous_price`.
- **Lead lists of professional sellers.** Filter on `professional` sellers to find dealers and shops active in a category, with their seller name, seller ID and location.
- **Stock and resale sourcing.** Run the same search daily, keep only new `listing_id` values and alert a buyer when a matching item appears below a price.
- **Demand research.** Switch `ad_type` to `wanted` to see what people across Spain are looking to buy.

### Milanuncios Scraper input

Start with a search term in `query`, a category slug in `category`, or a complete search URL in `url`. The actor searches the Spanish market and saves one dataset row per unique advertisement. A keyword such as `iphone 13` uses the site's listing search. A category such as `iphone` searches that category even without a keyword. Category and province slugs are the names used in the site's address bar.

When you paste a search URL, its keyword, category, province, ordering and price parameters take precedence over the separate search fields. `max_items` still controls the number of unique listings collected, and `include_top_ads` controls whether promoted top advertisements are included. A listing detail URL is not a search URL and is rejected with an input error.

The default `max_items` is 100. You can request up to 8200 unique listings from one search, subject to the source's available results and search window. A request for fewer rows stops once that number has been collected. Requesting more than the site has available returns the available listings. The actor never creates rows to fill the requested count.

Use `province` to limit a keyword search geographically, `category` to choose a section, and `price_min` and `price_max` for an asking-price range in EUR. `seller_type` accepts `all`, `private` or `professional`. `ad_type` accepts `offers` or `wanted`; wanted advertisements describe people seeking an item, so keep them separate when comparing selling prices.

Choose `newest`, `relevance`, `price_asc` or `price_desc` with `sort`. Newest is the default because relevance ordering can move advertisements between requests. The actor removes repeated listing identifiers, including promoted advertisements that appear at the top of several pages. Set `include_top_ads` to false to exclude those placements altogether.

#### Example input

This search asks for five listings matching `iphone 13`, newest first:

```json
{
  "query": "iphone 13",
  "max_items": 5
}
```

A pasted search URL works the same way, for example `{"url": "https://www.milanuncios.com/iphone-en-madrid/?s=iphone%2013", "max_items": 5}` for iPhone 13 listings in the Madrid province.

### Milanuncios Scraper output

#### Example output

The first row returned by the input above, retrieved on 5 October 2026. It reported 1441 matching listings in total.

```json
{
  "source": "milanuncios-scraper",
  "country": "ES",
  "currency": "EUR",
  "fetched_at": "2026-10-05T15:44:36.454Z",
  "listing_id": "618324520",
  "title": "Funda magnetica de iPhone 13, 14, 15",
  "url": "https://www.milanuncios.com/fundas-para-moviles/funda-magnetica-de-iphone-13-14-15-618324520.htm",
  "price": 5,
  "price_includes_taxes": null,
  "financed_price": null,
  "previous_price": null,
  "price_drop_label": null,
  "description": "Funda magnetica de iPhone 13, 14, 15 nueva",
  "image_url": "https://images.milanuncios.com/api/v1/ma-ad-media-pro/images/f5fe5395-35e7-406d-a3ed-9b87c59ab24d?rule=hw396_70",
  "images": [
    "https://images.milanuncios.com/api/v1/ma-ad-media-pro/images/f5fe5395-35e7-406d-a3ed-9b87c59ab24d?rule=original",
    "https://images.milanuncios.com/api/v1/ma-ad-media-pro/images/c7faf936-d260-4329-acbc-3c43f74c4ad8?rule=original"
  ],
  "image_count": 2,
  "city": "Arganda del Rey",
  "province": "Madrid",
  "region": "Madrid",
  "location": "Arganda del Rey (Madrid)",
  "category_id": 115,
  "category": "Fundas",
  "category_path": [
    "Telefonía",
    "Móviles",
    "Fundas"
  ],
  "attributes": {
    "color": "azul claro",
    "estado del producto": "prácticamente nuevo"
  },
  "condition": "prácticamente nuevo",
  "seller_type": "private",
  "seller_id": "236611126",
  "seller_name": "Ismael",
  "ad_type": "offer",
  "reserved": false,
  "shipping_available": false,
  "is_highlighted": false,
  "is_top_ad": false,
  "is_new": true,
  "phone_available": false,
  "published_at": "2026-10-05T15:00:17.000Z",
  "updated_at": "2026-10-05T15:00:17.000Z",
  "sorted_at": "2026-10-05T15:00:17.000Z",
  "origin": "milanuncios",
  "query": "iphone 13",
  "page": 1,
  "position": 1
}
```

The default dataset contains one row per listing. `listing_id` is the stable source identifier, and `url` opens the original advertisement. `title` and `description` carry the seller's content; descriptions retain their line breaks. `position` starts at one and continues across all collected pages. `source` identifies this scraper, `country` is ES, and `fetched_at` records when the source page was retrieved.

`price` is the current cash asking price as a number in EUR. A source price of zero becomes null because it means the listing does not state a price. A null price is therefore not a promise that an item is free. Discounted listings can carry `previous_price` and `price_drop_label`. Vehicle advertisements may additionally contain `financed_price` and `price_includes_taxes`, when the seller supplied those values.

`image_url` contains the primary thumbnail, and `images` contains the available original images. `image_count` reports their number. A listing without photos has a null primary image and an empty image list. The actor reports the media references supplied by the listing and does not download image files into your dataset.

Location fields include `city`, `province`, `region` and a combined `location`. Category fields include `category_id`, `category` and `category_path`. The `attributes` object preserves the site's Spanish labels and values, such as product condition, colour, vehicle mileage or property size. `condition` repeats the product-condition attribute when available; absent attributes are not inferred from the title.

Seller fields include `seller_type`, `seller_id` and `seller_name`. Flags describe reserved listings, available shipping, highlighted ads and promoted top ads. `phone_available` only indicates that the source offers a contact phone; this actor does not retrieve the phone number. Publication, update and ordering times appear as UTC timestamps in `published_at`, `updated_at` and `sorted_at`.

The run output includes delivery counts and notes. A source interruption can leave a partial result with a warning. Large searches are collected in successive pieces automatically on Apify. API users can continue a partial response using `next_cursor` with the same search options. A missing cursor means there is no further continuation for that request.

Every row also carries `currency` (EUR), `ad_type` (offer or wanted), `is_new`, `origin`, the `query` that found it and the source `page`.

### How it compares to other Milanuncios scrapers

Several Milanuncios scrapers exist. Many focus on one section, most often cars, or ask you to build search URLs by hand. This actor is meant for buyers who need any section of the site in one consistent dataset.

| | This actor | Typical alternatives |
|---|---|---|
| Sections covered | All categories: goods, vehicles, property and services | Often one section, usually cars |
| Input | Keyword, category and province slugs, price range, seller type, or a pasted search URL | Usually a search URL only |
| Wanted ads | Separate `ad_type` filter | Mixed with offers or absent |
| Duplicates | Promoted top ads and repeated pages removed by `listing_id` | Often left to you |
| Prices | Numeric EUR, zero shown as null, previous and financed price | Price text or number only |
| Billing | Per delivered unique listing; empty searches cost no listing events | Varies; some charge per page or per dataset item |

If you need phone numbers or detail-page statistics, this version is not the right fit; see the FAQ.

### Milanuncios Scraper pricing

The proposed launch price is $0.0025 per delivered listing plus a $0.0005 run start event. A 100-listing run would cost $0.2505 at those rates. Check the actor's Pricing tab for the active rate before running it. Listings are charged once after delivery, and repeated advertisements are removed before counting. A search with no listings has no listing charges; the run start event still applies.

Users without a paid Apify plan receive up to 10 listings per run. The run summary reports `free_tier_remaining`, and the output identifies whether the free plan cap was applied. Paying users can request the full supported volume. Your chosen maximum is an upper bound on delivered rows, not a promise that the source will have that many advertisements.

At the proposed rates, 1000 listings would cost $2.5005.

### FAQ

#### Can I use a URL copied from Milanuncios?

Yes. Paste a search or category URL into `url`. Its site filters take precedence over the separate keyword and filter inputs. URLs for individual advertisements are outside this actor's input scope.

#### Why are some prices null?

Some sellers do not state an asking price, and services or wanted advertisements can use a zero source value. These become null in the dataset so that they do not look like free products in price analysis.

#### Can I collect more than 8200 listings?

One source search has a limited page window. Split broader work into separate searches by category, province or price range, then combine datasets using `listing_id` to remove overlaps. The reported source total of 10000 can mean 10000 or more and is not the number guaranteed to be accessible.

#### Does this actor collect telephone numbers or exact coordinates?

It collects the information available in search listings. Phone numbers, precise coordinates and detail-page statistics are outside this version. City and province, seller identity, listing URLs and the source's contact-availability flag remain available for further workflows.

# Actor input Schema

## `query` (type: `string`):

Search keyword. Provide query, a search URL, or a category.

## `url` (type: `string`):

Milanuncios search URL. Overrides the keyword and site filters.

## `category` (type: `string`):

Category slug copied from the site URL, such as iphone.

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

Province slug copied from the site URL, such as madrid.

## `price_min` (type: `integer`):

Minimum asking price in EUR.

## `price_max` (type: `integer`):

Maximum asking price in EUR.

## `sort` (type: `string`):

Newest is the default; relevance can repeat listings between pages.

## `seller_type` (type: `string`):

Include all sellers, private sellers, or professional sellers.

## `ad_type` (type: `string`):

Offers are sellers; wanted ads are buyers looking for an item.

## `max_items` (type: `integer`):

Maximum unique listings across all cursor requests. The search window is about 8200 listings.

## `include_top_ads` (type: `boolean`):

Include promoted top ads once; duplicates are removed.

## `cursor` (type: `string`):

Opaque next_cursor from a previous response, used with the same search options.

## Actor input object example

```json
{
  "query": "iphone 13",
  "sort": "newest",
  "seller_type": "all",
  "ad_type": "offers",
  "max_items": 100,
  "include_top_ads": true
}
```

# Actor output Schema

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

One row per unique listing.

## `output` (type: `string`):

Counts, charge events and free plan flags.

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

Delivered listings, pages fetched and free_tier_remaining.

# 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 = {
    "query": "iphone 13"
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/milanuncios-scraper").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 = { "query": "iphone 13" }

# Run the Actor and wait for it to finish
run = client.actor("s-r/milanuncios-scraper").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 '{
  "query": "iphone 13"
}' |
apify call s-r/milanuncios-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/milanuncios-scraper"
        }
    }
}
```

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/0145aoST4wWOWfhMs/builds/jjLGslia6TKanWl1T/openapi.json
