MLS.PH Developer API

Free, public, read-only endpoints for Philippine property market statistics, the complete PSGC location hierarchy, and listing lookup by MLS number. No API key, no registration.

Getting started

Every endpoint on this page is a plain HTTPS GET returning JSON. There is nothing to install and nothing to sign up for — the request below works right now.

curl -s https://www.mls.ph/api/property-stats

Base URL
https://www.mls.ph

Authentication

None. The endpoints documented here are public and read-only, and they never return seller contact details.

The remainder of the MLS.PH API — creating listings, offers, inquiries, viewing appointments and profile data — is private. Those routes require an authenticated MLS.PH session and are not part of this public surface, so please do not build against them.

Write operations (POST)

The public API is read-only. Every endpoint on this page is a GET. There is no public POST, PUT or DELETE endpoint, and nothing you can call here changes data.

MLS.PH does run write endpoints — publishing a listing, sending an inquiry, submitting an offer, requesting a viewing — but they are first-party. They authenticate with a Firebase ID token minted for a signed-in MLS.PH user inside our own web app, and there is no API key exchange or OAuth flow that issues one to a third party. In practice that means you cannot create a listing, contact a seller, or submit an offer programmatically through this API.

Please don’t script the website’s own forms. Driving the public site’s offer or viewing-request forms automatically sends real email to real property sellers. Automated submissions are treated as abuse and blocked. If you have a genuine need to write to MLS.PH, talk to us and we will set up a proper integration.

Building something that needs to push inventory into MLS.PH or pull leads back out? That is a partner integration — see rate limits & fair use and get in touch.

Conventions

  • HTTPS only. Plain HTTP requests are redirected.
  • JSON, UTF-8. Responses are application/json. Place names carry Filipino and Spanish diacritics — Biñan, Parañaque, Sarangani — so decode as UTF-8.
  • Response envelope. The location and listing endpoints wrap their payload in { "success": true, ... }. The statistics endpoint is the one exception and returns its object at the top level.
  • Money is a string. Listing prices are serialized as decimal strings such as "8500000" to avoid float rounding. All amounts are in Philippine pesos (PHP). Values inside the statistics endpoint are rounded integers.
  • No CORS. MLS.PH does not send Access-Control-Allow-Origin, so cross-origin calls from browser JavaScript will be blocked. Call these endpoints from your server or through your own proxy.
  • Caching. Responses carry Cache-Control headers — one hour for statistics, one day for location data. Please honour them rather than re-fetching on every request.

Location codes (PSGC)

Locations use the Philippine Standard Geographic Code — the same 9-digit codes the PSA publishes — so they join cleanly against other government datasets. The hierarchy nests: each level refines the one above it.

LevelExampleMeaning
Region010000000Ilocos Region — 17 in total
Province012800000Ilocos Norte — 81 in total
City / municipality012801000Adams — 1,634 in total
Barangay012801001Adams (Pob.) — loaded per region

A barangay’s parent city code is its first six digits followed by 000, and a region code is its first two digits followed by seven zeroes. Note that NCR cities sit directly under a region with no province, so province can be null on a listing.

Endpoint reference

GET/api/property-stats

Property market statistics

Aggregate market data across every active listing on MLS.PH — totals by transaction type and category, average prices, the top 20 cities by inventory, and price distribution. Recomputed on request and cached at the edge for one hour.

Takes no parameters.

Example response

{
  "generatedAt": "2026-09-18T04:21:07.518Z",
  "source": "MLS.ph - Philippine Real Estate Marketplace",
  "url": "https://www.mls.ph",
  "overview": {
    "totalActiveListings": 1284,
    "forSale": 1109,
    "forRent": 175
  },
  "byPropertyCategory": {
    "counts": { "RESIDENTIAL": 812, "LAND_LOT": 301, "COMMERCIAL": 128 },
    "averagePrices": { "RESIDENTIAL": 8420000, "LAND_LOT": 3150000 }
  },
  "topCitiesByListings": [
    { "city": "Quezon City", "activeListings": 146 }
  ],
  "averagePriceByCity": [
    { "city": "Makati", "averagePrice": 24800000 }
  ],
  "averagePricePerSqmByCity": [
    { "city": "Taguig", "avgPricePerSqm": 215000, "listingsCount": 12 }
  ],
  "priceDistribution": {
    "under1M": 63, "1M-5M": 402, "5M-10M": 311,
    "10M-50M": 288, "over50M": 45
  },
  "recentActivity": { "newListingsLast7Days": 37 },
  "propertyTypes": ["Residential", "Land/Lot", "Commercial", "Industrial", "Others"],
  "transactionTypes": ["For Sale", "For Rent"],
  "coverage": { "regions": 16, "provinces": 82, "majorCities": ["Manila", "..."] }
}

Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400

This is the only endpoint that returns its payload at the top level — there is no success wrapper. Every figure is computed live from active listings, except coverage, which is a fixed editorial summary — treat the location endpoints as authoritative for region, province and city counts.

GET/api/locations/regions

List all regions

Returns all 17 Philippine regions, NCR first and the remainder alphabetical. Takes no parameters.

Takes no parameters.

Example response

{
  "success": true,
  "regions": [
    {
      "code": "130000000",
      "name": "NCR",
      "regionName": "National Capital Region",
      "islandGroup": "luzon"
    },
    {
      "code": "050000000",
      "name": "Bicol Region",
      "regionName": "Region V",
      "islandGroup": "luzon"
    }
  ]
}

name is the short display form and regionName is the long form. Which one reads as the “full” name flips between regions — NCR is name “NCR” / regionName “National Capital Region”, while Bicol is name “Bicol Region” / regionName “Region V”. Match on code, not on either name.

GET/api/locations/provinces

List provinces

Returns all 81 provinces, sorted alphabetically. Pass regionCode to narrow the list to a single region.

Parameters

NameTypeRequiredDescription
regionCodestringNoPSGC region code, e.g. 050000000. Omit to return every province.

Example response

{
  "success": true,
  "provinces": [
    {
      "code": "012800000",
      "name": "Ilocos Norte",
      "regionCode": "010000000",
      "islandGroup": "luzon"
    }
  ]
}

Cache-Control: public, s-maxage=86400, stale-while-revalidate=604800

GET/api/locations/cities

List cities and municipalities

Returns all 1,634 cities and municipalities. Sorted capitals first, then cities, then municipalities, each group alphabetical. provinceCode takes precedence over regionCode when both are supplied.

Parameters

NameTypeRequiredDescription
provinceCodestringNoPSGC province code, e.g. 012800000. More specific than regionCode.
regionCodestringNoPSGC region code. Ignored when provinceCode is also present.

Example response

{
  "success": true,
  "cities": [
    {
      "code": "012812000",
      "name": "City of Laoag",
      "provinceCode": "012800000",
      "regionCode": "010000000",
      "isCity": true,
      "isCapital": true,
      "displayName": "Laoag"
    }
  ]
}

Cache-Control: public, s-maxage=86400, stale-while-revalidate=604800

displayName is a convenience field with the “City of ” prefix removed — use name when you need the official PSGC spelling.

GET/api/locations/barangays

List barangays

Returns barangays for one city or one region. Either cityCode or regionCode is required — a request with neither returns 400. Barangay data is loaded per region, so regionCode queries can return tens of thousands of rows; use limit.

Parameters

NameTypeRequiredDescription
cityCodestringNoPSGC city code, e.g. 012801000. Takes precedence over regionCode.
regionCodestringNoPSGC region code. Used only when cityCode is absent.
searchstringNoCase-insensitive substring match on the barangay name.
limitintegerNoMaximum rows to return. Defaults to 100.

Example response

{
  "success": true,
  "barangays": [
    {
      "code": "012801001",
      "name": "Adams (Pob.)",
      "regionCode": "010000000",
      "cityCode": "012801000"
    }
  ],
  "total": 1,
  "hasMore": false
}

Cache-Control: public, s-maxage=86400, stale-while-revalidate=604800

total is the count before limit is applied, and hasMore tells you whether rows were truncated.

GET/api/listings/mls/{mlsNumber}

Look up a listing by MLS number

Returns a single listing by its public MLS number, along with the featured photo and the seller's display name and verification badges. Only listings that are currently active and available are returned.

Parameters

NameTypeRequiredDescription
mlsNumberstring (path)Yes14-character MLS number, e.g. MLSPH91A1B2C3D. Case-insensitive — lowercase input is upper-cased before lookup.

Example response

{
  "success": true,
  "listing": {
    "id": "clx8f2k9p0001abcd1234efgh",
    "mlsNumber": "MLSPH91A1B2C3D",
    "title": "3-Bedroom House and Lot",
    "price": "8500000",
    "priceType": "TOTAL",
    "transactionType": "FOR_SALE",
    "propertyCategory": "RESIDENTIAL",
    "propertySubtype": "HOUSE_SINGLE_DETACHED",
    "cityMunicipality": "Quezon City",
    "barangayArea": "Batasan Hills",
    "region": "National Capital Region",
    "province": null,
    "propertyDetails": { "bedrooms": 3, "bathrooms": 2, "floorArea": 120 },
    "photos": [{ "id": "...", "photoUrl": "https://...", "isFeatured": true }],
    "user": {
      "id": "clx8f2k9p0000abcd1234efgh",
      "profile": { "firstName": "Juan", "lastName": "Dela Cruz", "photoUrl": null },
      "badges": ["VERIFIED", "VERIFIED_BROKER"],
      "isVerified": true
    }
  }
}

Returns 404 for listings that are sold, reserved, inactive or in draft — not just for numbers that do not exist.

Errors

Failures return the matching HTTP status with a JSON body carrying a single error string. Messages are meant for developers and may change — branch on the status code, not the text.

StatusMeaningBody
200Success.The documented payload.
400Malformed request — an invalid MLS number format, or a barangay query with neither cityCode nor regionCode.{ "error": "..." }
404No matching listing, or the listing exists but is not publicly available (draft, inactive, reserved or sold).{ "error": "..." }
500Server-side failure. Safe to retry with backoff.{ "error": "..." }

A listing that is sold, reserved, inactive or still a draft returns 404, the same as an MLS number that never existed. Treat 404 as “not publicly available” rather than “not found”.

Code examples

cURL

# Market statistics
curl -s https://www.mls.ph/api/property-stats

# Every region
curl -s https://www.mls.ph/api/locations/regions

# Cities in Ilocos Norte
curl -s "https://www.mls.ph/api/locations/cities?provinceCode=012800000"

# Barangays in one city, name filtered
curl -s "https://www.mls.ph/api/locations/barangays?cityCode=012801000&search=san&limit=25"

# One listing by MLS number
curl -s https://www.mls.ph/api/listings/mls/MLSPH91A1B2C3D

JavaScript (server-side)

// Node.js / server-side JavaScript — no key required.
const BASE = "https://www.mls.ph";

async function getCities(provinceCode) {
  const res = await fetch(`${BASE}/api/locations/cities?provinceCode=${provinceCode}`);
  if (!res.ok) {
    throw new Error(`MLS.PH API returned ${res.status}`);
  }
  const { cities } = await res.json();
  return cities;
}

async function getListing(mlsNumber) {
  const res = await fetch(`${BASE}/api/listings/mls/${mlsNumber}`);
  if (res.status === 404) return null; // sold, inactive, or no such listing
  if (!res.ok) throw new Error(`MLS.PH API returned ${res.status}`);
  const { listing } = await res.json();
  return listing;
}

const cities = await getCities("012800000");
console.log(cities.map((c) => c.displayName));

Python

# Python 3 — requires: pip install requests
import requests

BASE = "https://www.mls.ph"

def get_stats():
    r = requests.get(f"{BASE}/api/property-stats", timeout=10)
    r.raise_for_status()
    return r.json()  # top-level object, no "success" wrapper

def get_listing(mls_number):
    r = requests.get(f"{BASE}/api/listings/mls/{mls_number}", timeout=10)
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()["listing"]

stats = get_stats()
print(stats["overview"]["totalActiveListings"])

Rate limits & fair use

There are no API keys on these endpoints, so there is no per-client quota to publish. What we ask instead:

  • Keep sustained traffic under roughly one request per second, and back off on 5xx responses.
  • Cache on your side. Region, province, city and barangay data changes very rarely — fetch it once and store it.
  • Attribute MLS.PH and link to www.mls.ph when you publish figures drawn from the statistics endpoint.
  • Listing content belongs to the seller who posted it. Do not republish listings in bulk without permission.

Traffic heavy enough to affect the site may be blocked at the edge without notice. If you need volume beyond this, talk to us first — that is a partner integration, and we would rather set it up properly.

Frequently asked questions

Do I need an API key?

No. Every endpoint documented on this page is public and read-only, and requires no key, token or registration. The rest of the MLS.PH API is private and requires an authenticated session, and is not covered here.

Can I call these endpoints from browser JavaScript?

Not cross-origin. MLS.PH does not send CORS headers, so a fetch from another website's front-end will be blocked by the browser. Call these endpoints from your own server, a scheduled job, or a backend proxy instead.

Can I create listings, send inquiries or submit offers through the API?

No. The public API is read-only — every documented endpoint is a GET. Write endpoints exist but are first-party: they require a Firebase ID token issued to a signed-in user in the MLS.PH web app, and there is no key exchange that issues one to a third party. Please do not script the public site's forms as a workaround; those submissions email real sellers and are treated as abuse. If you need write access, ask about a partner integration.

Is there an endpoint to search or bulk-export listings?

Not publicly at the moment. The listing endpoint documented here resolves one property at a time by its MLS number. If you need search or syndication access, get in touch about a partner integration.

What are the rate limits?

No hard per-key limit is enforced on these public endpoints today, because there are no keys. Please stay under roughly one request per second, cache responses on your side, and respect the Cache-Control headers — the location data changes very rarely. Sustained abusive traffic may be blocked at the edge.

Can I use MLS.PH data in my own app or research?

The statistics and location endpoints are intended to be used and cited. Attribute MLS.PH and link back to https://www.mls.ph. Listing content belongs to the sellers who posted it, so do not republish listings wholesale without permission.

Are the location codes the official PSGC ones?

Yes. Region, province, city and barangay codes follow the Philippine Standard Geographic Code, so they line up with PSA datasets and other systems that use PSGC.

Need search, syndication, or bulk access?

The endpoints above are the full public surface today. If you are building something that needs more — listing search, feeds, or a two-way integration — reach out and let’s talk about a partner integration.

Contact MLS.PH

Building on MLS.PH as a seller instead? Post your property for free