https://www.mappingg.com/api/v1 · JSON over HTTPS · Authorization: Bearer <key> · read scopes, plus optional enquiries:submit.01What the API provides
The Mappingg Developer API gives approved partners read access to the same public data shown on mappingg.com: projects (name, developer, locality, configuration, published price, possession, RERA number where listed, coordinates and image), developers, localities, map pins, infrastructure markers and published articles.
It does not give access to buyer or customer details, enquiries and leads, accounts, drafts or unpublished content, uploaded files, the admin panel, or the database. If a project field is hidden on the project’s public card, it is null in the API too. Hidden, pending-review and rejected projects are never returned.
The one write — sending enquiries from your website into our CRM — is a separate scope,enquiries:submit, granted only on request. It can add an enquiry but never read, list or change one.
02Getting access
- Email mappingg.associatte@gmail.com with your organisation, website, contact person and what you want to build.
- We review the request and create an application for you with only the scopes you need, and agreed limits.
- You receive your API key once, privately. Store it in your server’s secret store or environment variables.
Keys can be rotated (with an overlap period so you can switch without downtime) or revoked at any time.
03Authentication
Send your key in the Authorization header on every request:
Authorization: Bearer mgk_xxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxKeyed endpoints send no CORS headers, so browsers can’t call them directly. For a public web page, fetch from your own backend and cache the result there. The three endpoints that need no key (/health,/openapi.json, /sample/projects) allow any origin.
04Base URL and endpoints
Base URL: https://www.mappingg.com/api/v1. All endpoints return JSON; all are GET except POST /enquiries.
| Endpoint | Scope | Returns |
|---|---|---|
GET /health | none | Service status: ok or degraded. No key needed. |
GET /openapi.json | none | This API as an OpenAPI 3.1 document. |
GET /sample/projects | none | Three sample projects, so you can see the shape before you have a key. |
GET /metadata | any key | Your application, its scopes and its limits. |
GET /projects | projects:read | Public projects, with filters and pagination. |
GET /projects/{id} | projects:read | One project by number (e.g. 135) or slug. |
GET /categories | projects:read | Statuses, property types and cities, with project counts. |
GET /developers | developers:read | Developers (builders) with public projects. |
GET /developers/{id} | developers:read | One developer and their projects. |
GET /localities | localities:read | Localities with public projects. |
GET /localities/{id} | localities:read | One locality and its projects. |
GET /map/pins | map:read | Project pins inside a bounding box, clustered below zoom 13. |
GET /map/infrastructure | infrastructure:read | Metro stations, hospitals, schools and other public map markers. |
GET /blogs | blogs:read | Published articles (no drafts), newest first. |
GET /blogs/{slug} | blogs:read | One published article, with its HTML. |
POST /enquiries | enquiries:submit | Send a visitor’s enquiry from your website into the Mappingg CRM (server-to-server). |
Full parameters and response schemas: API reference · openapi.json.
05Scopes
Each application is given only the scopes it needs. Everything is read-only except enquiries:submit, which is never granted by default.
| Scope | Allows |
|---|---|
projects:read | Projects: list and read public project details |
developers:read | Developers: list developers (builders) and their public projects |
localities:read | Localities: list localities and their public projects |
map:read | Map: project pins inside a bounding box, with clustering |
infrastructure:read | Infrastructure: public map markers (metro, hospitals, schools …) |
blogs:read | Blogs: published articles only |
enquiries:submit | Enquiries: send property enquiries from your own website into the Mappingg CRM (server-to-server only) |
06Filtering and pagination
List endpoints take page (from 1) and limit. The default page size is 20, and the maximum is your application’s page size (at most 100). Paging stops at record 5,000: narrow your filters to go further. Unknown or repeated parameters return 400, so typos don’t silently return everything.
/projects filters: q (name search), locality, developer, type (ids from the other endpoints), city (pune, mumbai, …), status (upcoming,under_construction, ready_to_move, sold), updated_since (ISO 8601) andsort (id, name, -updated_at).
/map/pins needs north, south, east and west. Add zoom to get clusters below zoom 13, and limit (up to 500 pins).
{
"success": true,
"data": [
{
"id": 135,
"slug": "example-project-kharadi-135",
"url": "https://www.mappingg.com/projects/example-project-kharadi-135",
"name": "Example Project",
"developer": { "id": "example-developers", "name": "Example Developers" },
"property_type": "Residential",
"status": "under_construction",
"status_label": "Under construction",
"configuration": "2 / 3 / 4 BHK",
"price": "₹ 1.12 Cr Onwards",
"possession": "Dec 2030",
"locality": { "id": "kharadi", "name": "Kharadi" },
"city": { "id": "pune", "name": "Pune" },
"coordinates": { "lat": 18.55, "lng": 73.94 },
"image_url": "https://www.mappingg.com/api/media/pins/…",
"updated_at": "2026-10-01T10:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 1, "hasMore": false },
"meta": { "requestId": "6f1c…", "apiVersion": "v1" }
}Illustrative values. Prices, configurations and possession dates are the text published on the project page; they are not normalised. Try /sample/projects for live examples.
07Errors
Errors use one shape. Quote the requestId (also in the X-Request-Id header) when you contact support.
{
"success": false,
"error": { "code": "insufficient_scope", "message": "This key is not allowed to use this endpoint (needs scope \"blogs:read\").", "requestId": "6f1c…" }
}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | Unknown, repeated or out-of-range query parameter, or a malformed id. |
| 401 | missing_api_key | No Authorization: Bearer header. |
| 401 | invalid_api_key / revoked_api_key / expired_api_key | The key is wrong, was revoked, or has expired. |
| 403 | insufficient_scope | The key is valid but not allowed to use this endpoint. |
| 403 | application_disabled / application_revoked / application_expired | Your application has been paused, revoked or has expired. |
| 404 | not_found | No such project, developer, locality, article or endpoint. |
| 429 | rate_limited / quota_exceeded / client_blocked | Too many requests. Wait for the number of seconds in Retry-After. |
| 500 / 504 | internal_error / timeout | Our side. Retry later and quote the requestId if it persists. |
| 503 | api_disabled | The API is temporarily switched off. |
08Rate limits and quotas
Each application has a per-minute limit (default 60), a daily quota (default 5,000 requests, resetting at 00:00 UTC) and at most 5 requests in flight. Every keyed response carries:
X-RateLimit-Limit: 60 X-Quota-Limit: 5000
X-RateLimit-Remaining: 57 X-Quota-Remaining: 4812
X-RateLimit-Reset: 1791712860 X-Quota-Reset: 1791763200 (Unix seconds)Over a limit you get 429 with Retry-After (seconds). Back off and retry after it. Clients that keep failing authentication are blocked for 10 minutes. Unauthenticated endpoints allow 30 requests a minute per client. Need more? Ask us — limits are set per application.
09Caching and freshness
Data is read from the live site with a short server cache, so changes appear within about a minute. Keyed responses are Cache-Control: private, max-age=30 with Vary: Authorization: you may cache them in your own backend, but they must not be stored in a shared/public cache. For syncing, poll /projects?updated_since=…&sort=-updated_atinstead of downloading everything. Projects that are unpublished disappear from responses (404 for a single project), so re-check stored projects periodically and drop the ones that are gone.
10Code examples
Read the key from an environment variable (here MAPPINGG_API_KEY). Never hard-code it.
curl -s "https://www.mappingg.com/api/v1/projects?locality=kharadi&status=under_construction&limit=10" \
-H "Authorization: Bearer $MAPPINGG_API_KEY"Node.js (18+)const res = await fetch('https://www.mappingg.com/api/v1/projects?city=pune&limit=20', {
headers: { Authorization: `Bearer ${process.env.MAPPINGG_API_KEY}` },
});
if (res.status === 429) {
const wait = Number(res.headers.get('retry-after') || 60);
// back off for `wait` seconds, then retry
}
const body = await res.json();
if (!body.success) throw new Error(`${body.error.code}: ${body.error.message} (${body.error.requestId})`);
for (const p of body.data) console.log(p.id, p.name, p.price, p.url);Python (requests)import os, requests
r = requests.get(
"https://www.mappingg.com/api/v1/projects",
params={"locality": "kharadi", "page": 1, "limit": 20},
headers={"Authorization": f"Bearer {os.environ['MAPPINGG_API_KEY']}"},
timeout=15,
)
body = r.json()
if not body["success"]:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
for p in body["data"]:
print(p["id"], p["name"], p["price"], p["url"])PHP (cURL)<?php
$ch = curl_init('https://www.mappingg.com/api/v1/blogs?limit=5');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('MAPPINGG_API_KEY')],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$body['success']) { throw new Exception($body['error']['code']); }
foreach ($body['data'] as $post) { echo $post['title'], ' — ', $post['url'], PHP_EOL; }Map pins in a viewportcurl -s "https://www.mappingg.com/api/v1/map/pins?north=18.60&south=18.50&east=73.98&west=73.88&zoom=14" \
-H "Authorization: Bearer $MAPPINGG_API_KEY"No key yet? Try /api/v1/sample/projects and /api/v1/health in your browser.
11Sending enquiries (enquiries:submit)
If Mappingg has granted your application enquiries:submit, your website can send a visitor’s property enquiry straight into the Mappingg CRM. It arrives as a new lead attributed to your website, and our team follows it up. Your application must have your website’s domain registered with us: sourcePage must be an https page on that domain or a subdomain.
Request
POST https://www.mappingg.com/api/v1/enquiries
Authorization: Bearer <key>
Content-Type: application/json
{
"submissionId": "9f0c2b6e-1d7a-4b55-9a3e-2f6a1c8d4e01",
"name": "Asha Patil",
"phone": "+91 9876543210",
"email": "asha@example.com",
"projectId": 135,
"bhk": "2 BHK",
"budget": "₹1–1.5 Cr",
"message": "Please share the price sheet.",
"consent": {
"enquiry": true,
"whatsapp": true,
"text": "I agree to be contacted by Example Realty and Mappingg about this enquiry. I'd also like WhatsApp updates on matching projects.",
"textVersion": "2026-10",
"capturedAt": "2026-10-14T10:15:00+05:30"
},
"sourcePage": "https://www.example-realty.com/kharadi/new-projects",
"campaign": { "source": "google", "medium": "cpc", "name": "kharadi-oct" }
}| Field | Rules |
|---|---|
submissionId | Required. Your unique id for this enquiry (8–64 letters, digits, - or _; a UUID is ideal). Reuse it when retrying. |
name | Required, 2–120 characters. |
phone | Required. Country code + 10 digits, e.g. "+91 9876543210" (a plain 10-digit number is read as +91). |
email | Optional. |
projectId | Optional. A published project’s id from /projects. projectName, if also sent, must match it. |
locality, budget, bhk, message | Optional. bhk is one of the values in the API reference; message up to 1,000 characters. |
consent | Required — see “Consent” below. |
sourcePage | Required. The https page on your registered website where the form was. Query strings are not stored. |
campaign | Optional attribution: source, medium, name, term, content. |
Any other field (status, assignment, ids, notes…) is rejected with 400; you can’t set how Mappingg handles the lead.
Consent — your responsibility
- Show the visitor a clear consent statement next to an unticked checkbox, and only send the enquiry if they ticked it.
- Send the exact wording in
consent.text, your label for that wording inconsent.textVersion(change it whenever the wording changes), and when they submitted inconsent.capturedAt(within the last 24 hours). consent.enquirymust betrue.callandwhatsappare separate, optional opt-ins for other offers; sendtrueonly if the visitor ticked those boxes too.- Mappingg can’t see your form, so it records this as consent stated by you, with your wording, version and time. Receiving
consent.enquiry: truedoes not prove the visitor consented, so sending enquiries without real consent breaks our terms and gets the application revoked. - Tell visitors in your privacy notice that enquiries are shared with Mappingg (Associatte PropTech Pvt Ltd) to be followed up, and link to our privacy policy.
Responses, retries and duplicates
201received:{"success":true,"data":{"submissionId":"…","reference":"enq_…","status":"received"}}. Keep the reference for support.- Network error or
5xx? Retry with the same submissionId and body. You get the original receipt again (200, headerIdempotent-Replayed: true), never a second lead. A used submissionId with a different body returns409. - If the same phone number asks about the same project again within 24 hours, it is added to the existing lead instead of creating a new one. The response is the same either way: the API never says whether a person is already known to Mappingg.
400invalid or unknown fields ·413body over 16 KB ·415not JSON ·422invalid_phone, invalid_consent, source_not_allowed, website_not_registered, project_not_found, project_mismatch, content_rejected ·429limits (see Retry-After).- The API can’t read, list, update or delete enquiries.
Limits
Enquiries have their own limits per application, 10 a minute and 200 a day (UTC) by default, on top of the general request limits. Rejected submissions count too, and after 50 rejected submissions in an hour, enquiries pause for the rest of that hour (enquiries_paused). Rate-limit your own form per visitor as well (a CAPTCHA helps), so bots can’t use your form to fill your quota.
// server.js (Express). The browser posts the form to /enquire on YOUR domain;
// only this server knows MAPPINGG_API_KEY.
import express from 'express';
import { randomUUID } from 'node:crypto';
const app = express();
app.use(express.json({ limit: '16kb' }));
const CONSENT = 'I agree to be contacted by Example Realty and Mappingg about this enquiry.';
const WHATSAPP = " I'd also like WhatsApp updates on matching projects.";
app.post('/enquire', async (req, res) => {
const { name, phone, email, projectId, message, agree, whatsappOptIn } = req.body;
if (agree !== true) return res.status(400).json({ error: 'Please tick the consent box.' });
const body = {
submissionId: randomUUID(), // keep the same id for every retry of this enquiry
name, phone, email, message,
projectId: projectId ? Number(projectId) : undefined,
consent: {
enquiry: true,
whatsapp: whatsappOptIn === true,
text: CONSENT + (whatsappOptIn === true ? WHATSAPP : ''),
textVersion: '2026-10',
capturedAt: new Date().toISOString(),
},
sourcePage: 'https://www.example-realty.com/contact',
};
for (let attempt = 1; attempt <= 3; attempt++) {
const r = await fetch('https://www.mappingg.com/api/v1/enquiries', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MAPPINGG_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}).catch(() => null);
if (r && (r.status === 201 || r.status === 200)) return res.json({ ok: true });
if (r && r.status < 500 && r.status !== 429) {
const e = await r.json();
return res.status(400).json({ error: e.error.message }); // fix the input; don't retry
}
const wait = Number(r?.headers.get('retry-after') || attempt);
await new Promise((s) => setTimeout(s, wait * 1000));
}
res.status(503).json({ error: 'Please try again in a minute.' });
});Your website’s form<!-- Your website's form. No key in the page: it posts to YOUR backend. -->
<form id="enquire">
<input name="name" required> <input name="phone" required> <input name="email" type="email">
<label><input type="checkbox" name="agree"> I agree to be contacted by Example Realty and Mappingg about this enquiry.</label>
<label><input type="checkbox" name="whatsappOptIn"> I'd also like WhatsApp updates on matching projects.</label>
<button>Send</button>
</form>
<script>
document.getElementById('enquire').addEventListener('submit', async (e) => {
e.preventDefault();
const f = new FormData(e.target);
await fetch('/enquire', { // same origin: your server, not Mappingg
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: f.get('name'), phone: f.get('phone'), email: f.get('email') || undefined,
agree: f.get('agree') === 'on', whatsappOptIn: f.get('whatsappOptIn') === 'on',
}),
});
});
</script>Data processing
For enquiries you send, you collect the data from the visitor and are responsible for a lawful basis and a clear notice; Mappingg receives it to follow up the enquiry under its privacy policy. Don’t send enquiries for people who didn’t ask to be contacted, data about anyone other than the enquirer, or sensitive personal data. If a visitor asks to withdraw an enquiry or their consent, email mappingg.associatte@gmail.com with the reference.
12Attribution and permitted use
Wherever you show Mappingg data, show “Source: Mappingg” with a link to the project or page url from the response. Use the data to inform buyers and power your own product; don’t resell it as a dataset, try to rebuild the whole database by scraping, or present it as your own verified data.
Project details come from developers and are shown for reference only (see our disclaimer). RERA numbers are as listed — verify them on the issuing state's RERA website before relying on them. Use of the API is also covered by our terms of use and privacy policy.
13Versioning and changes
The version is in the path (/api/v1). Within v1 we only make additive changes: new endpoints, new optional parameters and new response fields — ignore fields you don’t know. Breaking changes go into a new version, and v1 keeps working for at least 6 months after the new version is announced to every registered contact email.
14Security guidance
- Keep keys server-side, in a secret manager or environment variables; never commit them.
- Use one application (and key) per integration, so one can be revoked without affecting the others.
- Rotate keys if a team member with access leaves; rotation keeps the old key working for an overlap period you choose.
- Article
content_htmlis our published HTML: sanitise it before inserting it into your own pages. - Report a suspected leak or vulnerability to mappingg.associatte@gmail.com.
15Support
Email mappingg.associatte@gmail.com with your application id and therequestId of the failing request. Service status: /api/v1/health.