---
title: "Building a Property Inquiry Agent: Answering Buyer Questions About Listings 24/7"
description: "Learn how to build an AI agent that answers buyer questions about property listings around the clock, including database lookups, FAQ handling, photo sharing, and automated showing scheduling."
canonical: https://callsphere.ai/blog/building-property-inquiry-agent-answering-buyer-questions-listings-24-7
category: "Learn Agentic AI"
tags: ["Real Estate AI", "Property Inquiry", "Agentic AI", "Python", "Chatbot"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-06T01:02:44.253Z
---

# Building a Property Inquiry Agent: Answering Buyer Questions About Listings 24/7

> Learn how to build an AI agent that answers buyer questions about property listings around the clock, including database lookups, FAQ handling, photo sharing, and automated showing scheduling.

## Why Real Estate Needs 24/7 Inquiry Agents

The average buyer browses listings at 9 PM on a Tuesday. By the time an agent responds the next morning, that buyer has already messaged three competitors. A property inquiry agent eliminates this gap by answering questions about listings, sharing photos, and scheduling showings instantly — no matter the hour.

In this guide, we will build a property inquiry agent that connects to a listing database, handles common buyer questions, serves property photos, and books showings automatically.

## Designing the Listing Database Layer

Every property inquiry agent starts with structured access to listing data. We will use a simple schema and a retrieval layer that the agent can call as a tool.

```mermaid
flowchart LR
    CALLER(["Buyer or Seller Lead"])
    subgraph TEL["Telephony"]
        SIP["Twilio SIP and PSTN"]
    end
    subgraph BRAIN["Real Estate AI Agent"]
        STT["Streaming STT
Deepgram or Whisper"]
        NLU{"Intent and
Entity Extraction"}
        TOOLS["Tool Calls"]
        TTS["Streaming TTS
ElevenLabs or Rime"]
    end
    subgraph DATA["Live Data Plane"]
        CRM[("CRM and Notes")]
        CAL[("Calendar and
Schedule")]
        KB[("Knowledge Base
and Policies")]
    end
    subgraph OUT["Outcomes"]
        O1(["Showing scheduled"])
        O2(["Lead routed to agent"])
        O3(["Pre-qual handed to broker"])
    end
    CALLER --> SIP --> STT --> NLU
    NLU -->|Lookup| TOOLS
    TOOLS  CRM
    TOOLS  CAL
    TOOLS  KB
    NLU --> TTS --> SIP --> CALLER
    NLU -->|Resolved| O1
    NLU -->|Schedule| O2
    NLU -->|Escalate| O3
    style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
    style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
    style O1 fill:#059669,stroke:#047857,color:#fff
    style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
    style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
```

```python
import asyncpg
from dataclasses import dataclass
from typing import Optional

@dataclass
class PropertyListing:
    listing_id: str
    address: str
    price: float
    bedrooms: int
    bathrooms: float
    sqft: int
    description: str
    photo_urls: list[str]
    status: str  # active, pending, sold
    listing_agent: str

class ListingDatabase:
    def __init__(self, pool: asyncpg.Pool):
        self.pool = pool

    async def search_listings(
        self,
        min_price: Optional[float] = None,
        max_price: Optional[float] = None,
        min_beds: Optional[int] = None,
        city: Optional[str] = None,
        limit: int = 10,
    ) -> list[PropertyListing]:
        conditions = ["status = 'active'"]
        params = []
        idx = 1

        if min_price is not None:
            conditions.append(f"price >= ${idx}")
            params.append(min_price)
            idx += 1
        if max_price is not None:
            conditions.append(f"price = ${idx}")
            params.append(min_beds)
            idx += 1
        if city is not None:
            conditions.append(f"LOWER(city) = LOWER(${idx})")
            params.append(city)
            idx += 1

        where_clause = " AND ".join(conditions)
        query = f"""
            SELECT * FROM listings
            WHERE {where_clause}
            ORDER BY created_at DESC
            LIMIT {limit}
        """
        rows = await self.pool.fetch(query, *params)
        return [PropertyListing(**dict(r)) for r in rows]

    async def get_listing(self, listing_id: str) -> Optional[PropertyListing]:
        row = await self.pool.fetchrow(
            "SELECT * FROM listings WHERE listing_id = $1",
            listing_id,
        )
        return PropertyListing(**dict(row)) if row else None
```

This layer gives the agent parameterized search capabilities. The key design choice is returning structured data rather than raw SQL rows so the agent can format responses naturally.

## Building the Agent with Tools

Now we wire the database into an agent using tool functions. Each tool handles a specific buyer intent.

```python
from agents import Agent, Runner, function_tool

listing_db: ListingDatabase  # initialized at startup

@function_tool
async def search_properties(
    city: str,
    max_price: float = None,
    min_bedrooms: int = None,
) -> str:
    """Search available properties by city, price range, and bedroom count."""
    results = await listing_db.search_listings(
        city=city,
        max_price=max_price,
        min_beds=min_bedrooms,
        limit=5,
    )
    if not results:
        return "No matching properties found. Try broadening your search."
    lines = []
    for p in results:
        lines.append(
            f"- {p.address}: {p.bedrooms}bd/{p.bathrooms}ba, "
            f"{p.sqft} sqft, ${p.price:,.0f} (ID: {p.listing_id})"
        )
    return "\n".join(lines)

@function_tool
async def get_property_details(listing_id: str) -> str:
    """Get full details and photos for a specific listing."""
    p = await listing_db.get_listing(listing_id)
    if not p:
        return "Listing not found."
    photos = "\n".join(p.photo_urls[:5])
    return (
        f"Address: {p.address}\n"
        f"Price: ${p.price:,.0f}\n"
        f"Beds/Baths: {p.bedrooms}/{p.bathrooms}\n"
        f"Sqft: {p.sqft}\n"
        f"Description: {p.description}\n"
        f"Photos:\n{photos}"
    )

@function_tool
async def schedule_showing(
    listing_id: str,
    buyer_name: str,
    buyer_phone: str,
    preferred_date: str,
) -> str:
    """Schedule a property showing for a buyer."""
    # In production, this writes to a calendar/CRM system
    return (
        f"Showing scheduled for {buyer_name} at listing "
        f"{listing_id} on {preferred_date}. "
        f"A confirmation will be sent to {buyer_phone}."
    )

property_agent = Agent(
    name="PropertyInquiryAgent",
    instructions="""You are a helpful real estate assistant. Answer
    questions about available properties using the search and detail
    tools. When a buyer is interested, offer to schedule a showing.
    Always be accurate — never invent property details.""",
    tools=[search_properties, get_property_details, schedule_showing],
)
```

## Handling FAQs with a Knowledge Base

Many buyer questions are not about specific listings but about process — closing costs, inspection timelines, mortgage pre-approval. We handle these with a lightweight FAQ retrieval tool.

```python
FAQ_DATA = {
    "closing_costs": "Typical closing costs range from 2-5% of the purchase price...",
    "inspection": "Home inspections usually occur within 7-10 days of accepted offer...",
    "preapproval": "Mortgage pre-approval typically requires pay stubs, tax returns...",
}

@function_tool
async def lookup_faq(topic: str) -> str:
    """Look up common real estate FAQs by topic keyword."""
    topic_lower = topic.lower()
    for key, answer in FAQ_DATA.items():
        if key in topic_lower or topic_lower in key:
            return answer
    return "I do not have a specific FAQ on that topic. Let me connect you with an agent."
```

This approach keeps the agent grounded in verified information rather than hallucinating answers about legal or financial topics.

## Running the Agent

```python
import asyncio

async def main():
    result = await Runner.run(
        property_agent,
        input="I am looking for a 3-bedroom house in Austin under $500k",
    )
    print(result.final_output)

asyncio.run(main())
```

The agent will call `search_properties` with the extracted parameters and present matching listings in a conversational format.

## FAQ

### How does the agent handle questions about properties not in the database?

The agent is instructed to never fabricate details. If a listing is not found, it responds honestly and suggests broadening the search or contacting a human agent for off-market properties.

### Can this agent handle multiple languages for international buyers?

Yes. Since the underlying LLM supports multilingual input and output, you can add an instruction to detect the buyer's language and respond accordingly. The database queries remain the same — only the presentation layer changes.

### What happens when the agent cannot answer a question?

The FAQ tool returns a fallback message suggesting human escalation. You can extend this by adding a handoff to a live agent tool that creates a callback request in your CRM.

---

#RealEstateAI #PropertyInquiry #AgenticAI #Python #Chatbot #LearnAI #AIEngineering

---

Source: https://callsphere.ai/blog/building-property-inquiry-agent-answering-buyer-questions-listings-24-7
