Building a Rental Listing Agent: AI-Powered Property Marketing and Description Generation
Learn to build an AI agent that creates compelling rental listings with auto-generated descriptions, photo captions, SEO-optimized content, and multi-channel distribution.
Why AI-Powered Listing Creation Matters
A property manager listing 20 vacant units writes the same type of description 20 times. The result is often generic, repetitive, and fails to highlight what makes each unit unique. An AI listing agent generates tailored, engaging descriptions, captions photos, optimizes for search engines, and distributes to multiple platforms — turning a 45-minute task into a 2-minute review.
Generating Property Descriptions
The core tool takes structured property data and produces a compelling narrative.
from agents import Agent, Runner, function_tool
from pydantic import BaseModel
class ListingInput(BaseModel):
address: str
unit_type: str # studio, 1br, 2br, etc.
sqft: int
rent: float
bedrooms: int
bathrooms: float
amenities: list[str]
neighborhood: str
pet_friendly: bool
available_date: str
unique_features: list[str] # recently renovated, corner unit, etc.
class GeneratedListing(BaseModel):
headline: str
description: str
bullet_points: list[str]
seo_title: str
meta_description: str
description_agent = Agent(
name="ListingDescriptionWriter",
instructions="""You are a professional real estate copywriter.
Write engaging, accurate rental listing descriptions.
Rules:
- Never use superlatives like 'best' or 'perfect'
- Include specific details (sqft, amenities, neighborhood)
- Follow Fair Housing Act: no discriminatory language
- Keep descriptions between 150-250 words
- Write in active voice""",
output_type=GeneratedListing,
)
@function_tool
async def generate_listing_description(
address: str,
unit_type: str,
sqft: int,
rent: float,
bedrooms: int,
bathrooms: float,
amenities: str,
neighborhood: str,
pet_friendly: bool,
available_date: str,
unique_features: str,
) -> str:
"""Generate a marketing description for a rental listing."""
prompt = f"""Create a rental listing for:
Address: {address}
Type: {unit_type} | {bedrooms}bd/{bathrooms}ba | {sqft} sqft
Rent: ${rent:,.0f}/month
Amenities: {amenities}
Neighborhood: {neighborhood}
Pet Friendly: {pet_friendly}
Available: {available_date}
Unique Features: {unique_features}"""
result = await Runner.run(description_agent, input=prompt)
listing = result.final_output
return (
f"Headline: {listing.headline}\n\n"
f"{listing.description}\n\n"
f"Highlights:\n" +
"\n".join(f"- {bp}" for bp in listing.bullet_points)
)
Photo Captioning
Property photos need descriptive captions for accessibility, SEO, and platform requirements.
import base64
from openai import AsyncOpenAI
async def caption_property_photo(
image_path: str,
property_context: str,
) -> str:
"""Generate an SEO-friendly caption for a property photo."""
client = AsyncOpenAI()
with open(image_path, "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
response = await client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": (
f"Write a concise, descriptive caption for this "
f"property photo. Context: {property_context}. "
f"Keep it under 20 words. Include the room type."
),
},
{
"type": "image_url",
"url": {"url": f"data:image/jpeg;base64,{img_b64}"},
},
],
}
],
)
return response.choices[0].message.content
SEO Optimization
Rental listings need to rank on apartment search engines and Google. The agent generates optimized metadata.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
def generate_seo_metadata(
listing: GeneratedListing,
city: str,
state: str,
unit_type: str,
rent: float,
) -> dict:
"""Generate SEO-optimized metadata for a rental listing."""
keywords = [
f"{unit_type} for rent in {city}",
f"apartments in {city} {state}",
f"{city} rentals under ${int(rent + 200)}",
f"pet friendly apartments {city}" if "pet" in listing.description.lower() else None,
]
keywords = [k for k in keywords if k is not None]
return {
"seo_title": listing.seo_title,
"meta_description": listing.meta_description[:160],
"keywords": keywords,
"og_title": listing.headline,
"og_description": listing.description[:200],
"structured_data": {
"@type": "Apartment",
"name": listing.headline,
"address": {"@type": "PostalAddress", "addressLocality": city},
},
}
Multi-Channel Distribution
Once the listing is generated, we push it to multiple platforms.
from dataclasses import dataclass
@dataclass
class PlatformConfig:
name: str
max_description_length: int
supports_html: bool
photo_limit: int
PLATFORMS = {
"zillow": PlatformConfig("Zillow", 5000, False, 30),
"apartments_com": PlatformConfig("Apartments.com", 3000, True, 25),
"craigslist": PlatformConfig("Craigslist", 2000, True, 12),
"facebook": PlatformConfig("Facebook Marketplace", 1000, False, 10),
}
def adapt_listing_for_platform(
description: str,
platform: str,
) -> str:
"""Adapt listing content for a specific platform's requirements."""
config = PLATFORMS.get(platform)
if not config:
return description
adapted = description[:config.max_description_length]
if not config.supports_html:
# Strip any HTML tags for plain-text platforms
import re
adapted = re.sub(r"<[^>]+>", "", adapted)
return adapted
@function_tool
async def distribute_listing(
listing_content: str,
platforms: str,
) -> str:
"""Distribute a listing to specified platforms."""
platform_list = [p.strip() for p in platforms.split(",")]
results = []
for platform in platform_list:
adapted = adapt_listing_for_platform(listing_content, platform)
results.append(f"Published to {platform} ({len(adapted)} chars)")
return "\n".join(results)
The Complete Listing Agent
listing_agent = Agent(
name="RentalListingAgent",
instructions="""You are a rental listing specialist.
Help property managers create and distribute listings.
Always ensure Fair Housing compliance — no language
that discriminates based on protected classes.""",
tools=[generate_listing_description, distribute_listing],
)
FAQ
How does the agent ensure Fair Housing Act compliance?
The description agent's instructions explicitly prohibit discriminatory language. Additionally, a post-processing step scans for flagged terms (e.g., "perfect for families", "walking distance to church") that could imply preference for protected classes. Flagged content is revised before publishing.
Can the agent update listings when rent prices change?
Yes. The agent can regenerate descriptions with updated pricing while preserving the rest of the content. A update_listing tool would take the existing listing ID and new parameters, regenerate only the changed sections, and republish to all platforms.
How do you handle duplicate listings across platforms?
Each listing gets a unique internal ID. The distribution system tracks which platforms have received each listing and their platform-specific IDs. Updates and deactivations are synchronized across all channels through these mappings.
#RentalListings #PropertyMarketing #SEO #AgenticAI #Python #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.