Categories
IT

Build a Free WiFi Geolocation API with Cloudflare Workers, powered by Apple’s WiFi database

Stop paying per request for WiFi geolocation. This Cloudflare Worker is a free, production-ready alternative to paid providers like Google Geolocation API.

It proxies Apple’s WiFi positioning service, accepts one or many BSSIDs, validates inputs, and can compute a weighted-centroid fix when you include RSSI values.

Add optional reverse geocoding and a smart auto-upgrade that retries broader lookups, and you get a fast, low-latency API that runs on the Cloudflare Free plan.

Why this project

If your app or device can observe nearby WiFi access points, you can approximate location without GPS.

Commercial APIs often charge per call and require keys, rate planning, and billing.

This Worker runs at the edge on Cloudflare, calling Apple’s WiFi database and returning coordinates with an optional human-readable address.

For multi-AP inputs with signal strengths, it computes a weighted centroid to estimate the device position more accurately.

You can use it in an MDM like as a Jamf Extension Attribute or script to trigger actions depending on where a device is etc.

Key features

  • HTTP GET and POST for single or batched BSSID lookups
  • Normalization and validation of BSSID and signal values before upstream queries
  • Signal sample summarization when multiple RSSI readings are provided
  • Weighted-centroid triangulation when you send several BSSIDs with RSSI
  • Fallback to Cloudflare IP geolocation when Apple has no data
  • Reverse geocoding via OpenStreetMap Nominatim (no keys), with smart throttling
  • Smart auto-upgrade: if an exact BSSID match returns nothing, it retries with a broader query
  • Zero server management thanks to Cloudflare Workers and Wrangler

Public repo

Code, examples, and configuration live here: wifi-geolocate-worker on GitHub

How it compares to paid geolocation APIs

This Worker is a cost-saving alternative to:

  • Google Geolocation API (WiFi plus cell; paid, API key, per-request billing)
  • HERE Positioning (WiFi positioning service; paid tiers)
  • Skyhook (legacy pioneer of WiFi positioning; commercial plans)
  • Unwired Labs (WiFi and cell geolocation; paid tiers)
  • Mozilla Location Service MLS (community database, usage limits apply)

With Cloudflare’s generous free tier and this Worker’s design, many small to mid-scale workloads can run at negligible cost while remaining very fast.

Prerequisites

  • Node.js 18 or newer to run Wrangler
  • Wrangler CLI
  • A Cloudflare account with Workers enabled

Quick start

Install Wrangler

# Local (recommended)
npm install -D wrangler@latest

# Or global
npm install -g wrangler@latest

# Verify
npx wrangler --version

Clone and setup

git clone https://github.com/gonzague/wifi-geolocate-worker.git
cd wifi-geolocate-worker
npm install

# Authenticate once
npx wrangler login
npx wrangler whoami

Run locally

npx wrangler dev
# Default local endpoint: http://127.0.0.1:8787

Deploy to Cloudflare

npx wrangler deploy
# wrangler.toml already points to worker/index.js and enables Smart Placement

API reference

GET /

Lookup a single access point by query string:

GET https://<your-worker>.workers.dev/?bssid=34:DB:FD:43:E3:A1&all=true&reverseGeocode=true
  • bssid required, 12 hex chars, separators optional
  • all optional, return all APs Apple responds with
  • reverseGeocode optional, add human-readable address

POST /

Batch query BSSIDs with optional RSSI in dBm:

{
  "accessPoints": [
    { "bssid": "34:DB:FD:43:E3:A1", "signal": -52 },
    { "bssid": "34:DB:FD:43:E3:B2", "signal": -60 },
    { "bssid": "34:DB:FD:40:01:10", "signal": -70 }
  ],
  "all": false,
  "reverseGeocode": true
}

Response shape

{
  "query": { "accessPoints": [{ "bssid": "34:db:fd:43:e3:a1", "signal": -52 }], "all": false },
  "found": true,
  "results": [
    {
      "bssid": "34:db:fd:43:e3:a1",
      "latitude": 48.856613,
      "longitude": 2.352222,
      "mapUrl": "https://www.google.com/maps/place/48.856613,2.352222",
      "signal": -52,
      "signalCount": 1,
      "signalMin": -52,
      "signalMax": -52,
      "address": {
        "displayName": "Champs-Élysées, Paris, Île-de-France, France",
        "address": {
          "road": "Champs-Élysées",
          "city": "Paris",
          "state": "Île-de-France",
          "country": "France"
        }
      }
    }
  ],
  "triangulated": {
    "latitude": 48.8571,
    "longitude": 2.3519,
    "pointsUsed": 3,
    "weightSum": 6.84,
    "method": "weighted-centroid",
    "signalWeightModel": "10^(dBm/10)"
  }
}

When Apple returns nothing, the Worker adds a Cloudflare IP geolocation fallback:

{
  "found": false,
  "fallback": {
    "latitude": 40.7128,
    "longitude": -74.0060,
    "accuracyRadius": 1000,
    "country": "US",
    "region": "NY",
    "city": "New York",
    "postalCode": "10001",
    "timezone": "America/New_York",
    "isp": "Cloudflare",
    "asOrganization": "Cloudflare, Inc."
  }
}

Smart auto-upgrade

If you request all=false and Apple has no exact match, the Worker retries with all=true and sets autoUpgraded: true in the response.

GET /?bssid=f8:ab:05:03:e9:40&all=false
{
  "query": { "accessPoints": [{"bssid": "f8:ab:05:03:e9:40", "signal": null}], "all": true },
  "found": true,
  "autoUpgraded": true,
  "results": [ ... ]
}

Reverse geocoding strategy

  • With triangulation from multiple BSSIDs, only the centroid is geocoded
  • With a single BSSID, exact matches get geocoded
  • When auto-upgraded results appear, the first result is geocoded

This respects Nominatim’s 1 req per second guideline, reduces latency, and avoids redundant calls when nearby APs share the same address.

Data handling and math

  • BSSIDs normalized to lowercase colon-separated hex
  • Signals interpreted as dBm RSSI and clamped between −120 and −5 for weighting
  • Weighted centroid uses a power model w = 10^(dBm/10) then normalizes weights

Caveats

  • Apple’s WiFi positioning is undocumented and can change
  • Always respect local law, privacy rules, and Apple terms
  • Reverse geocoding uses public OpenStreetMap Nominatim, rate limits apply

Credits and prior work

  • Academic research by François-Xavier Aguessy and Côme Demoustier on smartphone geolocation
  • Open source Apple BSSID Locator by Darko Sancanin
  • This Worker adapts those ideas for a serverless HTTP API, focusing on WiFi only

Useful links


Leave a Reply