Back to Articles

Reverse-Engineering Apple's FindMy Network: A Deep Dive into FindMy.py

[ View on GitHub ]

Reverse-Engineering Apple's FindMy Network: A Deep Dive into FindMy.py

Hook

Apple's FindMy network processes over 100 million location reports daily from a billion devices worldwide, yet until recently, accessing this data required being locked into Apple's ecosystem—FindMy.py changes that by reverse-engineering the entire protocol stack.

Context

Apple's FindMy network is a remarkable feat of distributed computing: every iPhone, iPad, and Mac becomes a Bluetooth beacon scanner, anonymously relaying encrypted location data from lost AirTags and devices to Apple's servers. The genius is in the cryptography—only the device owner can decrypt these location reports, ensuring privacy even as millions of strangers' phones help locate your lost keys.

For years, this network remained effectively closed. If you wanted to track your AirTag, you needed an Apple device or access to iCloud.com. Researchers and hobbyists who built DIY trackers using the OpenHaystack project needed Macs to query locations. Cross-platform applications couldn't integrate FindMy tracking. The community responded with fragmented reverse-engineering efforts—biemster's FindMy for location queries, Pypush for authentication protocols, various anisette servers for device attestation—but these remained disconnected pieces. FindMy.py emerged as the first comprehensive Python library that unifies this research, implementing everything from Apple account authentication through 2FA to cryptographic decryption of location reports, all without requiring macOS.

Technical Insight

The architecture of FindMy.py reveals just how much complexity Apple hides behind the FindMy interface. At its core, the library must solve three distinct problems: authenticating as a legitimate Apple device, querying the FindMy network for encrypted location reports, and decrypting those reports using elliptic curve cryptography.

Authentication proves particularly challenging because Apple doesn't just want your username and password—they want proof you're using genuine Apple hardware. This is where anisette provisioning enters. Anisette data is a collection of device-specific headers that Apple's servers validate to prevent unauthorized access. FindMy.py handles this by integrating with anisette providers (either local servers or the library's built-in provisioning), then managing the full authentication flow including SMS and trusted-device 2FA. Here's how you authenticate and fetch locations:

from findmy import FindMyAccessory, KeyPair
from findmy.reports import RemoteAnisetteProvider, AppleAccount
import asyncio

async def track_airtag():
    # Set up anisette provider for device attestation
    anisette = RemoteAnisetteProvider("https://ani.example.com")
    
    # Authenticate with Apple (handles 2FA automatically)
    acc = AppleAccount("user@icloud.com", "password", anisette)
    await acc.login()  # May prompt for 2FA code
    
    # For an official AirTag, use its serial/IMEI
    accessory = FindMyAccessory(acc, "AIRTAG_SERIAL_HERE")
    
    # Fetch and decrypt location reports
    locations = await accessory.fetch_locations()
    for report in locations:
        print(f"Location: {report.latitude}, {report.longitude}")
        print(f"Timestamp: {report.timestamp}")
        print(f"Accuracy: {report.horizontal_accuracy}m")

asyncio.run(track_airtag())

The cryptographic implementation deserves particular attention. FindMy uses a rotating key scheme where each device broadcasts a public key that changes every 15 minutes (derived from a master key pair). When other Apple devices detect this broadcast, they encrypt the location using this public key and upload it to Apple's servers. To decrypt these reports, FindMy.py must generate all possible public keys for the time period you're querying (since the device's key rotates), fetch reports matching those keys, then decrypt them using the corresponding private keys:

from findmy import KeyPair
import datetime

# For DIY OpenHaystack tags, you manage keys directly
master_key = KeyPair()  # Or load existing: KeyPair.from_b64(saved_key)

# Generate the rolling public keys for the last 7 days
start_date = datetime.datetime.now() - datetime.timedelta(days=7)
keys = master_key.derive_keys(start_date, datetime.datetime.now())

print(f"Generated {len(keys)} rolling keys")
# Each key is valid for ~15 minutes, so 7 days ≈ 672 keys

The library also implements Bluetooth scanning for detecting nearby FindMy devices, which is invaluable for debugging or building proximity-based applications. When you scan, FindMy.py decodes the Bluetooth advertisement data to extract the public key and status byte, letting you identify devices before they even report locations:

from findmy.scanner import OfflineFindingScanner

def on_device_found(device, public_key, status):
    print(f"Found device: {device.name}")
    print(f"Public key: {public_key.hex()}")
    print(f"Status: {status:#04x}")
    # Status byte encodes battery level, motion state, etc.

scanner = OfflineFindingScanner(on_device_found)
scanner.scan(duration=30)  # Scan for 30 seconds

What makes FindMy.py particularly impressive is how it consolidates disparate research. The anisette provisioning builds on Pypush's work, the cryptographic implementation draws from OpenHaystack's specifications, and the API interaction evolved from biemster's original FindMy. By unifying these into a coherent API with both sync and async support, it transforms scattered proof-of-concept code into a genuinely usable library.

Gotcha

The elephant in the room is sustainability and legality. FindMy.py works through reverse engineering—there's no official API, no developer agreement, no support channel. Apple could change their protocols tomorrow, implement additional security checks, or start flagging accounts that exhibit unusual access patterns. The library's reliance on anisette providers is particularly fragile; these services themselves may face legal pressure or technical countermeasures. If you build a production application on FindMy.py, you're accepting the risk that it could simply stop working after an Apple server update.

There are also practical limitations around authentication. While the library handles 2FA gracefully, accounts with advanced security features (like hardware security keys) may not work. The documentation explicitly notes that the CLI is still in development, and some edge cases around account types and device configurations remain unhandled. Rate limiting is another concern—aggressive querying could trigger Apple's anti-abuse systems, potentially locking you out of your account. The library doesn't implement backoff or retry logic by default, so you'll need to add that yourself for any serious application. Additionally, if you're tracking official Apple accessories rather than DIY OpenHaystack tags, you're working with device identifiers that may change or require reverse engineering additional Apple endpoints to discover programmatically.

Verdict

Use if: you're building cross-platform tracking applications that need FindMy integration, developing tools for OpenHaystack-compatible DIY trackers, conducting research on location privacy and tracking networks, or need to integrate AirTag tracking into automation workflows where official Apple tools don't suffice. This library is perfect for hobbyists, researchers, and developers comfortable with reverse-engineered APIs who understand the inherent fragility. Skip if: you need production-stable code with vendor support and SLAs, you're uncomfortable with potential ToS violations that could affect your Apple account, you can accomplish your goals using official Apple platforms (iCloud.com, Find My app), or you require legal indemnification for commercial use. The library is brilliant technically but fundamentally at odds with Apple's business model—use it with eyes wide open to the risks.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/malmeloo-findmy-py.svg)](https://starlog.is/api/badge-click/developer-tools/malmeloo-findmy-py)