MCP server
Connect an MCP client to Relmo over streamable HTTP.
The MCP server is the main way to connect an AI agent. It uses the streamable HTTP transport.
- Endpoint:
https://api.relmo.co/v1/mcp - Auth:
Authorization: Bearer <connector key>. See Authentication. - Server name:
relmo-listings
Tool descriptions and server instructions are served through MCP itself, so your client learns the recommended flow and the rules on data freshness and images when it connects.
Connect a client
Claude Code
claude mcp add --transport http relmo https://api.relmo.co/v1/mcp \
--header "Authorization: Bearer <connector key>"Other clients
Any client that supports remote streamable-HTTP MCP servers with custom headers works. Point it at
the endpoint above and set the Authorization header.
{
"mcpServers": {
"relmo": {
"url": "https://api.relmo.co/v1/mcp",
"headers": { "Authorization": "Bearer <connector key>" }
}
}
}The config format varies by client. Check its docs for how to add a remote MCP server.
Tools
suggest
Resolve a place or developer name to a slug. Call it first when the buyer names a city, locality or developer.
| Param | Type | Description |
|---|---|---|
q | string | Name to resolve, at least 2 characters. Required |
Returns matches grouped by kind. Localities with published listings come first in
popularLocalities; the rest are in localities.
{
"popularLocalities": [
{
"name": "Worli Seaface",
"slug": "worli-seaface",
"citySlug": "mumbai",
"cityName": "Mumbai",
"ancestorPath": [
{ "name": "Worli", "slug": "worli" },
{ "name": "South Mumbai", "slug": "south-mumbai" }
]
}
],
"localities": [],
"cities": [{ "slug": "mumbai", "name": "Mumbai" }],
"developers": [{ "slug": "lodha", "name": "Lodha" }],
"projects": [
{
"id": "…",
"name": "…",
"publicShortId": "…",
"localityName": "Worli",
"cityName": "Mumbai"
}
]
}ancestorPath runs from the immediate parent up to the root and excludes the city. A parent slug
covers its whole subtree, so for "Worli" search at worli (which includes Worli Seaface) rather
than listing each sub-area. Don't combine every similar name either: "Bandra" must not pull in
"Bandra Kurla Complex". See Recommended flow.
market_stats
Price, size and amenity figures for every matching project, not just one page. Takes the search filters.
{
"totalCount": 42,
"priceMin": 25000000,
"priceMedian": 68000000,
"priceMax": 350000000,
"priceP25": 42000000,
"priceP75": 110000000,
"carpetAreaMin": 610,
"carpetAreaMax": 4200,
"bedroomsAvailable": [2, 3, 4],
"readyCount": 9,
"underConstructionCount": 33,
"amenityCoverage": [{ "key": "swimming_pool", "count": 31 }],
"examples": [{ "name": "…", "localityName": "Worli", "publicShortId": "…" }]
}Prices are in rupees (₹) and areas in sq ft. amenityCoverage lists up to 12 amenities by how many
projects offer them; examples has up to 5 projects.
search_listings
List matching projects, up to 12 per page. Takes the search filters plus sort
and page.
| Param | Type | Description |
|---|---|---|
sort | enum | recommended (default), price_asc, price_desc, value_asc or possession_asc |
page | integer | 1-based page number. Default 1 |
{
"items": [
{
"id": "…",
"publicShortId": "…",
"name": "…",
"city": { "id": "…", "name": "Mumbai", "slug": "mumbai" },
"locality": { "id": "…", "name": "Worli", "slug": "worli" },
"salesStatus": "bookings_open",
"constructionStatus": "under_construction",
"possessionDate": "2028-12-31",
"bedroomsAvailable": [3, 4],
"priceFrom": 65000000,
"priceTo": 140000000,
"pricePerSqftMin": 52000,
"pricePerSqftMax": 61000,
"carpetAreaMin": 1250,
"carpetAreaMax": 2300,
"heroImageUrl": "https://…",
"heroImageBlurDataUrl": "data:image/…"
}
],
"totalCount": 42,
"page": 1,
"perPage": 12,
"totalPages": 4,
"scopeCounts": { "inSearchQuery": 42, "aroundSearchQuery": 0 }
}- Pass
idtocreate_enquiryandpublicShortIdtoget_listing. - A
nullpriceFrommeans price on request.bedroomsAvailableuses0for a studio. - Use
totalCountto tell the buyer "N of M".
If a search filtered by city, localities or developer matches nothing, the response also
carries a notice ({ "code": "no_matches", "message": "…" }). The usual cause is a guessed slug:
resolve the name with suggest and retry.
get_listing
Full detail for one project: phases, configurations, amenities and media.
| Param | Type | Description |
|---|---|---|
slug | string | The publicShortId from a search result or a market_stats example, or a full canonical slug |
An unknown slug returns an error. Resolve the name with suggest or search_listings and
retry with the slug it returns.
create_enquiry
Request a callback or book a site visit. The developer's sales team arranges the callback or visit.
| Param | Type | Description |
|---|---|---|
project_id | string | The id from a search result. Required |
action | enum | request_callback or book_visit. Required |
name | string | Buyer's name. Required |
phone | string | Buyer's phone number. Required |
platform | string | Your agent or platform name, e.g. instinct or claude |
submission_id | UUID | Idempotency key. Reuse the same UUID when retrying the same enquiry |
Generate a fresh submission_id for each new enquiry and resend it unchanged on retries (for
example after a network timeout). Repeats with the same key collapse into one, so a retry never
double-books the buyer. The value must be a valid UUID; anything else is rejected.
Returns { "captured": true, "leadId": "..." }. Sometimes it returns
{ "captured": false, "leadId": null } instead. Treat that as received too.
Search filters
Shared by market_stats and search_listings. All are optional.
| Param | Type | Description |
|---|---|---|
city | string | City slug (resolve names with suggest) |
localities | string[] | Locality slugs. Requires city |
developer | string | Developer slug |
bhk | number[] | Bedroom counts. 0 is a studio |
price_min, price_max | number | Price in rupees (₹) |
carpet_min, carpet_max | number | Carpet area in sqft |
lat, lng | number | Pin for "near here" search. Provide both together |
radius_km | number | Radius around the pin. Default 3, max 25. Requires lat/lng |
sales_status | enum[] | pre_launch, bookings_open, on_hold, sold_out |
construction_status | enum[] | planned, under_construction, ready_to_move, on_hold, complete |
amenities | string[] | Amenity keys, AND-ed. Allowed values are a fixed vocabulary published in the tool's schema |
possession_before | string | Handover on or before YYYY-MM-DD |
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid connector key |
429 | Rate limit exceeded (300 requests/minute/IP) |
Invalid arguments come back as a tool error, not an HTTP status. That includes a submission_id
that isn't a UUID and an unknown project_id.