Getting Started
What is MapState?
MapState is an infrastructure layer that turns your property data into a fast, interactive map experience — without building it yourself.
You connect a data source (JSON URL, CSV, REST API, or webhook), MapState normalizes it, geocodes missing coordinates, scores data quality, and renders a filterable, embeddable map that works on any website.
Core idea: Your CRM or listing system stays the source of truth. MapState syncs from it, enriches the data, and exposes a polished map. You never rebuild the map layer again.
Key concepts
Workspace
Your organization account. Contains your sources, properties, and map configuration.
Sources
Where your property data comes from — a JSON URL, file upload, API, or webhook endpoint.
Properties
Normalized listing records in your MapState catalog. Synced and enriched from your sources.
Map
The rendered, filterable map you embed on your website or share as a direct link.
Architecture overview
The data flow is simple and unidirectional:
Your system → MapState source → Normalize & enrich → Property catalog → Map embed
MapState never writes back to your system. It's a one-way read pipeline.
What MapState handles for you
Getting Started
Quick start
Go from zero to a live, embeddable property map in about 5 minutes.
Create your workspace
Sign up at mapstate.com/signup. Enter your agency name — this becomes your workspace slug (e.g. my-agency). No credit card required for the trial.
Import your data
Go to Sources → Add source. Choose one of:
- · JSON file — upload a
.jsonfile from your computer - · CSV file — upload a spreadsheet export, headers auto-mapped
- · Remote URL — a live endpoint MapState polls on a schedule
MapState detects your field names automatically. Most imports complete in under 10 seconds.
Review your properties
Navigate to Properties. You'll see a table of all imported records with a quality score. Properties missing coordinates are flagged — fix them by updating your source and re-syncing.
Toggle the Visibility switch on any property to publish it to the map.
Embed your map
Go to Settings → Deployment. Copy the iframe and paste it into your website:
<iframe src="https://mapstate.com/c/my-agency"
width="100%" height="600"
frameborder="0" allowfullscreen></iframe>
That's it. The map loads asynchronously and is fully responsive.
Next step: To keep your map in sync automatically, set up a Remote URL source with a scheduled sync — no more manual re-imports.
Data model
Properties
A property is a single real estate listing in your MapState catalog. Properties are created and updated by syncing from your data sources.
Property fields
| Field | Type | Required | Description |
|---|---|---|---|
title | string | required | Listing title or property name |
external_id | string | optional | Your internal ID. Used for deduplication on re-sync. |
listing_type | sale | rent | optional | Whether the property is for sale or rent |
property_type | string | optional | apartment, house, commercial, land… |
price_amount | number | optional | Numeric price value |
price_currency | string | optional | ISO 4217 code (EUR, USD…). Defaults to EUR. |
price_period | string | optional | For rentals: month, week, night… |
geo_lat | float | optional | Latitude. Required for map display. |
geo_lng | float | optional | Longitude. Required for map display. |
address_line_1 | string | optional | Street address |
address_city | string | optional | City name. Used for location search. |
address_postal_code | string | optional | Postal / ZIP code |
address_region | string | optional | State, province, or region |
bedrooms | integer | optional | Number of bedrooms |
bathrooms | integer | optional | Number of bathrooms |
area_value | number | optional | Total area (numeric) |
area_unit | string | optional | m², sqft, ha… Defaults to m². |
description_short | string | optional | Short description (one sentence) |
description_long | string | optional | Full listing description |
images | string[] | optional | Array of image URLs |
Status lifecycle
Every property has a status and an independent is_published flag:
draft → property is incomplete or not ready published → property is active and can appear on the map archived → property is hidden from the catalog (soft delete)
A property must have status published and is_published = true to appear on the map. This lets you prepare listings before making them live.
Synced vs. managed properties
Properties imported from a source are synced — their core fields are read-only inside MapState and update on the next sync. Properties you create manually are managed — you own all fields directly.
Source of truth: If a source has source of truth enabled, its values overwrite any local edits on every sync — even if you changed them in the dashboard.
Data model
Data sources
A source is a connection between your data system and MapState. Sources drive the sync pipeline that populates your property catalog.
Source types
| Type | Description | Best for |
|---|---|---|
url | A remote URL returning JSON or CSV | Live CRM feeds, property portals |
api | REST API with authentication headers | Proprietary systems with API keys |
webhook | MapState provides a POST endpoint you push to | Real-time updates on CRM save events |
file | One-time JSON or CSV upload | Migration, initial bulk import |
Sync modes
Manual — sync is triggered by clicking "Sync now" in the dashboard or calling the sync API endpoint.
Scheduled — MapState fetches from your source on a schedule (e.g. daily, 6h, every monday). Requires a remote URL or API source.
How sync works
On every sync, MapState:
- Fetches your source data
- Maps fields to the canonical property schema
- Matches records by
external_id— creates new, updates changed, archives missing - Runs geocoding on records without coordinates
- Recalculates quality scores
Field mapping
MapState auto-detects common field names (see JSON Format for the full alias table). You can verify detected mappings in Sources → Configure → Field mappings.
Data model
Quality scoring
Every property gets a quality score from 0 to 100. It measures completeness and usability for map display.
Score breakdown
| Field | Points | Why it matters |
|---|---|---|
| Coordinates (lat/lng) | +30 | Without these, the property cannot appear on the map |
| Price | +20 | Core filter criterion for buyers and renters |
| Property type | +15 | Essential for filtering and display |
| City | +15 | Used for location search and geocoding fallback |
| Rooms (bedrooms or bathrooms) | +10 | High-signal attribute for residential properties |
| Description | +10 | Affects click-through rate on property cards |
Score thresholds
≥ 70
Good — ready for map
40–69
Needs improvement
< 40
Incomplete — review required
The score is recalculated after every sync. The average quality score across all properties is shown on your dashboard.
Importing data
JSON format
MapState accepts JSON files and URLs. Arrays, nested objects, and non-standard field names are all handled automatically.
Accepted structures
Top-level array (recommended)
[
{
"id": "prop-001",
"title": "3BR Apartment, Haussmann Style",
"price": 850000,
"city": "Paris",
"lat": 48.8584,
"lng": 2.2945,
"type": "apartment",
"bedrooms": 3,
"surface": 95
},
{ ... }
]
Object with array key
MapState automatically detects arrays under these common keys:
{
"data": [...], // ✓ detected
"properties": [...], // ✓ detected
"listings": [...], // ✓ detected
"results": [...], // ✓ detected
"items": [...] // ✓ detected
}
Field name aliases
MapState detects these names automatically — you don't need to rename your fields.
| Canonical field | Accepted names |
|---|---|
title | name, nom, titre, listing_title, property_name |
external_id | id, ref, reference, listing_id, property_id |
listing_type | type_annonce, transaction, for_sale, sale_type |
property_type | type_bien, type, category, kind |
price_amount | price, prix, montant, amount, cost |
price_currency | currency, devise, monnaie |
geo_lat | lat, latitude, gps_lat |
geo_lng | lng, lon, longitude, gps_lng, long |
address_city | city, ville, commune, municipality |
address_postal_code | postal_code, zip, code_postal, postcode |
address_region | region, state, province, department |
bedrooms | rooms, pieces, chambres, nb_rooms |
area_value | surface, area, m2, sqft, superficie, size |
description_long | description, desc, body, details |
images | photos, images, pictures, gallery |
If your field name isn't detected automatically, configure the mapping manually in Sources → Configure → Field mappings.
Importing data
CSV format
MapState accepts .csv files with a header row. The same alias table as JSON applies — no renaming needed.
Requirements
- → First row must be column headers
- → UTF-8 encoding
- → Comma or semicolon delimiter (auto-detected)
- → No file size limit for uploads
Minimal example
id,title,price,city,lat,lng,type,rooms,surface 001,"3BR Apartment, Paris",850000,Paris,48.8584,2.2945,apartment,3,95 002,Villa with Pool,1200000,Lyon,45.7640,4.8357,house,5,210 003,Studio Rental,1100,Marseille,43.2965,5.3698,apartment,1,28
Multi-value columns
For fields like images, use a pipe-separated list:
images https://cdn.example.com/photo1.jpg|https://cdn.example.com/photo2.jpg
Tip: Export directly from your CRM or spreadsheet — MapState handles messy column names. You rarely need to clean the CSV first.
Importing data
Remote URL
Connect a live URL that returns JSON or CSV. MapState fetches from it on a schedule, keeping your map automatically up to date.
Supported URL types
- → Any public or authenticated URL returning JSON or CSV
- → Paginated APIs (MapState follows
nextlinks automatically) - → URLs with query parameters (e.g.
?format=json&status=active)
Authentication
Add static headers in Sources → Configure → Headers:
Authorization: Bearer your-api-token X-API-Key: your-key
Schedule syntax
Set the sync frequency in Sources → Configure → Sync → Frequency:
| Value | Meaning |
|---|---|
daily | Once per day at midnight UTC |
12h | Every 12 hours |
6h | Every 6 hours |
1h | Every hour |
every monday | Once per week on Monday |
Importing data
Webhooks
MapState provides a unique webhook endpoint per workspace. POST your property data to it whenever a listing changes in your system — the map updates in real time.
Endpoint
POST https://api.mapstate.com/hooks/{workspace-slug}
Your webhook URL is shown in Sources → Webhook URL. Copy it into your CRM automation or event hook.
Payload format
Send a JSON body with the same field aliases as JSON import:
POST https://api.mapstate.com/hooks/my-agency
Content-Type: application/json
{
"id": "prop-001",
"title": "3BR Apartment",
"price": 850000,
"city": "Paris",
"lat": 48.8584,
"lng": 2.2945
}
You can also send an array to update multiple properties in one request.
Delete a property
Add "_action": "delete" to remove a property from the catalog:
{ "id": "prop-001", "_action": "delete" }
Response
{
"success": true,
"created": 1,
"updated": 0,
"deleted": 0,
"errors": 0
}
Use case: Hook your CRM's "on listing save" event to POST to MapState. Your map reflects every change within seconds, with no scheduled sync delay.
Publishing
Map embed
Embed your MapState map anywhere with a single iframe. Responsive, lazy-loaded, zero backend work on your side.
iFrame embed (recommended)
<iframe src="https://mapstate.com/c/my-agency"
width="100%" height="600"
frameborder="0" allowfullscreen></iframe>
URL parameters
Append parameters to the URL to control the embed behaviour:
| Parameter | Default | Description |
|---|---|---|
filter | true | Show the filter panel (true / false) |
cluster | false | Cluster nearby markers (true / false) |
<!-- Embed without filters, with clustering -->
<iframe src="https://mapstate.com/c/my-agency?filter=false&cluster=true"
width="100%" height="600" frameborder="0"></iframe>
Direct link
Share your map as a standalone page:
https://mapstate.com/c/{workspace-slug}
Map behaviour
When the map is visible in split view, the property list on the left automatically updates to show only the listings visible in the current viewport. Panning or zooming re-filters the list in real time — no additional requests.
Publishing
Custom domain
Serve your map from your own domain (e.g. map.youragency.com) for a fully white-labeled experience.
Setup
-
1
Go to Settings → Deployment → Custom domain and enter your subdomain (e.g.
map.youragency.com). -
2
Add a
CNAMErecord in your DNS:map.youragency.com CNAME cname.mapstate.com
-
3
Wait for DNS propagation (usually < 5 minutes). MapState provisions an SSL certificate automatically via Let's Encrypt.
Root domains (e.g. youragency.com) are not supported — use a subdomain like map. or listings.
Customization
Map styling
Customize the look of your map to match your brand. All settings are in Settings → Map styling.
Primary color
Used for markers, active filter highlights, and UI accents. Pick from presets or enter a custom hex value. Contrast ratios are adjusted automatically to keep labels readable.
Font family
Choose from: Inter, Lato, Montserrat, Poppins. The font applies to all text within the map widget — it doesn't affect your parent page.
Border radius scale
A global scale from 0 (sharp corners) to 24 (fully rounded). Affects cards, buttons, badges, and markers uniformly.
Map base style
Powered by Mapbox. Uses the Mapbox Standard style by default. Connect your own Mapbox style URL via Settings → Map styling → Mapbox token to use a fully custom cartographic style.
Marker behaviour
Markers render as small dots by default to keep the map clean. On hover — either on the marker itself or on the corresponding property card — the marker grows and reveals a home icon. The active marker is highlighted when a popup is open.
Customization
Branding
Remove MapState branding and replace it with your own on Pro and Business plans.
Logo
Upload your logo in Settings → General → Logo. PNG or SVG, recommended size: 180 × 40px. The logo appears in the map header and property detail pages.
White-label embed
On Pro+ plans, "Powered by MapState" is removed from the widget and all public URLs. Your branding is the only brand visible to end users.
Workspace name & slug
Your workspace name appears in page titles and email notifications. Your slug is part of the public map URL (mapstate.com/c/your-slug) — change it carefully, as existing links will break.