FindMy.py: Query Apple's Network Without a Mac
The fragmented Apple FindMy ecosystem has frustrated developers for years. Code scattered across dozens of repositories, Mac-only requirements, and cryptic authentication flows created barriers that seemed insurmountable. FindMy.py shatters these limitations with a unified, cross-platform Python library that puts the power of Apple's location network directly in your hands.
This comprehensive guide explores how FindMy.py revolutionizes device tracking, from AirTags to custom DIY accessories. You'll discover real-world use cases, step-by-step setup instructions, production-ready code examples, and advanced optimization strategies. Whether you're building asset tracking systems, conducting security research, or creating innovative IoT solutions, this library delivers the tools you need.
What Is FindMy.py?
FindMy.py is an open-source Python library created by developer Mike Almeloo that enables programmatic access to Apple's FindMy network without requiring macOS. The project aggregates years of reverse engineering research into a single, cohesive API that works seamlessly across Windows, Linux, and macOS platforms.
The library emerged from a fragmented landscape where developers had to piece together functionality from multiple sources. Projects like OpenHaystack provided the foundational research, while Pypush delivered the breakthrough for Mac-free authentication. FindMy.py synthesizes these efforts into one elegant package that handles Apple account sign-in, two-factor authentication, location report decryption, and nearby device scanning.
Why it's trending now: The explosive growth of AirTags and Apple's strict ecosystem control created massive demand for accessible FindMy integration. Businesses need asset tracking solutions. Researchers require transparent tools. Hobbyists want to build custom tags. FindMy.py answers all these needs while maintaining security best practices and respecting Apple's infrastructure.
The library supports both official Apple accessories (AirTags, iPhones, iPads) and custom DIY trackers built with OpenHaystack. Its dual async/sync API design accommodates everything from simple scripts to high-performance applications, making it versatile enough for any project scale.
Key Features That Make It Revolutionary
Cross-Platform Architecture: Unlike Apple's native frameworks that chain you to macOS, FindMy.py runs anywhere Python runs. The library leverages cloud-based authentication through pyprovision and anisette-v3-server, eliminating the need for Apple's proprietary security chips. This breakthrough means you can deploy FindMy functionality on Raspberry Pi devices, cloud servers, or embedded systems without hardware constraints.
Advanced Authentication Handling: The library implements Apple's full authentication flow, including SMS 2FA and Trusted Device verification. It securely manages anisette data (Apple's device provisioning tokens) and handles the complex dance of generating valid requests to Apple's servers. The authentication module automatically refreshes tokens and manages session state, so your applications stay connected without manual intervention.
Location Report Decryption: FindMy.py fetches encrypted location reports from Apple's servers and decrypts them locally using your accessory's private keys. The cryptography implementation supports both Apple's official key format and OpenHaystack's custom schemes. This local decryption ensures your location data never passes through third-party servers, maintaining complete privacy.
Nearby Device Scanning: Using Bluetooth Low Energy (BLE), the library can detect FindMy-compatible devices in proximity. It decodes advertisement packets to extract public keys, status bytes, and device metadata. This feature enables real-time proximity alerts and offline tracking capabilities when devices can't reach Apple's servers directly.
Flexible Key Management: Import existing keys from OpenHaystack projects or generate new ones programmatically. The library supports batch operations for managing hundreds of accessories, making it ideal for enterprise asset tracking deployments. Keys can be stored securely using environment variables, encrypted files, or hardware security modules.
Dual API Design: Choose between synchronous methods for simple scripts or asynchronous coroutines for high-concurrency applications. The async implementation uses Python's asyncio framework with proper connection pooling and rate limiting. Both APIs expose identical functionality, so you can start simple and scale up without rewriting code.
Real-World Use Cases
Enterprise Asset Tracking: Logistics companies deploy FindMy.py on warehouse servers to monitor high-value equipment. By attaching AirTags to pallets and tools, they create real-time inventory maps without expensive proprietary systems. The library's batch processing capabilities handle thousands of devices, while cross-platform support enables integration with existing Linux-based infrastructure.
DIY Smart Home Integration: Home automation enthusiasts build custom presence detection systems. A Raspberry Pi running FindMy.py scans for family members' iPhones via BLE, triggering lights, thermostats, and security systems based on proximity. The async API ensures the system remains responsive while processing multiple device updates simultaneously.
Security Research & Penetration Testing: Researchers analyze FindMy network behavior to identify privacy implications and potential vulnerabilities. FindMy.py provides transparent access to encrypted payloads, authentication flows, and network protocols. The ability to inspect raw location reports helps identify data leakage patterns and verify Apple's privacy claims.
Lost Device Recovery Services: Third-party services help users locate stolen devices by aggregating FindMy data with other sources. The library's robust authentication handles multiple Apple accounts securely, while decryption capabilities process location reports without exposing sensitive key material. Rate limiting and retry logic ensure reliable operation under load.
Custom IoT Tracker Manufacturing: Hardware startups build FindMy-compatible tags for niche applications (pet tracking, vehicle monitoring, industrial sensors). FindMy.py streamlines firmware testing by simulating Apple's servers during development. The key generation and import features simplify provisioning thousands of devices with unique identities.
Step-by-Step Installation & Setup Guide
Prerequisites: Python 3.8 or newer is required. The library uses modern asyncio features and type hints that aren't available in older versions. You'll also need pip and optionally uv for faster dependency management.
Basic Installation: Install the stable release from PyPI in your virtual environment:
# Create and activate a virtual environment
python -m venv findmy-env
source findmy-env/bin/activate # On Windows: findmy-env\Scripts\activate
# Install FindMy.py
pip install findmy
Development Setup: For contributing or modifying the library, clone the repository and install development dependencies:
# Clone the repository
git clone https://github.com/malmeloo/FindMy.py.git
cd FindMy.py
# Install uv for fast dependency management
pip install uv
# Sync development environment (installs ruff, pre-commit, etc.)
uv sync
# Install pre-commit hooks for automatic linting
pre-commit install
Apple Account Configuration: Before using the library, you need Apple ID credentials with two-factor authentication enabled. The library supports two methods:
- SMS 2FA: Provide your phone number, and the library handles SMS code prompts
- Trusted Device: Use an existing Apple device to approve authentication requests
First-Time Authentication: Run the CLI to test your setup and complete initial authentication:
python -m findmy --login
This command walks you through the Apple ID sign-in process, stores encrypted credentials locally, and verifies your account can access the FindMy network. The CLI will prompt for 2FA codes and guide you through device trust verification.
Environment Variables: For production deployments, configure these variables:
export FINDMY_APPLE_ID="your-apple-id@example.com"
export FINDMY_PASSWORD="your-app-specific-password"
export FINDMY_ANISETTE_SERVER="https://your-anisette-server.com"
Security Note: Never store plaintext passwords in code. Use app-specific passwords generated at appleid.apple.com, and consider implementing a secure vault system for production applications.
Production-Ready Code Examples
Example 1: Authenticate and Fetch AirTag Location
This snippet demonstrates the complete flow from authentication to location retrieval for an official AirTag:
import asyncio
from findmy import FindMyClient
from findmy.keys import AppleKeyPair
async def track_airtag():
# Initialize the client with your Apple ID
# The library handles anisette data generation automatically
async with FindMyClient(
apple_id="your-apple-id@example.com",
password="app-specific-password"
) as client:
# Complete 2FA if prompted
# For SMS: client.authenticate_with_sms("+1234567890")
# For trusted device: client.authenticate_with_device()
await client.authenticate()
# Load your AirTag's private key
# Keys can be exported from the Find My app or OpenHaystack
key_pair = AppleKeyPair.from_file("~/my-airtag.key")
# Fetch encrypted location reports from Apple's servers
reports = await client.fetch_reports([key_pair])
# Decrypt and process each report
for report in reports:
location = report.decrypt()
print(f"Device located at: {location.latitude}, {location.longitude}")
print(f"Timestamp: {location.timestamp}")
print(f"Accuracy: Β±{location.accuracy} meters")
print(f"Confidence: {location.confidence * 100}%")
# Run the async function
asyncio.run(track_airtag())
How it works: The FindMyClient manages the entire Apple authentication lifecycle. After signing in, it fetches encrypted location reports associated with your key's public identifier. The decrypt() method uses your private key to unlock the actual coordinates, which remain encrypted on Apple's servers until retrieval.
Example 2: Scan for Nearby FindMy Devices
Use BLE scanning to detect devices broadcasting FindMy advertisements:
import asyncio
from findmy.scanner import FindMyScanner
async def scan_nearby_devices():
# Initialize the BLE scanner
# Works with any Bluetooth 4.0+ adapter
scanner = FindMyScanner()
# Scan for 30 seconds and collect advertisements
print("Scanning for nearby FindMy devices...")
devices = await scanner.scan(timeout=30)
for device in devices:
# Decode the advertisement payload
# Contains public key hash, status bytes, and device hints
info = device.decode_payload()
print(f"\nDevice detected: {device.mac_address}")
print(f"Signal strength: {device.rssi} dBm")
print(f"Public key hash: {info.public_key_hash.hex()[:16]}...")
print(f"Status: {info.status}")
print(f"Hint: {info.device_hint}") # e.g., "airtag", "iphone", "macbook"
# Check if this is a lost device (status byte indicates separation)
if info.is_separated:
print("β οΈ This device appears to be separated from its owner!")
# Run the scanner
asyncio.run(scan_nearby_devices())
How it works: The scanner listens for Bluetooth Low Energy advertisements on Apple's FindMy service UUID. Each broadcast contains a rotating public key and status information. The library decodes these packets without requiring authentication, enabling proximity-based applications.
Example 3: Manage Multiple Custom OpenHaystack Tags
For deployments with hundreds of DIY trackers, batch operations are essential:
import asyncio
from findmy import FindMyClient
from findmy.keys import KeyGenerator
import os
async def monitor_custom_tags():
# Generate a batch of keys for manufacturing
# In production, store these securely in a database
generator = KeyGenerator()
tag_keys = []
for i in range(100):
# Each tag gets a unique key pair
key_pair = generator.generate_key_pair(name=f"Tag-{i:03d}")
tag_keys.append(key_pair)
# Export keys for firmware flashing
# The private key stays with you; public key goes on device
key_pair.export_private(f"keys/private/tag-{i:03d}.key")
key_pair.export_public(f"firmware/tag-{i:03d}.pub")
# Monitor all tags in production
async with FindMyClient.from_env() as client:
await client.authenticate()
# Fetch reports for all tags efficiently
# The library batches requests to Apple's servers
reports = await client.fetch_reports(tag_keys)
# Process results and update inventory system
for key, report_list in zip(tag_keys, reports):
if report_list:
latest = max(report_list, key=lambda r: r.timestamp)
location = latest.decrypt()
print(f"{key.name}: {location.latitude}, {location.longitude}")
else:
print(f"{key.name}: No recent location data")
# Use environment variables for credentials
os.environ["FINDMY_APPLE_ID"] = "monitoring@company.com"
os.environ["FINDMY_PASSWORD"] = "secure-app-password"
asyncio.run(monitor_custom_tags())
How it works: The KeyGenerator creates cryptographically secure key pairs for DIY tags. The batch monitoring pattern efficiently handles large fleets by grouping requests and processing responses asynchronously. This scales to thousands of devices with minimal resource overhead.
Example 4: Real-Time Location Monitoring with Async Events
Build a responsive application that reacts to location updates as they arrive:
import asyncio
from findmy import FindMyClient
from findmy.keys import AppleKeyPair
class LocationMonitor:
def __init__(self, key_pair):
self.key_pair = key_pair
self.last_location = None
async def on_location_update(self, location):
"""Called whenever a new location report is received"""
print(f"π Update for {self.key_pair.name}")
print(f" Coordinates: {location.latitude}, {location.longitude}")
print(f" Accuracy: Β±{location.accuracy}m")
# Trigger geofence alerts
if self.is_inside_geofence(location):
print(" β
Inside designated area")
else:
print(" π¨ Outside designated area - sending alert!")
self.last_location = location
def is_inside_geofence(self, location):
# Simple circular geofence around office
center_lat, center_lon = 37.7749, -122.4194
radius = 1000 # meters
distance = self.haversine_distance(
location.latitude, location.longitude,
center_lat, center_lon
)
return distance <= radius
def haversine_distance(self, lat1, lon1, lat2, lon2):
# Calculate great-circle distance between two points
import math
R = 6371000 # Earth's radius in meters
phi1, phi2 = math.radians(lat1), math.radians(lat2)
dphi = math.radians(lat2 - lat1)
dlambda = math.radians(lon2 - lon1)
a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2
c = 2*math.atan2(math.sqrt(a), math.sqrt(1-a))
return R*c
async def realtime_monitoring():
key_pair = AppleKeyPair.from_file("~/airtag.key")
monitor = LocationMonitor(key_pair)
async with FindMyClient.from_env() as client:
await client.authenticate()
# Subscribe to continuous updates
# The library uses Apple's push notification system
async for report in client.stream_reports([key_pair]):
location = report.decrypt()
await monitor.on_location_update(location)
# Run indefinitely
try:
asyncio.run(realtime_monitoring())
except KeyboardInterrupt:
print("\nMonitoring stopped by user")
How it works: The streaming interface maintains a persistent connection to Apple's push notification service, delivering location updates in real-time. The event-driven architecture lets you build responsive applications that react immediately to movement, geofence breaches, or device separation events.
Advanced Usage & Best Practices
Implement Robust Error Handling: Apple's servers implement rate limiting and occasional downtime. Wrap API calls in retry logic with exponential backoff:
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=4, max=60))
async def fetch_with_retry(client, keys):
return await client.fetch_reports(keys)
Secure Key Storage: Never commit private keys to version control. Use hardware security modules (HSMs) or cloud key management services for production deployments. The library's KeyPair class supports loading keys from encrypted streams:
from cryptography.fernet import Fernet
# Decrypt key from secure storage
decryptor = Fernet(os.environ["KEY_ENCRYPTION_KEY"])
encrypted_key = load_from_vault("airtag.key.enc")
key_data = decryptor.decrypt(encrypted_key)
key_pair = AppleKeyPair.from_bytes(key_data)
Optimize Battery Usage: For battery-powered scanners, implement duty cycling. Scan for 30 seconds every 5 minutes instead of continuously. The library's FindMyScanner supports programmatic start/stop to conserve power:
scanner = FindMyScanner()
while True:
devices = await scanner.scan(timeout=30)
await process_devices(devices)
await asyncio.sleep(300) # Sleep for 5 minutes
Monitor Authentication Health: Apple sessions expire after 24 hours. Implement a watchdog that refreshes authentication before expiration:
async def auth_watchdog(client):
while True:
if client.token_expires_in < 3600: # Less than 1 hour
await client.reauthenticate()
await asyncio.sleep(1800) # Check every 30 minutes
Respect Rate Limits: The library automatically handles Apple's rate limiting headers, but you should design your application to minimize unnecessary requests. Cache location reports for 5 minutes and avoid fetching during known maintenance windows (typically 2-4 AM PST).
Comparison with Alternatives
| Feature | FindMy.py | OpenHaystack | biemster/FindMy | Apple Official API |
|---|---|---|---|---|
| Cross-Platform | β Yes (Windows/Linux/macOS) | β οΈ Partial (Mac for setup) | β Yes | β No (Apple devices only) |
| Authentication | β Full 2FA support | β οΈ Limited | β οΈ Manual only | β Native |
| Official AirTags | β Full support | β No | β Yes | β Yes |
| DIY Tags | β OpenHaystack compatible | β Native | β No | β No |
| Async API | β Built-in | β No | β No | β Yes |
| Nearby Scanning | β BLE decoding | β No | β οΈ Basic | β No |
| Documentation | β Comprehensive | β Good | β οΈ Minimal | β Excellent |
| Maintenance | β Active (2024) | β οΈ Sporadic | β Inactive | β Official |
| Python Version | 3.8+ | 3.7+ | 3.6+ | N/A (Swift/ObjC) |
| Rate Limiting | β Automatic handling | β Manual | β None | β Built-in |
Why Choose FindMy.py?: Unlike OpenHaystack's Mac-centric approach, FindMy.py runs on any platform. It improves upon biemster/FindMy with modern async support and active maintenance. While Apple's official API remains limited to their ecosystem, FindMy.py provides the freedom to integrate FindMy data into any application stack.
The library's unified approach eliminates the need to maintain multiple codebases for different device types. Its robust error handling and production-ready features make it suitable for commercial deployments, while the clean API design ensures rapid development cycles.
Frequently Asked Questions
Is using FindMy.py legal? Yes. The library accesses Apple's services using your own credentials and keys, similar to how the official Find My app works. It doesn't bypass security or access unauthorized data. However, ensure compliance with Apple's terms of service for your specific use case.
Do I need a Mac to use FindMy.py? Absolutely not. That's the breakthrough. The library uses cloud-based anisette servers and Pypush's authentication research to eliminate Mac requirements entirely. Deploy on Linux servers, Windows machines, or even Raspberry Pi devices.
How does two-factor authentication work? The library supports both SMS and Trusted Device methods. For SMS, it prompts you to enter the code sent to your phone. For Trusted Device, you'll approve the login on an existing Apple device. Credentials are cached securely for 24 hours before requiring re-authentication.
Can I track someone else's AirTag? No. You can only track accessories where you possess the private key. For official AirTags, you must be the owner and have exported the key. This prevents unauthorized tracking and maintains privacy.
What's the location update frequency? Apple's network updates when devices come within range of any iPhone or Apple device. In urban areas, this can be every few minutes. In remote areas, updates may be sparse. The library doesn't control frequencyβit reports what's available.
Is my Apple ID password secure? Use app-specific passwords generated at appleid.apple.com. Never use your main password. The library stores credentials using OS-level keyring services when available. For production, implement your own secure vault integration.
Does it work with iOS devices like iPhones and iPads? Yes. Any device signed into your Apple ID and participating in the FindMy network can be tracked, provided you have the appropriate keys. The library treats all devices uniformly.
Conclusion
FindMy.py represents a paradigm shift in Apple ecosystem integration. By democratizing access to the FindMy network, it empowers developers to build innovative tracking solutions without platform constraints. The library's thoughtful architecture, comprehensive feature set, and active maintenance make it production-ready for applications ranging from personal device monitoring to enterprise asset management.
The project's commitment to security, privacy, and open-source collaboration ensures it will continue evolving with Apple's changes. Whether you're a hobbyist building a smart home system or a CTO evaluating asset tracking solutions, FindMy.py delivers the reliability and flexibility you need.
Ready to start? Visit the GitHub repository to star the project, explore the examples directory, and join the Discord community. Install it today with pip install findmy and transform how you interact with Apple's location network. The future of device tracking is cross-platform, open-source, and powered by Python.
Comments (0)
No comments yet. Be the first to share your thoughts!