Intermediate
20 min

Search Listings by Map Area

Query Airbnb properties within custom polygons and radius circles for precise micro-market analysis and neighborhood-level research.

1

Radius Search

Use POST /listings/search/radius to find all listings within a circular area around a point. Provide the center coordinates (latitude and longitude) and a radius in miles (1 to 100). This example searches within 5 miles of downtown Nashville, sorted by trailing-twelve-month revenue.

Python

import requests

API_KEY = "your_api_key_here"
BASE_URL = "https://api.airroi.com/v1"

# Search listings within 5 miles of downtown Nashville
response = requests.post(
    f"{BASE_URL}/listings/search/radius",
    headers={"X-API-KEY": API_KEY},
    json={
        "latitude": 36.1627,
        "longitude": -86.7816,
        "radius_miles": 5,
        "filter": {
            "bedrooms": {"gte": 1}
        },
        "sort": {
            "ttm_revenue": "desc"
        },
        "pagination": {
            "page_size": 10,
            "offset": 0
        },
        "currency": "usd"
    }
)

data = response.json()
print(f"Found {data['total']} listings within 5 miles")
for listing in data["results"]:
    print(f"  {listing['title']} - {listing['ttm_revenue']:,.0f}/yr")

2

Polygon Search

Use POST /listings/search/polygon to search within a custom-shaped boundary. Provide an array of coordinate points (minimum 3) that define the polygon. The first point must be repeated as the last point to close the shape. This is ideal for tracing neighborhood boundaries, school districts, or custom investment zones. Best practice is to use 4 to 8 vertices for optimal performance.

Python

# Define a polygon around a neighborhood boundary
# Example: East Nashville neighborhood
polygon_coords = [
    {"latitude": 36.1850, "longitude": -86.7500},
    {"latitude": 36.1850, "longitude": -86.7200},
    {"latitude": 36.1650, "longitude": -86.7200},
    {"latitude": 36.1650, "longitude": -86.7500},
    # Close the polygon by repeating the first point
    {"latitude": 36.1850, "longitude": -86.7500}
]

response = requests.post(
    f"{BASE_URL}/listings/search/polygon",
    headers={"X-API-KEY": API_KEY},
    json={
        "polygon": polygon_coords,
        "pagination": {
            "page_size": 10,
            "offset": 0
        },
        "currency": "usd"
    }
)

data = response.json()
print(f"Found {data['total']} listings in the polygon area")
for listing in data["results"]:
    print(f"  {listing['title']}")
    print(f"    Bedrooms: {listing['bedrooms']}, Revenue: {listing['ttm_revenue']:,.0f}")

3

Add Filters to Geospatial Queries

Combine geospatial boundaries with property and performance filters. The filter object supports operators including eq (exact match), gt/gte (greater than), lt/lte (less than), range (between min and max), any (has at least one of), all (has all of), and none (has none of). This example finds 2+ bedroom superhost listings with a pool or hot tub.

Python

# Combine geospatial search with property filters
response = requests.post(
    f"{BASE_URL}/listings/search/radius",
    headers={"X-API-KEY": API_KEY},
    json={
        "latitude": 36.1627,
        "longitude": -86.7816,
        "radius_miles": 10,
        "filter": {
            "bedrooms": {"gte": 2},
            "superhost": {"eq": True},
            "amenities": {"any": ["pool", "hot_tub"]},
            "ttm_occupancy": {"gte": 0.50},
            "average_daily_rate": {"range": [100, 500]}
        },
        "sort": {
            "ttm_revenue": "desc"
        },
        "pagination": {
            "page_size": 10,
            "offset": 0
        },
        "currency": "usd"
    }
)

data = response.json()
print(f"Found {data['total']} filtered listings")
print("\nFilter operators used:")
print("  gte  - greater than or equal (bedrooms >= 2)")
print("  eq   - exact match (superhost = true)")
print("  any  - has at least one of (pool OR hot_tub)")
print("  range - between min and max (ADR $100-$500)")

4

Sort and Paginate Results

Sort results by any metric field (e.g., ttm_revenue, average_daily_rate, ttm_occupancy) in ascending or descending order. Paginate through large result sets using page_size (max 50) and offset. Loop through pages until you have collected all matching listings.

Python

# Sort by revenue (descending) and paginate through all results
all_listings = []
page_size = 10
offset = 0

while True:
    response = requests.post(
        f"{BASE_URL}/listings/search/radius",
        headers={"X-API-KEY": API_KEY},
        json={
            "latitude": 36.1627,
            "longitude": -86.7816,
            "radius_miles": 5,
            "sort": {
                "ttm_revenue": "desc"
            },
            "pagination": {
                "page_size": page_size,
                "offset": offset
            },
            "currency": "usd"
        }
    )

    data = response.json()
    all_listings.extend(data["results"])

    print(f"Page {offset // page_size + 1}: fetched {len(data['results'])} listings")

    # Stop when we've fetched all results
    if offset + page_size >= data["total"]:
        break

    offset += page_size

print(f"\nTotal listings collected: {len(all_listings)}")
print(f"Top earner: {all_listings[0]['title']} - {all_listings[0]['ttm_revenue']:,.0f}/yr")

5

Micro-Market Analysis

Use polygon search to isolate a specific neighborhood, then calculate aggregate statistics across all listings in that area. Compute average revenue, median revenue, average occupancy, listing count, and listing density per square mile. This gives you a hyperlocal market snapshot that city-level data cannot provide.

Python

import math

# Micro-market analysis: isolate a neighborhood and compute stats
polygon_coords = [
    {"latitude": 36.1850, "longitude": -86.7500},
    {"latitude": 36.1850, "longitude": -86.7200},
    {"latitude": 36.1650, "longitude": -86.7200},
    {"latitude": 36.1650, "longitude": -86.7500},
    {"latitude": 36.1850, "longitude": -86.7500}
]

# Fetch all listings in the polygon
all_listings = []
offset = 0
while True:
    resp = requests.post(
        f"{BASE_URL}/listings/search/polygon",
        headers={"X-API-KEY": API_KEY},
        json={
            "polygon": polygon_coords,
            "pagination": {"page_size": 50, "offset": offset},
            "currency": "usd"
        }
    )
    data = resp.json()
    all_listings.extend(data["results"])
    if offset + 50 >= data["total"]:
        break
    offset += 50

# Calculate aggregate stats
revenues = [l["ttm_revenue"] for l in all_listings if l.get("ttm_revenue")]
occupancies = [l["ttm_occupancy"] for l in all_listings if l.get("ttm_occupancy")]

# Approximate area in square miles (rough rectangle)
lat_diff = abs(36.1850 - 36.1650)
lng_diff = abs(-86.7500 - (-86.7200))
area_sq_miles = (lat_diff * 69) * (lng_diff * 69 * math.cos(math.radians(36.175)))

print("=== Micro-Market Analysis ===")
print(f"Total listings: {len(all_listings)}")
print(f"Avg annual revenue: {sum(revenues)/len(revenues):,.0f}")
print(f"Median revenue: {sorted(revenues)[len(revenues)//2]:,.0f}")
print(f"Avg occupancy: {sum(occupancies)/len(occupancies):.1%}")
print(f"Area: {area_sq_miles:.2f} sq miles")
print(f"Listing density: {len(all_listings)/area_sq_miles:.0f} listings/sq mile")

6

Best Practices

Follow these guidelines to get the most out of geospatial searches:

  • Polygon vertex count: Use 4 to 8 vertices for optimal performance. More vertices increase precision but slow down queries. A simple rectangle (4 vertices) works well for most neighborhood analyses.
  • Always close your polygon: The last coordinate must exactly match the first coordinate. Failing to close the polygon will result in an API error.
  • Radius vs. polygon: Use radius search for circular areas around a point of interest (airport, downtown, beach). Use polygon search for irregular boundaries (neighborhoods, zip codes, custom zones).
  • Large result sets: For areas with thousands of listings, use filters to narrow results before paginating. This reduces API calls and processing time.
  • Getting coordinates: Right-click any location in Google Maps to copy its latitude and longitude. Use tools like geojson.io to draw polygons and export coordinates.

Continue Learning

Keep exploring the AirROI API with these related tutorials.

Frequently Asked Questions

The maximum radius is 100 miles. The minimum is 1 mile. For very large search areas, consider breaking them into multiple smaller radius searches or using a polygon to define the exact boundary you need.

A polygon requires a minimum of 3 vertices (forming a triangle) and the first point must be repeated as the last point to close the shape. For optimal performance, use 4 to 8 vertices. More vertices increase precision but also increase response time.

Yes. Both radius and polygon searches accept a filter object where you can apply any supported filter operator (eq, gt, gte, lt, lte, range, any, all, none) on fields like bedrooms, bathrooms, revenue, occupancy, superhost status, amenities, and more.

The API uses decimal degrees for latitude and longitude (e.g., latitude: 36.1699, longitude: -115.1398). Latitude ranges from -90 to 90 and longitude from -180 to 180. You can get coordinates from Google Maps by right-clicking any location.

Use radius search when you want listings within a circular area around a point of interest, such as around a landmark or city center. Use polygon search when you need to define irregular boundaries, such as specific neighborhoods, school districts, or custom investment zones.

Results are paginated with a default page_size of 10 and a maximum of 50 per page. Use the offset parameter to page through results. The total number of matching listings is returned in the response so you know how many pages to request.

Ready to Build?

Get your API key and start making calls in minutes.
made with