A working Next.js 15 App Router application built on the Bayut Property Data API: debounced location autocomplete, property search, and result cards, in TypeScript.
Built and maintained by Happy Endpoint. Full API reference at bayutapi.dev.
| Path | What it is |
|---|---|
lib/bayut.ts |
Typed API client with retries, timeouts, and error handling |
app/api/locations/route.ts |
Route handler proxying location autocomplete |
app/api/properties/route.ts |
Route handler proxying property search |
components/LocationSearch.tsx |
Debounced autocomplete with request cancellation |
components/PropertyCard.tsx |
Listing card with AED formatting and unit conversion |
app/page.tsx |
The search page tying it together |
This builds and runs. npm run build produces a clean production build, and
both route handlers return live data.
git clone https://github.com/happyendpointhq/bayut-api-javascript-nextjs
cd bayut-api-javascript-nextjs
npm install
cp .env.example .env.local
# then add your key to .env.local
npm run devOpen http://localhost:3000, search for an area, and pick it from the dropdown.
Get a key, free tier available, at https://rapidapi.com/happyendpoint/api/uae-real-estate3/
This matters more than anything else in this repo.
RAPIDAPI_KEY=your_key_here # correct, server only
NEXT_PUBLIC_RAPIDAPI_KEY=... # wrong, ships to every visitor
Anything prefixed NEXT_PUBLIC_ is inlined into the client bundle and readable
by anyone who opens devtools. A leaked RapidAPI key gets used, and you pay for
the usage.
The pattern here is that lib/bayut.ts is only imported by route handlers, which
run on the server. Client components call /api/properties on your own origin,
never the Bayut API directly.
import { searchProperties, autocomplete, text, LOCATIONS } from '@/lib/bayut';
// Resolve an area name to a location ID
const locations = await autocomplete('dubai marina');
const locationId = locations[0].externalID;
// Or use a verified constant
const results = await searchProperties({
purpose: 'for-sale',
locationId: LOCATIONS.dubaiMarina,
propertyType: 'apartments',
rooms: '1',
priceMax: 1_500_000,
});
console.log(`${results.total} properties`);
for (const property of results.properties) {
console.log(text(property.title), property.price);
}import { searchProperties, text, LOCATIONS } from '@/lib/bayut';
export default async function MarinaListings() {
const { properties } = await searchProperties({
locationId: LOCATIONS.dubaiMarina,
rooms: '1',
});
return (
<ul>
{properties.map((property) => (
<li key={property.externalID}>
{text(property.title)} - AED {property.price.toLocaleString()}
</li>
))}
</ul>
);
}The API is not consistent between endpoints, which is why lib/bayut.ts exports
helpers rather than leaving you to parse raw responses.
title has two shapes. /search-property returns {title: {en: "..."}}
while /property-details returns title as a plain string. Use text(), which
handles both.
location is a hierarchy array, ordered by level: country, city, community,
building. Use locationPath() to render it as UAE > Dubai > Dubai Marina > Studio One Tower.
/property-details is slow, routinely over 30 seconds against sub-second
search responses. The client raises its timeout automatically for that path.
Rental prices are annual. PropertyCard appends /year when purpose is
for-rent so the figure is not mistaken for a monthly rent.
Exported as LOCATIONS from lib/bayut.ts.
| Area | ID |
|---|---|
| Dubai (whole emirate) | 5002 |
| Abu Dhabi (whole emirate) | 6020 |
| JVC | 5416 |
| Business Bay | 5093 |
| Downtown Dubai | 6901 |
| Dubai Marina | 5003 |
| Dubai Hills Estate | 8288 |
| JLT | 5152 |
| Palm Jumeirah | 5460 |
A wrong location ID returns a different area rather than an error, so
confirm any you add through /autocomplete rather than guessing.
Requests set next: { revalidate: 300 }, so Next caches responses for five
minutes. Listings change often enough that indefinite caching would show stale
prices, and often enough that no caching burns quota. Adjust in lib/bayut.ts
to suit your plan.
- Push your fork to GitHub
- Import it in Vercel
- Add
RAPIDAPI_KEYas an environment variable, not prefixedNEXT_PUBLIC_ - Deploy
The route handlers run as serverless functions, so the key stays server side.
You can, but do not. It would require shipping the key to the browser. Use a route handler, as this example does.
Almost certainly a wrong location ID. The API returns results for whatever ID
you send rather than rejecting an unknown one. Use /api/locations to confirm.
The client in lib/bayut.ts does, unchanged. The route handlers would need
rewriting as pages/api handlers, and next: { revalidate } has no effect
outside the App Router.
Yes. bayut-api-python-examples.
RapidAPI hosts an MCP server, so you can query this API from an AI assistant without writing code:
{
"mcpServers": {
"Bayut UAE Real Estate": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.rapidapi.com",
"--header",
"x-api-host: uae-real-estate3.p.rapidapi.com",
"--header",
"x-api-key: YOUR_RAPIDAPI_KEY"
]
}
}
}- bayut-api - full endpoint documentation
- bayut-api-python-examples - the same patterns in Python
- bayut-api-postman-collection - try the endpoints without writing code
- dubai-rental-yield-calculator - yields by area
- uae-real-estate-data-guide - every route to UAE property data
Happy Endpoint is an independent provider. This project is not affiliated with, endorsed by, sponsored by, or connected to any of the websites, platforms, retailers, or marketplaces referenced here or reachable through the underlying APIs.
All product names, brands, trademarks, and registered trademarks are the property of their respective owners. Any reference to them is descriptive only, to identify the subject matter of the data, and does not imply any association or endorsement.
Users are responsible for ensuring their use of any data complies with applicable laws and the terms of service of the relevant source.
Happy Endpoint builds and maintains real-time data APIs for property portals, retailers, and marketplaces. All APIs are available on RapidAPI with a free tier.
- Catalogue: happyendpoint.com/library
- Datasets: happyendpoint.com/datasets
- Documentation: docs.happyendpoint.com
- Contact: happyendpointhq@gmail.com
MIT. See LICENSE.