# Urbania Peru Property Listings Scraper (`automatic/urbania-peru-scraper`) Actor

Property listings for rent and sale from Urbania.pe, Peru's largest real-estate portal. Keyword search, optional detail enrichment (full description, posting date, seller contact, gallery), structured fields: price, currency, rooms, bathrooms, area, address, publisher. Fresh listings every run.

- **URL**: https://apify.com/automatic/urbania-peru-scraper.md
- **Developed by:** [Philip Kirkbride](https://apify.com/automatic) (community)
- **Categories:** Real estate, Lead generation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 74.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.

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

## Urbania Peru Scraper: Property Listings for Rent and Sale

Collect current property listings from Urbania.pe, Peru's real-estate portal:
departamentos, casas and other properties for rent (alquiler) or sale (venta), by
city, with an optional keyword such as a district. Each row has the price in PEN or
USD, bedrooms, bathrooms, area in m², address, publisher, image and link. Optionally
add the full description, posting date, public contact and photo gallery.

Built for property price research, rental market studies and lead lists of agencies
and developments in Peru.

### Use cases

- **Rental price research:** compare asking rents (precios de alquiler) by district
  in Lima, e.g. Miraflores, San Isidro or Surco.
- **Sale market monitoring:** follow listings for sale (venta) in a city and track
  price per m² over time.
- **Market studies:** build a sample of listings by city with rooms, bathrooms, area
  and price for analysis.
- **Agency and developer leads:** list the publishers active in a district, with the
  public contact from the detail page.
- **Investment screening:** shortlist properties by price and size before visiting.

### Quick start

The Console form starts with rentals in Lima, one page, up to 50 listings:

```json
{"operation": "rentals", "city": "lima", "maxResults": 50}
```

Rentals in Miraflores, with details:

```json
{"operation": "rentals", "city": "lima", "search": "miraflores", "maxResults": 10, "includeDetails": true}
```

Listings for sale in Arequipa, three pages:

```json
{"operation": "sale", "city": "arequipa", "maxPages": 3, "maxResults": 100}
```

An empty input `{}` runs the same default: rentals in Lima, one page, up to 50
listings. A city left blank on purpose (`{"city": ""}`) stops at once with a
message asking for one.

### Input

| Name | Type | Default | What it does |
| --- | --- | --- | --- |
| `operation` | string | `rentals` | `rentals` (alquiler) or `sale` (venta). |
| `city` | string | `lima` | City as it appears in Urbania links, lower case with hyphens, e.g. `lima` or `arequipa`. |
| `search` | string | empty | Optional keyword, e.g. a district such as `miraflores`. Stored on each row as `source_query`. |
| `maxPages` | integer | `1` | Result pages to read, 1 to 3. |
| `maxResults` | integer | `50` | Maximum listings per run, 1 to 200. |
| `requestDelaySecs` | number | `4` | Pause between page requests, 1 to 30 seconds (at least 3 seconds with the default connection settings). |
| `includeDetails` | boolean | `false` | Open each listing and add `description`, `postedAt`, `sellerContact` and `additionalImageUrls`. |

The form also has advanced connection settings (`client` and the connection
fields). Leave them at their defaults unless support asks you to change them.

An unsupported operation, a malformed city or a limit out of range stops the run
before any page is read, with a message that names the field.

### Output

One row per listing, deduplicated by listing id. Every row has `id`, `listingKey`,
`url`, `operation`, `city`, `source` and `collectedAt`; `name`, `priceAmount`,
`priceCurrency`, `address`, `rooms`, `bathrooms`, `areaM2`, `publisher`, `imageUrl`
and `source_query` are filled when the listing shows them. With `includeDetails`,
rows also carry `description`, `postedAt`, `sellerContact` and `additionalImageUrls`.

Example row (values are illustrative):

```json
{
  "id": "150330100",
  "listingKey": "urbania.pe:150330100",
  "url": "https://urbania.pe/inmueble/clasificado/alclapin-alquiler-de-departamento-en-san-miguel-lima-2-dormitorios-150330100",
  "name": "CENTURY 21 PREMIUM",
  "priceAmount": 1950.0,
  "priceCurrency": "PEN",
  "operation": "rentals",
  "city": "lima",
  "source_query": null,
  "address": "San Miguel, Lima",
  "rooms": 2,
  "bathrooms": 1,
  "areaM2": 69.0,
  "publisher": "CENTURY 21 PREMIUM",
  "imageUrl": "https://img10.naventcdn.com/avisos/111/01/50/33/01/00/720x532/1615926868.jpg",
  "source": "urbania.pe",
  "collectedAt": "2026-11-10T14:05:12.481203+00:00"
}
```

Field notes:

- `listingKey` (`urbania.pe:<id>`) is the stable key for joins and dedupe.
- `name` is the publisher or development name shown on the listing (an agency or a
  project), not a property title; `publisher` carries the same value.
- `priceCurrency` is `PEN` or `USD`. When a listing shows two currencies, the first
  one is used. "Consultar precio" listings have a null price.
- Ranges such as `1 a 2 dorm.` report the upper bound.
- `postedAt` (details only) is an ISO timestamp; `sellerContact` is a published
  email or phone number when the listing shows one.
- Missing values are `null`, never guessed.

Download results as JSON, CSV, Excel or XML from the dataset, or read them with the
Apify API.

### Pricing

Pay per result: one start charge per run (one start event per GB of memory; the
default 512 MB run is one start event) plus one charge per listing saved to the
dataset. Current price: $0.0006 per listing plus a $0.0005 start fee, so 1,000 listings cost about $0.60.
Detail enrichment adds no extra event; it only makes the run take longer.

### Scheduling tips

Schedule the same input daily or weekly to follow a district. Each run is a fresh
snapshot; join runs on `listingKey` to see new listings and price changes.

### FAQ

**How fresh is the data?** Listings are read when the run starts; every row carries
its `collectedAt` timestamp.

**Which cities are supported?** Any Urbania city written the way it appears in the
site's search links, such as `lima` or `arequipa`. Use `search` for a district or
neighbourhood inside the city.

**What are the limits?** Up to 3 result pages and 200 listings per run.

**What if a page or detail is unavailable?** If a later result page cannot be read,
the run keeps the listings already collected. A detail page that cannot be read
leaves that listing's detail fields `null`; it never fails the run.

**Is contact data included?** Only with `includeDetails`, and only contacts the
publisher shows publicly on the listing. Treat it as personal data where local law
requires.

### Scope

Public Urbania.pe listings in Peru only, for rent or sale. Prices are asking prices
as listed, not closing prices. No login and no private data.

For help, contact Apify support and include the Actor run ID.

# Actor input Schema

## `operation` (type: `string`):

Listing operation to search for.

## `city` (type: `string`):

City to search, using the Urbania city slug, e.g. 'lima' or 'arequipa'.

## `search` (type: `string`):

Optional free-text keyword filter. Records carry the applied term in source_query; blank means no filter.

## `maxPages` (type: `integer`):

Maximum pages to collect, from 1 to 3.

## `maxResults` (type: `integer`):

Stop after this many listings.

## `requestDelaySecs` (type: `number`):

Seconds to wait between page requests, from 1 to 30.

## `client` (type: `string`):

How listing pages are loaded. Leave the default unless support asks you to change it.

## `proxy` (type: `string`):

Connection used to reach the site. Leave the default; choose 'external' to use your own proxy, or 'none' only for local testing.

## `proxyCountry` (type: `string`):

Two-letter country code for the connection.

## `proxyUrl` (type: `string`):

Optional custom proxy URL used when the connection is 'external', e.g. http://user:pass@host:port. Either a full URL with embedded credentials or a bare host:port together with proxyUsername/proxyPassword.

## `proxyUsername` (type: `string`):

Username for proxyUrl when the URL does not embed credentials.

## `proxyPassword` (type: `string`):

Password for proxyUrl when the URL does not embed credentials.

## `includeDetails` (type: `boolean`):

When true, also collect each listing's detail page (full description, posting date, public seller contact and gallery). Missing fields are null and a failed detail page never fails the run.

## Actor input object example

```json
{
  "operation": "rentals",
  "city": "lima",
  "search": "",
  "maxPages": 1,
  "maxResults": 50,
  "requestDelaySecs": 4,
  "client": "standard",
  "proxy": "residential",
  "proxyCountry": "PE",
  "proxyUrl": "",
  "proxyUsername": "",
  "proxyPassword": "",
  "includeDetails": 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 = {
    "operation": "rentals",
    "city": "lima",
    "search": "",
    "maxPages": 1,
    "maxResults": 50,
    "requestDelaySecs": 4,
    "client": "standard",
    "proxy": "residential",
    "proxyCountry": "PE",
    "proxyUrl": "",
    "proxyUsername": "",
    "proxyPassword": "",
    "includeDetails": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("automatic/urbania-peru-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 = {
    "operation": "rentals",
    "city": "lima",
    "search": "",
    "maxPages": 1,
    "maxResults": 50,
    "requestDelaySecs": 4,
    "client": "standard",
    "proxy": "residential",
    "proxyCountry": "PE",
    "proxyUrl": "",
    "proxyUsername": "",
    "proxyPassword": "",
    "includeDetails": False,
}

# Run the Actor and wait for it to finish
run = client.actor("automatic/urbania-peru-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 '{
  "operation": "rentals",
  "city": "lima",
  "search": "",
  "maxPages": 1,
  "maxResults": 50,
  "requestDelaySecs": 4,
  "client": "standard",
  "proxy": "residential",
  "proxyCountry": "PE",
  "proxyUrl": "",
  "proxyUsername": "",
  "proxyPassword": "",
  "includeDetails": false
}' |
apify call automatic/urbania-peru-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automatic/urbania-peru-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/g5M5Set1JfdgNwfvZ/builds/YXGOOW2c7APTTOIi7/openapi.json
