HOMEX — Condo Rentals in Metro
Manila` and the same `og:description`) — per-listing dynamic ``/OG tags
are **not observed**, i.e. not implemented.
## 3. Data layer & hidden APIs
The site ships pure client-side data fetching: server-rendered HTML for `/units/[id]`
returns only `HOMEX......Loading...` — all listing data is fetched
**after** hydration via `fetch()` calls embedded in the App Router client bundle
(`app/page-26d1c79b0bcddfaa.js`). Confirmed by reading the deobfuscated bundle and by
watching the real browser render:
**Endpoints found (full paths, both directly curl-able and unauthenticated):**
- `GET /api/public/buildings` — returns all buildings.
- `GET /api/public/units?building_id=&city=&tower=&unit_type=&tags=&search=&status=` —
returns units; the homepage's live filter bar calls this with `status=available`
hardcoded plus whichever of `city`, `building_id`, `tower`, `unit_type`, `tags`,
`search` the user has set (single-select each, from the bundle's
`e.set("tower",C)`, `e.set("unit_type",_)`, `e.set("tags",R)`,
`e.set("search",y.trim())` calls).
Both are listed under `Disallow: /api/` in robots.txt (crawlers blocked) but are called
directly by client JS in every page load and return plain JSON with **no auth check** —
loading the site as a normal visitor triggers these exact requests.
**Real response observed, `GET /api/public/buildings`:**
```json
{"buildings":[
{"id":2,"name":"Makati CBD Apartment","city":"Makati","towers":"Tower A"},
{"id":5,"name":"MOA Bayfront Condo","city":"Pasay","towers":"Tower 1, Tower 2"},
{"id":4,"name":"Quezon City Townhouse","city":"Quezon City","towers":null},
{"id":1,"name":"BGC High Street Condo","city":"Taguig","towers":"Tower 1, Tower 2"}
]}
```
**Real response observed, `GET /api/public/units?status=available`** (one record,
field names verbatim):
```json
{"id":2,"building_id":1,"tower":"Tower 1","floor":"22F","area":65,"unit_type":"2BR",
"unit_number":"22B","owner_name":"John Dela Cruz","owner_contact":"+63 918 234 5678",
"owner_email":"landlord@homex.ph","monthly_rent":68000,
"description":"Spacious 2-bedroom unit with balcony.",
"images":"/uploads/units/twobr-living-01.png,/uploads/units/twobr-bedroom-01.png",
"status":"available","created_at":"2026-07-07T12:45:18.684Z","tags":"balcony,split_ac",
"building_name":"BGC High Street Condo","building_city":"Taguig",
"building_towers":"Tower 1, Tower 2","agent_id":2,"agent_name":"Maria Santos",
"agent_avatar":null}
```
**Noteworthy finding — unauthenticated PII exposure:** the public units endpoint
returns `owner_name`, `owner_contact` (a real-looking PH mobile number format
`+63 9xx xxx xxxx`), and `owner_email` for every unit, with no authentication and no
redaction, to any anonymous visitor/crawler that hits the endpoint. This is the kind of
data a listing portal normally keeps server-side and brokers through an agent-contact
form. Flagging as an observed data-shape fact, not something we probed further.
**Inferred field shape (listing/"unit" object):** `id, building_id, tower, floor, area
(sqm, int), unit_type ("Studio"|"1BR"|"2BR"|"3BR"|"4BR" per homepage filter dropdown),
unit_number, owner_name, owner_contact, owner_email, monthly_rent (PHP, int, no
decimals), description, images (comma-joined string of paths, not an array),
status ("available"|"leased"|"maintenance" per `units.statusAvailable/Leased/
Maintenance` i18n keys), created_at (ISO), tags (comma-joined string, nullable —
observed values: `balcony`, `pet`, `split_ac`), building_name, building_city,
building_towers, agent_id, agent_name, agent_avatar`. Images are stored as
comma-separated string, not a JSON array — a schema smell (inferred: backend is a
single flat SQL table joined with buildings, not a normalized media table).
**Sitemap/routing mismatch (observed, notable):** `sitemap.xml` lists 13 URLs — the
homepage, 7 stale `/units?building_id=N` (N=1–7) query-string pages, and 5
`/units/{id}` permalinks (ids 2,3,5,7,9). But `/units?building_id=1` returns a live
**404** (`x-matched-path: /404`, `x-next-error-status: 404`) — the app apparently used
to have a server-rendered `/units` list route and has since moved all listing/filter UI
to client-side state on `/`, without updating (or 301-redirecting) the sitemap. The
`/api/public/buildings` response also only returns 4 buildings even though the sitemap
references building_id up to 7 — buildings 3, 6, 7 exist in the DB/sitemap history but
are no longer surfaced by the public API (inferred: deleted or deliberately hidden).
This is a real, observable SEO/architecture defect worth avoiding in our own build.
## 4. Feature inventory
Enumerated from the live rendered homepage, the unit detail page, and the full embedded
i18n dictionary (`en` locale, ~500 keys, extracted from `_next/static/chunks/
768-1e82e92bd9a4f12c.js`):
- **Search filters** (all on the homepage, single-select dropdowns, no price-range
slider observed): City (Taguig/Makati/Quezon City/Pasay), Condo (building name),
Unit Type (Studio/1BR/2BR/3BR/4BR), Tag (With Balcony/Pet Friendly/Split-type
Aircon), plus a free-text "Search condo name..." box. No bedroom-count vs unit-type
distinction, no bathroom filter, no price filter, no amenities beyond the 3 tags
observed.
- **Map:** not observed. No map library shipped, no lat/lng fields in the unit JSON
shape, no map UI on listing or detail pages.
- **Gallery/media:** image carousel on detail page (‹ › arrows observed in rendered
DOM), 2 images per unit typical, served from `/uploads/units/*.png` — plain file
uploads, not a CDN-transformed media pipeline (images are then re-optimized by
Next.js Image on the way out).
- **Agent/lead contact mechanism:** **"Contact Agent on Telegram"** button on the
detail page (confirmed both in i18n keys `chat.contactAgent`/`chat.telegramPrompt`
and directly in the rendered page for unit 9), plus an agent avatar/name block
("Maria Santos"). Not a phone/WhatsApp click-to-call on the public listing page
itself (WhatsApp is only mentioned in the About-page narrative as the old
pain-point tool agents used before HOMEX, not as the in-product contact channel).
Separately, a general "Contact Us" page offers Email, **Telegram**, and Facebook
channels plus a name/email/message form (`contact.*` keys).
Internally (dashboard-gated) there's also a full agent↔landlord↔tenant **Chat/
Conversations** system (`chat.*` keys — conversations list, send/read state,
"Inquiry about {condo} — Unit {unit}" auto-subject).
- **Saved search / alerts / email digests:** not observed. No saved-search, favorite,
or email-alert strings found anywhere in the i18n dictionary or bundles.
- **User accounts/auth:** full custom auth — Login, Register ("I am a..." role
picker), Forgot Password (email reset link), Reset Password, Change Password
(forced on first login via temp password), all with dedicated i18n strings. Four
roles: Admin, Agent, Landlord, Tenant, each with a distinct dashboard.
- **Monetization:** not observed. No ads, no "featured"/"boosted"/"premium" listing
concept, no subscription/paywall strings anywhere in the bundles. The product's
actual monetization is presumably a **B2B SaaS subscription for
agents/landlords/property managers** to use the rent/lease/commission-tracking
dashboard (inferred from the whole feature set — this is a vertical SaaS, not an ad-
supported classifieds site).
- **i18n & currency:** English / Simplified Chinese (`zh`) toggle only — no Filipino/
Tagalog. Currency is PHP only, formatted as `₱XX,XXX/mo`, no currency switcher
observed.
- **Mobile app:** not observed. No app-store badges, deep-link meta tags, or
smart-banner markers found.
- **Landlord/property-management back-office (dashboard, gated, not directly
crawled but fully inventoried via i18n keys):** rent-record generation with a
payment **schedule** (per-period rows), tenant-uploaded **proof-of-payment**
images with agent/landlord **confirmation** workflow, automatic **overdue
detection** with "all parties notified" alerting, lease creation with **multi-
tenant** support (comma-separated names/emails/phones), configurable **payment
frequency** (monthly/bi-monthly/quarterly/semi-annual/annual/other), lease
**document upload** (contract + receipt, PDF/image/ZIP/RAR/7Z up to 20MB), **agent
commission** tracking with paid/unpaid status and monthly summaries, a
**notifications** center, and an **account-management** screen (admin can
disable/reset-password/delete agent/landlord/tenant accounts, auto-generates
temp passwords). Landlord and tenant accounts are **auto-created** from the email
entered when an admin/agent adds a unit owner or a lease tenant
(`units.ownerEmailHint`, `leases.tenantEmailsHint`) — a genuinely clever
onboarding trick worth studying even though it's out of scope for our static demo.
## 5. UX & content notes
- Homepage layout: sticky header (logo + language toggle + Sign In) → hero band with
headline "Available Units for Rent" / "Find your perfect condo in Metro Manila" +
centered search box → filter bar (4 dropdowns) directly below the hero → result count
("5 units found") → responsive card grid → footer (About · Contact Us · copyright ·
Privacy Policy).
- Unit cards show: price (large, bold, brand-blue `₱`), building name, tag pills
(amber), unit type / area / city, tower + floor chips, and an "Available" status
pill (green).
- Detail page: back-to-listings link, image carousel, price header with status pill,
city, a details grid (unit type/area/tower/floor), free-text description, agent
card with initial-avatar + name + "Contact Agent on Telegram" CTA + "Need help?"
micro-copy, and a "More units in this condo" related-listings rail.
- Brand color is a mid-blue (`theme-color`/`#185FA5`), applied consistently to price
text and primary buttons in the rendered screenshots.
- Copy is confident/startup-y ("Condo Management for Metro Manila", "Four-Role
System", "Synced Reminders") — positions itself to agents/landlords as a management
tool first, listing portal second.
## 6. SEO & metadata
- `` and all meta/OG/Twitter tags are **static and site-wide** — identical on
homepage, listing, and every unit detail page (`HOMEX — Condo Rentals in Metro
Manila` / same description / same `og:image: https://www.homex.ph/og-home.png`).
No per-listing title, description, or OG image — a missed SEO opportunity (a
detail page for a ₱24,000/mo Pasay studio should really title itself around that
unit).
- `robots.txt`: `Allow: /`, `Disallow: /dashboard/`, `Disallow: /api/`,
`Sitemap: https://www.homex.ph/sitemap.xml`.
- `sitemap.xml`: only 13 URLs total (see §3 for the stale-route mismatch finding);
`changefreq`/`priority` set per URL type (home: daily/1.0, building query pages:
weekly/0.5, unit permalinks: weekly/0.8). No hreflang tags anywhere despite having
an EN/ZH toggle — the Chinese UI is **not** exposed via a crawlable `?lang=zh` URL
or hreflang alternate, so it's invisible to search engines (inferred: language
state is pure client-side React state, not URL-driven).
- No JSON-LD (`RealEstateListing`, `Product`, `Organization`, `BreadcrumbList`, etc.)
anywhere — a meaningful SEO gap relative to how a real property portal should mark
up listings for Google's rich-results / real-estate schema.
## 7. Takeaways for property-portal
1. **Per-listing SEO is worth doing properly, unlike HOMEX.** Since property-portal
is a static export, we can trivially generate a unique ``/meta
description/OG image per listing at build time — HOMEX's identical-meta-everywhere
approach is a concrete anti-pattern to avoid. Consider adding `RealEstateListing`
JSON-LD per detail page (HOMEX has none) — cheap win for a demo that wants to look
more finished than a real competitor.
2. **Keep contact info agent-brokered, never expose raw owner PII in a public API/
payload.** HOMEX's `/api/public/units` leaking `owner_name/owner_contact/
owner_email` unauthenticated is exactly the kind of mistake to call out and avoid;
our mock data/contact flow should route through an "agent"/"contact form"
abstraction even in the static demo, not raw owner records.
3. **A single clear lead-contact CTA (their "Contact Agent on Telegram") beats a
vague "inquire" form.** For our booking/viewing stub, one prominent, channel-
specific CTA per listing (we already have a booking stub — keep it singular and
obvious, mirroring this pattern) reads more credible than a generic contact form
buried lower on the page.
4. **Don't ship filters we can't back with data.** HOMEX's filter set is deliberately
small (city, building, unit type, one tag dropdown, free text) and every option is
backed by real distinct values in their ~9-unit dataset — no filter offers choices
that return zero results. With our ~50 mock listings we have more room, but the
lesson holds: match filter granularity (price range, bedrooms, amenities) to what
the mock data actually varies across, and sanity-check that no filter combination
dead-ends.
5. **Map + saved-search/alerts are real differentiators to keep**, since HOMEX has
neither — our Leaflet map and saved-search-alert feature already out-do this
specific competitor and should be emphasized in any demo narrative that references
it.
6. **Keep the sitemap/routing in sync with actual routes** — HOMEX's sitemap
references a now-404'ing `/units?building_id=N` route, a visible, easy-to-avoid
bug. Since our site is a static export, regenerate the sitemap from the same data
source that generates the static listing pages so this class of drift is
structurally impossible.
## Matrix row
Category | Framework | Host/CDN | Search filters | Map | Saved search/alerts | Agent/lead contact | Auth | Listing data source | Notable API endpoints | Monetization | i18n/currency | Mobile app | Standout feature
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
Condo rental management SaaS (listing micro-site is a lead-gen front end) | Next.js (App Router, RSC) | Vercel (direct, no CDN in front) | City, Condo/building, Unit Type (Studio–4BR), single Tag dropdown, free-text search | not observed | not observed | "Contact Agent on Telegram" button + agent name/avatar; separate Email/Telegram/Facebook contact page | Custom email+password, 4 roles (Admin/Agent/Landlord/Tenant), forced password change on first login, auto-created landlord/tenant accounts from email | Client-fetched JSON from own `/api/public/*` endpoints (Postgres/MySQL-style flat rows, joined with building) | `GET /api/public/buildings`, `GET /api/public/units?building_id=&city=&tower=&unit_type=&tags=&search=&status=` | not observed (inferred B2B SaaS subscription for agents/landlords, not ads) | EN / Simplified Chinese toggle (client-state only, no hreflang); PHP only, no currency switcher | not observed | Full back-office: rent-payment schedule + proof-of-payment confirmation workflow, agent commission tracking, multi-tenant lease documents, auto-account-creation from email