A complete, open real estate site running on Express API: see it live, deploy it in minutes and modify it however you want.
The wiki documents all 21 actions one by one. This page answers the other question, the one that comes first: how do I build a site with this?
1. See it live
express.mapaprop.com is the site running on a real Mapaprop account, against the production Express API. What you see there is what gets deployed: it is not a mock-up.
The code is at github.com/Mapaprop/mapaprop-express-starter, under the MIT license.
2. What it is
A complete website —home, featured properties, search, property page, developments, blog, contact and seller capture— written in HTML and JavaScript, with no framework and no build step. You open it, you edit it and you see the change.
It is not a loose code sample: it is the implementation we keep up to date with every change in Express API, and the same one we use to show the API working.
| What it brings on the client side | HTML, CSS and JavaScript with no dependencies: nothing to install, nothing to compile |
| What it brings on the server side | a function that holds your token and talks to the API for you |
| The languages | Spanish, English and Portuguese, with a selector |
| The tests | 131, with no external dependencies |
| The license | MIT: use it, change it and publish it however you want |
The design —colors, typography, logo, cover, calls to action, social networks and footer— comes from your account if you activate the Mapaprop website and bind it to your token. Without that, the site uses the fallback values in public/js/config.js.
3. From zero to deployed
- Have your plan. Express API is included from Mapaprop Pro+ upwards.
- Activate your token in Settings > API Token. Step-by-step guide: Express API Token.
- Deploy the site. The repository has a Vercel button that clones the project and asks you for the token during the process; it is stored as the
MAPAPROP_API_TOKENenvironment variable. - Edit
public/js/config.jswith your fallback data and publish again.
You do not need to activate the CORS scope: the site talks to the API from the server, not from the browser. The CORS scope is needed when the browser JavaScript calls the API directly, which is exactly what this site avoids.
It also works on your own server: it is a Node server with no dependencies, and it runs behind nginx or Apache like any other application. What is not enough is static file hosting on its own, because the function that holds the token has to be executed.
4. Trying it with no account and no token
git clone https://github.com/Mapaprop/mapaprop-express-starter
cd mapaprop-express-starter
npm run dev:mock # http://localhost:3000
That starts the site against the repository fixtures, which are real, anonymized API responses. It is the way to look at it, to work on the design and to send a change without being a Mapaprop customer. In mock mode the forms do not send anything anywhere.
With your token, the real mode:
cp .env.example .env.local # and paste your token inside
npm run dev # http://localhost:3000
npm test # the full suite
5. The map of the code
For the developer who is going to modify it, what handles what:
| File | What it handles |
|---|---|
api/mp/[...path].js | the server function: it adds the token and only lets through the actions the site uses |
dev-local.mjs | the Node server that dispatches to that same function, for your machine or your hosting |
public/js/config.js | your fallback data. It is the first thing you will edit |
public/js/api.js | the client that talks to the server function |
public/js/brand.js | applies your account's design: colors, typography, logo and favicon |
public/js/i18n.js | the texts in the three languages and the selector |
public/js/ui.js | the shared rendering: prices, photos, features and pagination |
public/css/style.css | all the styling, in one file |
public/<section>/index.html | one folder per section, served at /<section> |
fixtures/ | the anonymized responses and the mock mode server |
tests/ | the suite, which runs without installing anything |
6. Why the token lives on the server side
This is the most important architectural decision in the project, and the one worth understanding before changing it.
Your account has a single API Token, that token carries every permission your plan grants you and it does not expire. If you have the Property API add-on, that same token can also create properties and publish them on the portals. Put in the site's JavaScript, any visitor would read it from the browser developer tools and walk away with a permanent credential for your account.
That is why it lives in a server environment variable, and the browser talks to a function that is the only one that knows it. That function also:
- only lets through the actions the site uses, through an explicit list. Without that list, a hand-crafted route could reach another part of the API with your token.
- filters the body of both forms against the fields each action documents.
- trims
settings-v2down to what the site uses. Besides your name and your contact details, that action returns your tax data and your account's Mapbox and reCAPTCHA keys. Everything the function returns ends up public, so whatever the site does not need does not go out. - fills in the IP, the browser and the referer of every inquiry: these are data the inquiry action needs and that the browser cannot declare about itself.
- caches the catalogs so no calls are wasted.
If you add a new action, add it to that list. And if you need a settings-v2 field that is not returned today, add it deliberately and ask yourself whether that field can end up published, because that is exactly what will happen.
The site also declares a Content-Security-Policy and three more headers, and the Node server reads them from the same file Vercel does: that way the defense does not depend on where you deployed it.
7. Inquiries come back to your CRM
The form on every property page posts to messages, so the inquiry enters your Mapaprop Inbox as a lead: you assign it to an agent, you follow up on it, it stays in your reports and it triggers your automations.
If you replace it with a form that only emails you, the circle breaks: you keep loading properties into Mapaprop and you serve your customers outside, where the inquiry does not exist for your CRM.
The seller capture form uses seller-submissions: it creates the contact in your CRM and emails you with the attachments. That action does need a site bound to your token.
8. What it does not cover
| The generic inquiry | messages requires a published property, so an inquiry that is not about any property does not enter the CRM through the API. The site's contact page shows your branches with phone, email and WhatsApp, and its form opens the visitor's mail client |
| Hosting and maintenance | are on you. That is the flip side of owning the code: nobody updates it on your server for you |
| 20 properties per request | that is the API cap. The listing paginates on its own |
| Drafts | the API only returns published properties |
| Seller capture attachments | 2 MB per file, and the real ceiling is set by the total of the submission: they travel encoded and that inflates the body by about a third |
If you regenerate your token, the previous one stops working immediately: update the environment variable and deploy again. If you cancel your plan, the token is deactivated and the site stops bringing properties.
9. How to contribute
The repository is public and takes pull requests.
- If something is missing —a filter, a block, a language— open an issue at github.com/Mapaprop/mapaprop-express-starter/issues and we will look into it.
- If you already solved it, send your PR. Mock mode exists precisely so that you can work and test without an account.
- If you find a security problem, how to report it is written in the repository's
SECURITY.md.