Wi-Fi Triangulation for Geolocation on Windows Using netsh and Unwired Labs API

April 12, 2025

Introduction

Wi-Fi triangulation is a method to geolocate a device by analyzing the signal strengths and MAC addresses (BSSIDs) of nearby Wi-Fi access points. It uses trilateration to estimate a device's position based on the relative distances to multiple access points, derived from signal strength data. This article explains the process of implementing Wi-Fi triangulation on a Windows system using the netsh command-line tool, the Unwired Labs Location API for geolocation, and the Nominatim API for reverse geocoding. We'll break down each step, from scanning networks to converting coordinates into a human-readable address, and address common pitfalls in the process.

Prerequisites

To execute this process, you'll need:

  • A Windows 10 or 11 system (we're focusing on Windows-specific commands).
  • < SMALL>li>Administrative access to run network commands.
  • An API key from Unwired Labs (unwiredlabs.com) for Wi-Fi geolocation.
  • A tool like curl (available for Windows) to make API requests.
  • An email address for Nominatim API usage, per their policy.
  • An active internet connection for API calls.

Step 1: Collecting Wi-Fi Data with netsh

Wi-Fi access points broadcast beacon frames containing their SSID (network name), BSSID (MAC address), and other metadata. Signal strength, reported as a percentage or in dBm, indicates how close the device is to the access point. We need to collect this data for all visible networks. On Windows, we use the netsh utility to scan for Wi-Fi networks:

netsh wlan show networks mode=bssid
  • netsh: Network Shell, a Windows utility for network management.
  • wlan: Targets the wireless LAN subsystem.
  • show networks: Lists all visible Wi-Fi networks.
  • mode=bssid: Includes detailed info like BSSID and signal strength.

Example Output

Interface name : Wi-Fi
There are 3 networks currently visible.

SSID 1 : CyberNet-2G
    Network type            : Infrastructure
    Authentication          : WPA2-Personal
    Encryption              : CCMP
    BSSID 1                 : aa:bb:cc:dd:ee:ff
         Signal             : 72%
         Radio type         : 802.11n
         Band               : 2.4 GHz
         Channel            : 6
SSID 2 : CyberNet-5G
    Network type            : Infrastructure
    Authentication          : WPA2-Personal
    Encryption              : CCMP
    BSSID 1                 : aa:bb:cc:dd:ee:fe
         Signal             : 65%
         Radio type         : 802.11ac
         Band               : 5 GHz
         Channel            : 48
SSID 3 : NeonGrid
    Network type            : Infrastructure
    Authentication          : WPA2-Personal
    Encryption              : CCMP
    BSSID 1                 : 11:22:33:44:55:66
         Signal             : 40%
         Radio type         : 802.11n
         Band               : 2.4 GHz
         Channel            : 1

Key data extracted:

  • SSID: CyberNet-2G, BSSID: aa:bb:cc:dd:ee:ff, Signal: 72%
  • SSID: CyberNet-5G, BSSID: aa:bb:cc:dd:ee:fe, Signal: 65%
  • SSID: NeonGrid, BSSID: 11:22:33:44:55:66, Signal: 40%

Challenge: Incomplete Scans

Windows caches network lists and may only show the connected network if a fresh scan isn’t triggered. This issue is well-documented here:
https://superuser.com/questions/1446576/netsh-wlan-show-networks-only-shows-the-currently-connected-wifi-network

  1. Identify the interface:
    netsh wlan show interfaces
  2. Disable the interface:
    netsh interface set interface "Wi-Fi" admin=disable
  3. Re-enable it:
    netsh interface set interface "Wi-Fi" admin=enable
  4. Wait 5 seconds, then re-run the scan command.

Post-Processing Data

The output is a text string that needs parsing to extract SSID, BSSID, and signal strength. For the API, we need to:

  • Format the BSSID as xx-xx-xx-xx-xx-xx (replace colons with hyphens).
  • Convert signal strength from percentage to dBm using:
    signal_dbm = -100 + (signal_percentage * 0.7)

Examples:

  • 72% → -49.6 dBm
  • 65% → -54.5 dBm
  • 40% → -72.0 dBm

Processed data:

  • SSID: CyberNet-2G, BSSID: aa-bb-cc-dd-ee-ff, Signal: -49.6 dBm
  • SSID: CyberNet-5G, BSSID: aa-bb-cc-dd-ee-fe, Signal: -54.5 dBm
  • SSID: NeonGrid, BSSID: 11-22-33-44-55-66, Signal: -72.0 dBm

Step 2: Geolocating with Unwired Labs API

The Unwired Labs API geolocates devices using Wi-Fi data. Endpoint:

https://us1.unwiredlabs.com/v2/process.php

Example JSON payload:

{
  "token": "pk.1234567890abcdef1234567890abcdef",
  "wifi": [
    { "bssid": "aa-bb-cc-dd-ee-ff", "signal": -49.6 },
    { "bssid": "aa-bb-cc-dd-ee-fe", "signal": -54.5 },
    { "bssid": "11-22-33-44-55-66", "signal": -72.0 }
  ],
  "address": 1
}

Send it using curl:

curl -X POST -H "Content-Type: application/json" -d @payload.json https://us1.unwiredlabs.com/v2/process.php

Expected response:

{
  "status": "ok",
  "lat": 40.7128,
  "lon": -74.0060,
  "accuracy": 50
}

Failure response:

{
  "status": "error",
  "message": "No matches found",
  "balance": 100
}

Step 3: Reverse Geocoding with Nominatim

Endpoint:

https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json

Example request:

curl -H "User-Agent: WiFiTriangulation/1.0 ([email protected])" "https://nominatim.openstreetmap.org/reverse?lat=40.7128&lon=-74.0060&format=json"

Example response:

{
  "display_name": "New York, New York County, New York, United States",
  "address": {
    "city": "New York",
    "county": "New York County",
    "state": "New York",
    "country": "United States"
  }
}

Map link:

https://www.openstreetmap.org/?mlat=40.7128&mlon=-74.0060#map=15/40.7128/-74.0060

Final Thought

Wi-Fi triangulation isn’t locked to just one tool or API. We used Unwired Labs here mainly because it's free and simple to work with, but there are plenty of other services out there that do the same thing. You can swap in different APIs, use custom tools, and even automate the whole process if you want. It’s pretty lightweight, and once you get the basics down, it’s easy to build on or tweak for your own use cases.