Portal Inmobiliario APIportalinmobiliario.com ↗
Search and retrieve Chilean property listings from Portal Inmobiliario. Access price in UF/CLP, rooms, bathrooms, area in m², and location via 2 endpoints.
What is the Portal Inmobiliario API?
The Portal Inmobiliario API provides access to Chile's leading real estate platform through 2 endpoints, returning structured property data including price in UF or CLP, room counts, bathroom counts, area in m², and full location strings. The search_listings endpoint lets you query by property type, operation, and location slug, returning up to 48 listings per page, while get_listing_detail returns complete attribute tables for a single listing by MLC ID or URL.
curl -X GET 'https://api.parse.bot/scraper/f3817e67-e705-4258-a9a6-8fa44f79e1e1/search_listings?location=santiago-metropolitana&operation=venta&property_type=departamento' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace portalinmobiliario-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Portal Inmobiliario API Client
A Python client for scraping property listings from Portal Inmobiliario (Chile's leading real estate platform).
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, Dict, List, Any
class ParseClient:
"""Client for the Portal Inmobiliario Parse API."""
def __init__(self, api_key: Optional[str] = None):
"""
Initialize the Parse API client.
Args:
api_key: Optional API key. If not provided, uses PARSE_API_KEY environment variable.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "f3817e67-e705-4258-a9a6-8fa44f79e1e1"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key must be provided or set in PARSE_API_KEY environment variable")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""
Make an API call to the Parse Bot service.
Args:
endpoint: The endpoint name to call
method: HTTP method (GET or POST)
**params: Query or body parameters
Returns:
Response JSON as dictionary
Raises:
requests.RequestException: If the API call fails
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method == "GET":
response = requests.get(url, headers=headers, params=params)
elif method == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
def search_listings(
self,
property_type: str = "departamento",
operation: str = "venta",
location: str = "vitacura-metropolitana",
page: int = 1
) -> Dict[str, Any]:
"""
Search property listings by type, operation, and location.
Args:
property_type: Property type slug (e.g., departamento, casa, oficina, terreno, local-comercial)
operation: Operation type: venta (sale) or arriendo (rent)
location: Location slug (e.g., vitacura-metropolitana, santiago-metropolitana)
page: Page number (1-based). Each page returns up to 48 listings.
Returns:
Dictionary containing total count, current page, listings count, and listings array
"""
return self._call(
"search_listings",
method="GET",
property_type=property_type,
operation=operation,
location=location,
page=page
)
def get_listing_detail(
self,
listing_url: Optional[str] = None,
listing_id: Optional[str] = None
) -> Dict[str, Any]:
"""
Get detailed information for a specific property listing.
Args:
listing_url: Full URL of the listing (from search results)
listing_id: MLC listing ID (e.g., MLC-1742229599)
Returns:
Dictionary containing detailed listing information
Raises:
ValueError: If neither listing_url nor listing_id is provided
"""
if not listing_url and not listing_id:
raise ValueError("Either listing_url or listing_id must be provided")
params = {}
if listing_url:
params["listing_url"] = listing_url
if listing_id:
params["listing_id"] = listing_id
return self._call("get_listing_detail", method="GET", **params)
def format_listing_summary(listing: Dict[str, Any]) -> str:
"""Format a listing for display."""
price_display = f"{listing.get('price_prefix', '')} {listing['price']}".strip()
return (
f" {listing['title']}\n"
f" Price: {price_display} {listing['currency']}\n"
f" Rooms: {listing['rooms']} | Bathrooms: {listing['bathrooms']}\n"
f" Area: {listing['area_m2']} m² ({listing['area_type']})\n"
f" Location: {listing['location']}\n"
f" ID: {listing['id']}\n"
)
def format_listing_detail(detail: Dict[str, Any]) -> str:
"""Format detailed listing information for display."""
price_display = f"{detail.get('price_prefix', '')} {detail['price']}".strip()
output = (
f"Title: {detail['title']}\n"
f"ID: {detail['id']}\n"
f"Price: {price_display} {detail['currency']}\n"
f"Rooms: {detail['rooms']}\n"
f"Bathrooms: {detail['bathrooms']}\n"
f"Area: {detail['area_m2']} m²\n"
f"Location: {detail['location']}\n"
)
if detail.get('attributes'):
output += "\nAdditional Attributes:\n"
for attr_name, attr_value in detail['attributes'].items():
output += f" {attr_name}: {attr_value}\n"
return output
if __name__ == "__main__":
# Initialize the client
client = ParseClient()
print("=" * 80)
print("Portal Inmobiliario Property Market Analysis")
print("=" * 80)
# WORKFLOW: Search apartments for sale, analyze top listings, get details
print("\n[STEP 1] Searching for apartments (departamentos) for sale in Vitacura...")
search_results = client.search_listings(
property_type="departamento",
operation="venta",
location="vitacura-metropolitana",
page=1
)
total_listings = search_results['data']['total']
listings = search_results['data']['listings']
print(f"✓ Found {total_listings} total listings in Vitacura")
print(f"✓ Showing {len(listings)} listings on page 1\n")
# Display summary of first 5 listings
print("[STEP 2] Top 5 listings summary:\n")
top_listings = listings[:5]
for i, listing in enumerate(top_listings, 1):
print(f"{i}. {format_listing_summary(listing)}")
# Get detailed information for top 3 listings
print("\n" + "=" * 80)
print("[STEP 3] Fetching detailed information for top 3 listings...")
print("=" * 80)
detailed_listings = []
for i, listing in enumerate(top_listings[:3], 1):
listing_id = listing['id']
print(f"\n[{i}/3] Fetching details for: {listing['title']}...")
try:
detail = client.get_listing_detail(listing_id=listing_id)
detailed_listings.append(detail['data'])
print(f"✓ Details retrieved successfully\n")
print(format_listing_detail(detail['data']))
except Exception as e:
print(f"✗ Error fetching details: {e}\n")
# Price comparison analysis
print("\n" + "=" * 80)
print("[STEP 4] Price Comparison Analysis")
print("=" * 80)
uf_prices = []
clp_prices = []
for detail in detailed_listings:
if detail['currency'] == 'UF':
uf_prices.append(detail['price'])
else:
clp_prices.append(detail['price'])
if uf_prices:
avg_uf = sum(uf_prices) / len(uf_prices)
print(f"\nAverage price (UF): {avg_uf:,.2f}")
print(f"Price range (UF): {min(uf_prices):,.2f} - {max(uf_prices):,.2f}")
if clp_prices:
avg_clp = sum(clp_prices) / len(clp_prices)
print(f"\nAverage price (CLP): {avg_clp:,.2f}")
print(f"Price range (CLP): {min(clp_prices):,.2f} - {max(clp_prices):,.2f}")
# WORKFLOW: Search rental market in a different location
print("\n" + "=" * 80)
print("[STEP 5] Searching rental market in Las Condes...")
print("=" * 80)
rental_results = client.search_listings(
property_type="casa",
operation="arriendo",
location="las-condes-metropolitana",
page=1
)
rental_listings = rental_results['data']['listings']
print(f"✓ Found {rental_results['data']['total']} total rental listings")
print(f"✓ Showing {len(rental_listings)} listings on page 1\n")
if rental_listings:
print("First 3 rental houses available:\n")
for i, listing in enumerate(rental_listings[:3], 1):
print(f"{i}. {format_listing_summary(listing)}")
# Get details for a rental property
if rental_listings:
first_rental = rental_listings[0]
print("\n" + "=" * 80)
print(f"[STEP 6] Detailed view of: {first_rental['title']}")
print("=" * 80 + "\n")
try:
rental_detail = client.get_listing_detail(listing_id=first_rental['id'])
print(format_listing_detail(rental_detail['data']))
except Exception as e:
print(f"Error fetching rental details: {e}")
print("\n" + "=" * 80)
print("Analysis complete!")
print("=" * 80)Search property listings by type, operation, and location. Returns up to 48 listings per page with price, rooms, bathrooms, area, and location information.
| Param | Type | Description |
|---|---|---|
| page | integer | Page number (1-based). Each page returns up to 48 listings. |
| location | string | Location slug (e.g., vitacura-metropolitana, santiago-metropolitana, las-condes-metropolitana, providencia-metropolitana) |
| operation | string | Operation type: venta (sale) or arriendo (rent) |
| property_type | string | Property type slug: departamento, casa, oficina, terreno, local-comercial |
{
"type": "object",
"fields": {
"page": "integer - current page number",
"total": "integer - total number of listings matching the search",
"listings": "array of listing objects with title, url, id, currency, price, rooms, bathrooms, area_m2, area_type, location, and optional price_prefix",
"listings_count": "integer - number of listings returned on this page"
},
"sample": {
"data": {
"page": 1,
"total": 3299,
"listings": [
{
"id": "MLC-1863859893",
"url": "https://portalinmobiliario.com/MLC-1863859893-parque-costanera-residences-_JM",
"price": 7990,
"rooms": "1 a 3",
"title": "Parque Costanera Residences",
"area_m2": "46 - 149",
"currency": "UF",
"location": "Américo Vespucio Nte. 2920, Vitacura, Parque Bicentenario, Vitacura",
"area_type": "útiles",
"bathrooms": "1 a 4"
}
],
"listings_count": 48
},
"status": "success"
}
}About the Portal Inmobiliario API
Search Listings
The search_listings endpoint accepts four optional parameters: location (a slug such as vitacura-metropolitana or las-condes-metropolitana), operation (venta for sale or arriendo for rent), property_type (e.g., departamento, casa, oficina, terreno, local-comercial), and page for 1-based pagination. Each response includes a total count of all matching listings, a listings_count for the current page, and an array of listing objects. Each listing carries a title, url, id, currency (UF or $), price, rooms, bathrooms, area_m2, area_type, and location. A price_prefix field (e.g., Desde) appears on project listings where the price is a starting figure.
Listing Detail
The get_listing_detail endpoint accepts either a listing_url from search results or a listing_id in MLC format (e.g., MLC-1234567890). The response expands on search data with a full attributes object containing every key-value pair from the listing's specs table — this can include floor number, parking spaces, storage units, year of construction, and other property-specific fields not guaranteed in the search response. The currency field distinguishes UF-denominated listings (common for sales) from CLP-denominated ones (common for rentals).
Coverage and Pagination
The API covers residential and commercial property types across Chile, filtered by the location slugs the platform uses in its URLs. Pagination is handled via the page parameter; the total field in search_listings responses lets you calculate how many pages exist for a given query. There is no server-side sorting or price-range filter exposed at this time — filtering by price range or sorting by newest requires processing the returned listings client-side.
The Portal Inmobiliario API is a managed, monitored endpoint for portalinmobiliario.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when portalinmobiliario.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official portalinmobiliario.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Monitor asking prices in UF across Santiago neighbourhoods for buy-vs-rent analysis
- Aggregate room and area data from
search_listingsto build per-m² price indexes by commune - Track new apartment listings in specific location slugs to alert buyers when inventory changes
- Extract the
attributesobject fromget_listing_detailto compare parking and storage availability across projects - Compile rental (
arriendo) price distributions for offices and commercial spaces by zone - Build a property comparison tool using price, rooms, bathrooms, and area_m2 from search results
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Portal Inmobiliario have an official developer API?+
What does the `attributes` field in `get_listing_detail` contain?+
attributes object contains all key-value pairs from the listing's specs table on the detail page. Common fields include parking spaces, storage units, floor level, and year of construction, but the exact keys vary by listing type and what the publisher filled in. Always treat individual attribute keys as optional in your parsing logic.Can I filter search results by price range or sort by most recent listing date?+
search_listings endpoint does not currently expose price-range or sort-order parameters. Results are returned in the platform's default order and must be filtered client-side by price, area, or date if needed. You can fork this API on Parse and revise it to add those filter parameters if the underlying data supports them.Are listings from outside the Metropolitan Region covered?+
location parameter accepts any slug that appears in Portal Inmobiliario URLs, so regions beyond Santiago (e.g., Valparaíso, Biobío) should work as long as you supply the correct slug. However, coverage depth varies by region since the platform has denser inventory in the Metropolitan Region. Slugs for less common locations are not validated ahead of time — an empty listings array with total: 0 indicates no results for that slug.