MapWise API Reference

MapWise API

Programmatic access to Florida property data across all 67 counties — search parcels by owner, PIN, address, acreage, sales, value, and more. Every input is public record, sourced directly from the county property appraisers and the Florida Department of Revenue, with zero licensed or third-party-aggregated data. This API is for targeted queries and integrations, not bulk data download.

Quick Start

Base URL

https://maps.mapwise.com/api_v2

Get started in 3 steps

  1. Try the test endpoint — no API key needed. Make a request to /parcels/test to see the response format.
  2. Get your API key — keys come with a paid MapWise Pro subscription that has a payment method on file; on a free trial, you can create one once the trial converts to paid. The account owner or billing admin logs in and opens My Account → Parcels API, then creates a key.
  3. Query the full endpoint — use your API key with /parcels to access all 67 Florida counties.

Authentication

There are two kinds of key, and they are not interchangeable.

KeyWhat it can reachWhere it goes
Secret key Server-only a1b2c3… Reads data: parcels, and anything else listed in the rates below. Not map tiles — the tile routes refuse a secret key. Your server. It works from anywhere, so anyone who gets a copy can use it until you revoke it. Never put it in a web page, a mobile app, or a repository.
Publishable key Safe to embed mw_pk_… Draws map tiles, and nothing else. It cannot read data. Your web pages, where visitors can see it. It works only on the websites you register with it, so list just the sites you use.

Both keys bill to your account. MapWise Pro includes API access, but usage is metered, not included: everything either key reads or draws is charged at the rates below. The difference between the two keys is reach, not cost — a secret key can read every product you pay for, from anywhere, while a publishable key can only draw tiles, and only from the sites you listed. Keep that list tight and watch your usage on Usage & spend.

Send a secret key on every request to https://maps.mapwise.com/api_v2, either way:

  • X-API-Key: YOUR_API_KEY
  • Authorization: Bearer YOUR_API_KEY

Sending the wrong class is refused rather than downgraded: the response is 403 with KEY_CLASS_MISMATCH. A publishable key cannot read data, and a secret key is not accepted from a browser.

Create either kind in My Account → Parcels API. API access is included with MapWise Pro; issuing keys needs the Super Admin role.

Data & Pricing

API access is included with MapWise Pro — there is no separate API plan. You pay for what you use, billed monthly:

ProductRateWhat it covers
Parcels$0.06 / recordParcel lookups, base data

These are standard rates. Your account’s own rates, including any negotiated price, are shown under Usage & spend in My Account. For volume pricing, contact us.

  • County coverage: all 67 Florida counties with complete sales history and property records.
  • Data updates: weekly to biweekly from official county property appraiser sources (statewide median under a week).
  • Test endpoint: free — requests to /parcels/test do not count toward billing.

Monthly Spend Caps

Every account includes a monthly spend cap — $100/month by default. Adjust it from My Account → Parcels API → Monthly Spend Cap (requires the Super Admin or Billing role), up to the account maximum of $1,000/month; for higher limits, contact us about enterprise pricing. Once the cap is reached, further billable requests are declined with 429 until usage resets on the 1st of the month (00:00 UTC). Email warnings send at 50%, 80%, and 100% of the cap.

Security note: a leaked key cannot raise its own cap — spend-cap changes require an authenticated dashboard session, not the API key.

Every billable response includes your current spend state in headers:

X-Billing-Period-Spend-Cents: 1200
X-Billing-Period-Cap-Cents: 3000
X-Billing-Period-Reset: 2026-08-01T00:00:00Z

A cap-exceeded response is a 429, like a rate limit, but not the envelope every other failure uses — it is its own shape, with no errormsg to read. Check for a top-level error to tell the two apart:

{
  "error": "spend_cap_exceeded",
  "endpoint": "parcels",
  "current_spend_cents": 10000,
  "cap_cents": 10000,
  "reset_at": "2026-10-01T00:00:00Z",
  "estimated_call_cost_cents": 6,
  "request_id": "01a0ba0a-e8f4-7fff-853c-bbce832d6c46"
}

It carries Retry-After set to the period reset, which may be days away, so treat it differently from a rate limit: backing off will not help before reset_at. Either raise the cap in My Account or wait for the reset. current_spend_cents against cap_cents tells you which.

Rate Limiting & Security

Limits are per API key, and map tiles are counted separately from data so that panning a map cannot exhaust the budget your data calls need:

  • Data endpoints: 10 requests per second, sustained. The allowance refills continuously, and you can spend up to 600 requests at once before it does — enough for a burst of parallel lookups, after which you are held to the sustained rate.
  • Map tiles and WMS: 60 requests per second. A single pan or zoom fetches dozens of tiles, so these get their own, faster budget.
  • The keyless /parcels/test endpoint: 5 requests per second for anonymous callers. Use a key and the full /parcels endpoint for the limits above.

Every response tells you where you stand, so a client can pace itself rather than guess:

X-RateLimit-Bucket-Capacity: 600
X-RateLimit-Bucket-Remaining: 587
X-RateLimit-Endpoint-Limit: 60

Over the limit is a 429 with a Retry-After header saying when to come back. A 429 can also mean your monthly spend cap is reached rather than your rate limit — the body’s error field says which, and spend_cap_exceeded means waiting will not help until the period resets. Back off exponentially either way; do not retry in a tight loop.

Need more throughput? Contact us — these are defaults, not ceilings.

Security best practices:

  • Store API keys in environment variables — never commit them to version control.
  • Rotate API keys regularly and use HTTPS for all requests.
  • Monitor your API usage and rate limits.

Parcels

Test parcel search (no API key required)

GET /parcels/test

Try the request and response format without an API key. Nothing is billed. Every search runs against Alachua County, whatever county you send, and is answered as an anonymous caller: the owner, values, sale prices, zoning and land use come back masked (***, or a redacted object). Send the same query to /parcels with a key for the full record.

Returns matching Alachua County parcels, with the owner, values and sale prices masked. Anything else is a refusal — see Error Responses.

Query parameters

ParameterTypeDescription
searchCounty stringCounty name (case- and separator-insensitive; ALL for statewide).
searchPin stringParcel identification number (supports * wildcard).
searchParcelId stringParcel ID (supports * wildcard).
limit integerPage size, 1 to 100. Values above 100 are capped at 100.
offset integerRow offset for pagination. Ignored when start is also sent.

Response 200

successboolean
dataarray of object
metaobject
ogc_fidinteger
countystring
extracted_atstringWhen this parcel was last extracted from the source.
countinteger
pa_pin_linkobjectA field withheld from keyless requests. Send an API key to receive it.
redactedboolean
messagestring
identifiersobject
pinstring
pin_cleanstring
pin2string nullable
pin2_cleanstring nullable
altkeystring nullable
prop_detailsstring nullable
ownerobjectA field withheld from keyless requests. Send an API key to receive it.
redactedboolean
messagestring
siteobject
addressstring nullable
citystring nullable
zipcodestring nullable
subdivisionstring nullable
subdivision_commonstring nullable
subdivision_idstring nullable
condostring nullableWithheld from keyless requests: always *** (or null where the record has no value).
landobject
acres_deednumber nullable
acres_gisnumber nullable
zoningstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
land_useobjectA field withheld from keyless requests. Send an API key to receive it.
redactedboolean
messagestring
buildingobject
num_buildingsinteger nullable
storiesnumber nullable
sqftobject
heatednumber nullable
totalnumber nullable
adjustednumber nullable
total_sqftnumber nullable
heated_sqftnumber nullable
year_built_actualinteger nullable
year_built_effectiveinteger nullable
bedsinteger nullable
bathsobject
fullinteger nullable
halfinteger nullable
valuationobject
marketobject
buildingstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
improvementsstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
landstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
agstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
totalstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
assessed_totalstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
exempt_totalstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
taxable_totalstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
salesobject
recentobject nullable
datestring nullableSale dates are not masked.
amountstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
typestring nullableWithheld from keyless requests: always *** (or null where the record has no value).
qualstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
vacstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
bookstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
pagestring nullableWithheld from keyless requests: always *** (or null where the record has no value).
docnumstring nullable
grantorstring nullable
previousarray of object
datestring nullableSale dates are not masked.
amountstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
typestring nullableWithheld from keyless requests: always *** (or null where the record has no value).
qualstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
vacstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
bookstring nullableWithheld from keyless requests: always *** (or null where the record has no value).
pagestring nullableWithheld from keyless requests: always *** (or null where the record has no value).
docnumstring nullable
grantorstring nullable
metaobject
record_countintegerRecords returned in this page.
total_countintegerTotal matching records.
test_modeboolean
data_scopeobject
countystring

Errors

StatusMeaning
400Invalid parameter.
429Too many requests. This endpoint is limited more tightly than the keyed ones; back off and retry.
500Unexpected server failure.

Search parcels

GET /parcels

Full parcel search across all 67 counties. Requires an API key. Billed per record returned. Combine spatial, owner, value, and characteristic filters; paginate with limit/offset.

Returns matching parcels. Anything else is a refusal — see Error Responses.

Query parameters

ParameterTypeDescription
format stringResponse format (case-insensitive).
enum: JSON · HTML
limit integerPage size, 1 to 100. Values above 100 are capped at 100.
start integerRow offset, and an alias of offset. Send both and start wins.
offset integerRow offset for pagination. Ignored when start is also sent.
sortBy stringField to sort by.
enum: owner · county · pin · acres · sale_date
order stringSort direction.
enum: ASC · DESC
searchCounty stringCounty name (case- and separator-insensitive; ALL for statewide).
searchCountyMulti stringComma-separated list of counties (each validated against the county list; ALL accepted; case- and separator-insensitive). NOTE: when combined with searchCounty, the two are AND-ed — sending both narrows to the intersection and disjoint sets return an empty 200. (Behavior tracked in api_v2#1602.)
searchOwner stringOwner name (partial match).
searchMailing stringOwner mailing address (partial match).
searchPin stringParcel identification number (supports * wildcard).
searchParcelId stringParcel ID (supports * wildcard).
searchAddress stringSite street address (partial match).
searchNumber stringSite street number.
searchStreet stringSite street name.
searchUnit stringSite unit.
searchCity stringSite city.
searchZipcode stringSite ZIP (5-digit or ZIP+4).
searchCityOwner stringOwner-address city.
searchStateOwner stringOwner-address state.
searchZipcodeOwner stringOwner-address ZIP (5-digit or ZIP+4).
searchAcresMin number
searchAcresMax number
saleAmountMin number
saleAmountMax number
saleDateMin string <date>Earliest sale date (YYYY-MM-DD).
saleDateMax string <date>Latest sale date (YYYY-MM-DD).
searchSqftHtdMin number
searchSqftHtdMax number
searchSqftTotMin number
searchSqftTotMax number
searchSqftAdjMin number
searchSqftAdjMax number
searchYearBuiltMin integer
searchYearBuiltMax integer
searchYearBuiltEffMin integer
searchYearBuiltEffMax integer
searchMarketValueMin number
searchMarketValueMax number
searchLandUse stringDOR land-use code(s), comma-separated.
searchLandUse4 string4-digit land-use code(s), comma-separated.
searchSubdivision string
searchLegal stringLegal description (partial match).
searchBook string
searchPage string
searchDocnum string
bbox stringBounding box xmin,ymin,xmax,ymax (EPSG:4326).
lat_lon stringPoint "lat,lon" for point-in-parcel search.
searchGeometry stringWKT geometry for spatial search (GeoJSON via POST body).
bufferPoint booleanTreat lat_lon as the center of a buffer.
bufferMeters integerBuffer radius in METRES (1-50000; 1 mile ~ 1609).
includeGeometry booleanInclude parcel geometry in the response.
includeLegal booleanInclude the legal description.
includeHazards booleanInclude hazard (flood) data.
includeFloodZoneFragments booleanInclude per-zone flood fragments.

Response 200

successboolean
dataarray of object
metaobject
ogc_fidinteger
countystring
extracted_atstringWhen this parcel was last extracted from the source.
countinteger
pa_pin_linkstringLink to the county property appraiser record.
identifiersobject
pinstring
pin_cleanstring
pin2string nullable
pin2_cleanstring nullable
altkeystring nullable
prop_detailsstring nullable
ownerobject
primary_namestring
secondary_namestring nullable
tertiary_namestring nullable
address_line1string
address_line2string nullable
address_line3string nullable
citystring
statestring
zipcodestring
siteobject
addressstring nullable
citystring nullable
zipcodestring nullable
subdivisionstring nullable
subdivision_commonstring nullable
subdivision_idstring nullable
condostring nullable
landobject
acres_deednumber nullable
acres_gisnumber nullable
zoningstring nullable
land_useobject
lusestring nullable
luse_descstring nullable
lusedorstring nullable
lusedor_descstring nullable
buildingobject
num_buildingsinteger nullable
storiesnumber nullable
sqftobject
heatednumber nullable
totalnumber nullable
adjustednumber nullable
total_sqftnumber nullable
heated_sqftnumber nullable
year_built_actualinteger nullable
year_built_effectiveinteger nullable
bedsinteger nullable
bathsobject
fullinteger nullable
halfinteger nullable
valuationobject
marketobject
buildinginteger nullable
improvementsinteger nullable
landinteger nullable
aginteger nullable
totalinteger nullable
assessed_totalinteger nullable
exempt_totalinteger nullable
taxable_totalinteger nullable
salesobject
recentobject nullable
datestring nullable
amountinteger nullable
typestring nullable
qualstring nullable
vacstring nullable
bookstring nullable
pagestring nullable
docnumstring nullable
grantorstring nullable
previousarray of object
datestring nullable
amountinteger nullable
typestring nullable
qualstring nullable
vacstring nullable
bookstring nullable
pagestring nullable
docnumstring nullable
grantorstring nullable
metaobject
record_countintegerRecords returned in this page.
total_countintegerTotal matching records.

Errors

StatusMeaning
400Invalid parameter.
401Missing or invalid API key.
403Wrong key class, or a website the key does not list. A publishable key cannot read data (code KEY_CLASS_MISMATCH).
429Rate limit or spend cap exceeded. These are two different bodies: a rate limit is the standard error envelope, a spend cap is its own shape carrying the billing state you need to recover. Check for a top-level error of spend_cap_exceeded to tell them apart.
500Unexpected server failure.
Test endpoint data scope

The keyless /parcels/test endpoint accepts every parameter, but results are limited to a fixed scope:

  • County: ALACHUA only — other counties return empty results.
  • City: GAINESVILLE only for site-address searches.
  • Sale dates: 1990-01-01 to 2000-12-31 only.

Use the full /parcels endpoint with your API key for complete access.

Error Responses

Every failure has the same shape:

{
  "success": false,
  "status": 400,
  "timestamp": "2026-09-20 15:56:09.032819",
  "errormsg": "Query validation failed.",
  "errors": [
    "Parameter 'limit' has an invalid format (received 'abc').",
    "Missing required parameter: searchCounty"
  ],
  "help": { "message": "For a complete list of valid parameters..." }
}

A 400 carries an errors array with one entry per failed rule and a help object pointing at the parameters involved. Other failures carry the same four fields with no errors.

Where a refusal has a machine-readable reason, branch on that rather than on errormsg, whose wording is written for a person and can change. A request a key gate refused carries it at data.code — KEY_CLASS_MISMATCH when a key of the wrong class is used — and a path that does not route carries it at the top level.

Every response carries an X-Request-Id, including failures. It identifies that one request in our logs, so include it when you contact us and we can find what happened instead of searching. Send your own if you would rather correlate with your logs: up to 64 letters, digits and hyphens, and we will use yours instead of ours — so send something unique per request.

StatusDescriptionCommon cause
400Bad RequestMissing searchCounty, lat_lon, or bbox
401UnauthorizedMissing or invalid API key
404Not FoundNo such endpoint path, with a valid key — without one you get 401 first, because the key is checked before the path. A search that matches nothing is not a 404: it is a 200 with an empty array and meta.record_count of 0, so do not wait for a status that will never arrive.
403ForbiddenA publishable key was sent to a data endpoint, or a secret key to a browser one — the body's code is KEY_CLASS_MISMATCH. Keys are not interchangeable; use the class the endpoint takes.
403ForbiddenOrigin not permitted for this API key — the request came from a website the key does not list, or carried no Origin and no Referer at all. Add the site in My Account, and note that a server-side call cannot use a publishable key.
429Too Many RequestsRate limit (10 requests per second on data endpoints, 60 per second on tiles) or monthly spend cap reached — check the body's error field: spend_cap_exceeded means wait for the period reset, anything else means back off and retry
500Internal Server ErrorUnexpected server failure

Best Practices

  • Validate all user-supplied input before sending requests.
  • Check meta.record_count to determine if any results were returned.
  • Handle non-200 HTTP status codes gracefully in your application.
  • Implement exponential backoff when you receive a 429 response.
  • All error responses include success: false and an errormsg field.

Contact & Support

Questions about the API, higher rate limits, or enterprise pricing? Contact us.