Skip to main content

happyendpoint

Python client for Happy Endpoint real-time data APIs: real estate listings and transactions, ecommerce and retail product data, and travel pricing. One key, one client, every data domain.

Beta. This is a 0.x release. The API surface may change before 1.0.

pip install happyendpoint
from happyendpoint import HappyEndpoint

he = HappyEndpoint()   # reads RAPIDAPI_KEY

results = he.realestate.search("dubai marina", bedrooms="1", price_max=1_500_000)
print(f"{results.total} matching, median {results.median_price:,.0f}")

for prop in results[:3]:
    print(prop.title)
    print(f"  {prop.price:,.0f} | {prop.area_sqm}sqm | {prop.price_per_sqm:,}/sqm")

Install and authenticate

pip install happyendpoint

Get a key at rapidapi.com/user/happyendpoint. One key works across every API and each has a free tier, but you subscribe to each API separately.

he = HappyEndpoint()                    # from RAPIDAPI_KEY
he = HappyEndpoint(api_key="your_key")  # explicit

Requires Python 3.9 or newer.


Data domains

Clients are grouped by what the data is about, not by which site it came from.

Attribute Covers
he.realestate Property listings, transactions, agents, off-plan developments
he.beauty Beauty and cosmetics product catalogues, pricing, reviews
he.home Home furnishing and furniture catalogues across several countries
he.travel Hotels, flights, and car rental pricing

Domain aliases are the recommended way in. Source-specific attributes exist for anyone who needs a particular provider, but the aliases read better and stay stable if the underlying source for a domain changes.


Areas, not IDs

Property search endpoints take a numeric location id, and passing a wrong one returns a different area rather than an error. Results look plausible and are quietly about the wrong place.

So this client takes names and resolves them, then reports what it matched:

results = he.realestate.search("jvc")
print(results.location.name, results.location.id)

Ambiguous names resolve to the busiest match, which is almost always the community rather than a building sharing its name. To choose yourself:

for loc in he.realestate.find_locations("marina", limit=5):
    print(f"{loc.name:<40} {loc.id:<8} {loc.listings:,} listings")

results = he.realestate.search(he.realestate.find_locations("marina")[2])

Unresolvable names raise LocationNotFound rather than falling back to a guess.


Rental yields

Gross yield across several areas is not a single API call. This computes it:

for row in he.realestate.compare_yields(["jvc", "business bay", "downtown dubai"]):
    print(f"{row.area:<34} gross {row.gross_yield_pct:>5}%  net {row.net_yield_pct():>5}%")

net_yield_pct() subtracts typical holding costs and takes overrides:

row.net_yield_pct(
    service_charge_per_sqm=215,
    area_sqm=90,
    management_pct=5,
    maintenance_pct=1,
    vacancy_pct=5,
)

Defaults are deliberately realistic rather than flattering. The gap between gross and net is usually 2 to 3 percentage points.


Transactions versus listings

The distinction that matters most for analysis:

listings = he.realestate.search("dubai marina")       # what sellers ASK
txns = he.realestate.transactions("dubai marina")     # what buyers PAID

for t in txns[:3]:
    print(f"{t.date}  {t.amount:>12,.0f}  {t.price_per_sqm:>8,.0f}/sqm  {t.sale_type}")

sale_type distinguishes a developer's first sale from an owner resale, which matters when comparing new-build against existing stock.


Normalised results

Upstream endpoints disagree with each other about casing, nesting, and envelope shape. This client hides that behind consistent dataclasses:

prop.title            # str, whichever shape the endpoint returned
prop.price_per_sqm    # computed
prop.area_sqft        # converted
prop.location         # readable hierarchy path
prop.amenities        # flattened out of nested groups
prop.is_annual_rent   # rentals are quoted yearly, easy to misread
prop.raw              # the untouched payload, for fields not modelled here

API reference

Real estate, he.realestate

Method Returns
search(location, purpose, property_type, bedrooms, price_min, price_max, page) SearchResult
get_property(property_id) Property, slow endpoint
search_off_plan(location, price_max, max_pre_handover_payment) SearchResult
iter_all(location, max_pages, delay, **kwargs) iterator of Property
transactions(location, purpose, time_period, page) list[Transaction]
find_agents(location, purpose) list[Agent]
find_locations(query, limit) list[Location]
resolve_location(query) Location
rental_yield(location, bedrooms, property_type) YieldResult or None
compare_yields(locations, bedrooms, property_type) list[YieldResult], ranked

Retail, he.beauty and he.home

he.beauty.search("moisturizer")             # note: `keyword`, not `query`, upstream
he.beauty.search_by_brand("gucci")          # brand name, not an id
he.beauty.reviews(product_id)
he.beauty.availability(sku_id, latitude, longitude)
he.beauty.stores(latitude, longitude, radius=50)

he.home.search("desk", country_code="us")
he.home.search_filters("desk")              # facets, for building filter UIs
he.home.countries()

Home endpoints require both a country and a language code. language_code defaults to "en"; omitting it upstream returns a validation error rather than falling back, so the client always sends it.

Home furnishing data covers eight countries, and the same product is priced differently in each, so cross-market comparison is a couple of calls:

for country in ("us", "gb", "de", "se"):
    print(country, he.home.search("bookcase", country_code=country))

Travel, he.travel

locations = he.travel.find_location("new york")
hotels = he.travel.search_hotels(
    location_id="3000016152",
    check_in="2026-03-05",     # ISO in, converted internally
    check_out="2026-03-07",
    adults=2,
)

Pass ISO dates. Hotel endpoints expect MM-DD-YYYY while car endpoints expect YYYY-MM-DD; the client handles the difference.


Errors

from happyendpoint import (
    HappyEndpointError,    # base class
    AuthenticationError,   # 401, key missing or wrong
    SubscriptionError,     # 403, not subscribed or quota exhausted
    RateLimitError,        # 429 after retries
    LocationNotFound,      # no area matched
)

SubscriptionError names the API and links its subscription page, because "403" alone does not tell you which API you need.

Rate limits and timeouts retry with exponential backoff. Tune with HappyEndpoint(max_retries=5, timeout=45).


Paging

page_two = he.realestate.search("jvc", page=2)

for prop in he.realestate.iter_all("jvc", max_pages=5, delay=0.5):
    print(prop.title)

iter_all sleeps between requests so you stay inside the free tier. Drop max_pages to fetch everything.


FAQ

Do I need a paid plan?

No. Every API has a free tier, enough to explore and prototype.

Why does a call raise SubscriptionError?

You are subscribed to some APIs but not the one you called. The message names it and links the page. HappyEndpoint.available_apis() lists all of them.

Are rental prices monthly or annual?

Annual. prop.is_annual_rent tells you, so you do not divide by twelve without noticing.

Which price should I use for valuation?

Transactions. search() returns asking prices, which run higher than what property actually sells for.

Can I get the untouched API response?

Yes, prop.raw, txn.raw, and agent.raw hold the original payload.

Is there a JavaScript version?

Yes, happyendpoint-js. There is also an MCP server for AI assistants, happyendpoint-mcp.


Development

git clone https://github.com/happyendpointhq/happyendpoint-python
cd happyendpoint-python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

Tests mock HTTP, so they run without a key.


Status

Beta, 0.x. The public API may change before 1.0. Pin a version if you need stability:

happyendpoint==0.2.2

Disclaimer

Happy Endpoint is an independent provider. This package is not affiliated with, endorsed by, sponsored by, or connected to any of the websites, platforms, retailers, or marketplaces whose data may be accessible 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.


About Happy Endpoint

Happy Endpoint builds and maintains real-time data APIs across real estate, ecommerce, retail, and travel.

Licence

MIT. See LICENSE.

Release files for happyendpoint 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for happyendpoint 0.2.2
File Size Uploaded
happyendpoint-0.2.2.tar.gz 23.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for happyendpoint 0.2.2
File Interpreter ABI Platform
happyendpoint-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 43.0 kB

Release files / happyendpoint-0.2.2.tar.gz

Download URL happyendpoint-0.2.2.tar.gz
Size 23.7 kB
Tags Source
SHA-256 checksum
How to use checksums
07f44ec8bf63913a67d4c321cdb375b947ed302f17d50ddbb7a30f3946a25411
BLAKE2b-256 checksum
How to use checksums
6d765da0b5f18611f5d5d880d57f7f42e3dd41cfb19f8ede543f8a3002fa66ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / happyendpoint-0.2.2-py3-none-any.whl

Download URL happyendpoint-0.2.2-py3-none-any.whl
Size 19.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d84b2de0162d769f1933190d66a9d9ff4e8c8d304fd265356d1abe2634a7411e
BLAKE2b-256 checksum
How to use checksums
e3abb349e0c69372344d45b436dc2ff46397e218f0e2270af36d7462a0ac9a1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page