Skip to content

Instantly share code, notes, and snippets.

@munro
Last active October 7, 2025 17:48
Show Gist options
  • Save munro/9477c48fcc6cc7a1fef12315dca22479 to your computer and use it in GitHub Desktop.
Save munro/9477c48fcc6cc7a1fef12315dca22479 to your computer and use it in GitHub Desktop.

Run with

uv run iphone_stock.py

You can change this line for your city:

LOCATION = "Miami, FL"

And then you may have to change this if you're looking for a different model:

IPHONES = {
    "iPhone 17 Pro - Cosmic Orange - 256GB": "MG7L4LL/A",
    "iPhone 17 Pro - Cosmic Orange - 512GB": "MG7P4LL/A",
    "iPhone 17 Pro - Deep Blue - 256GB": "MG7M4LL/A",
    "iPhone 17 Pro - Deep Blue - 512GB": "MG7Q4LL/A",
    "iPhone 17 Pro - Deep Blue - 1TB": "MG7U4LL/A",
}

Example output:

$ uv run iphone_stock.py
➡️  Checking stock for: iPhone 17 Pro - Cosmic Orange - 256GB ➡️
❌ No stock found for: iPhone 17 Pro - Cosmic Orange - 256GB ❌
➡️  Checking stock for: iPhone 17 Pro - Cosmic Orange - 512GB ➡️
✅ 1 in stock @ Waterside Shops - Naples, FL - 104.2 miles away ✅
➡️  Checking stock for: iPhone 17 Pro - Deep Blue - 256GB ➡️
✅ 1 in stock @ Coconut Point - Estero, FL - 108.96 miles away ✅
✅ 1 in stock @ Waterside Shops - Naples, FL - 104.2 miles away ✅
✅ 1 in stock @ Aventura - Aventura, FL - 13.41 miles away ✅
➡️  Checking stock for: iPhone 17 Pro - Deep Blue - 512GB ➡️
✅ 1 in stock @ Coconut Point - Estero, FL - 108.96 miles away ✅
✅ 1 in stock @ Waterside Shops - Naples, FL - 104.2 miles away ✅
✅ 1 in stock @ Boca Raton - Boca Raton, FL - 41.29 miles away ✅
✅ 1 in stock @ The Galleria - Fort Lauderdale, FL - 25.8 miles away ✅
✅ 1 in stock @ Aventura - Aventura, FL - 13.41 miles away ✅
✅ 1 in stock @ Dadeland - Miami, FL - 9.14 miles away ✅
➡️  Checking stock for: iPhone 17 Pro - Deep Blue - 1TB ➡️
❌ No stock found for: iPhone 17 Pro - Deep Blue - 1TB ❌
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "playwright>=1.55.0",
# "undetected-playwright>=0.3.0",
# ]
# ///
import asyncio
from playwright.async_api import Browser, Page, async_playwright
LOCATION = "Miami, FL"
IPHONES = {
"iPhone 17 Pro - Cosmic Orange - 256GB": "MG7L4LL/A",
"iPhone 17 Pro - Cosmic Orange - 512GB": "MG7P4LL/A",
"iPhone 17 Pro - Deep Blue - 256GB": "MG7M4LL/A",
"iPhone 17 Pro - Deep Blue - 512GB": "MG7Q4LL/A",
"iPhone 17 Pro - Deep Blue - 1TB": "MG7U4LL/A",
}
async def start_browser() -> tuple[Browser, Page]:
playwright = await async_playwright().__aenter__()
browser = await playwright.chromium.launch(headless=True)
context = await browser.new_context(
locale="en-US,en;q=0.9,it;q=0.8", no_viewport=True
)
page = await context.new_page()
page.set_default_timeout(15000)
page.set_default_navigation_timeout(15000)
await add_stealth(page)
return browser, page
async def add_stealth(page: Page):
await page.add_init_script(r"""
// remove webdriver from prototype (works in many cases)
try {
delete Object.getPrototypeOf(navigator).webdriver;
} catch (e) {}
// and also define it explicitly to be safe
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
configurable: true
});
""")
def parse_availability(availability: dict, product_id: str):
objs = []
for store in availability["body"]["content"]["pickupMessage"]["stores"]:
availability = store["partsAvailability"][product_id]
is_buyable = availability["buyability"]["isBuyable"]
quantity = availability["buyability"]["inventory"]
store_name = store["storeName"]
store_city = store["city"]
store_state = store["state"]
distance = store["storedistance"]
obj = {
"store_name": store_name,
"city": store_city,
"state": store_state,
"is_buyable": is_buyable,
"quantity": quantity,
"distance": distance,
}
objs.append(obj)
objs = sorted(objs, key=lambda x: (x["is_buyable"], -x["distance"]))
return objs
def print_stock(objs: list[dict]):
for item in objs:
msg = f"{item['quantity']} in stock @ {item['store_name']} - {item['city']}, {item['state']} - {item['distance']} miles away"
if item["is_buyable"]:
print(f"✅ {msg} ✅")
else:
print(f"❌ {msg} ❌")
async def run_stock_check(page: Page):
await page.goto("https://www.apple.com/iphone/", wait_until="networkidle")
assert await page.evaluate("navigator.webdriver") is False
await page.goto(
"https://www.apple.com/shop/buy-iphone/iphone-17-pro/6.3-inch-display-256gb-deep-blue-unlocked",
wait_until="networkidle",
)
for name, product_id in IPHONES.items():
print(f"➡️ Checking stock for: {name} ➡️")
response = await get_fulfillment_messages(page, product_id)
objs = parse_availability(response, product_id)
objs = [x for x in objs if x["is_buyable"] or x["quantity"] > 0]
if not objs:
print(f"❌ No stock found for: {name} ❌")
continue
else:
print_stock(objs)
async def main():
browser, page = await start_browser()
await run_stock_check(page)
await browser.close()
async def get_fulfillment_messages(page: Page, product_id: str):
response = await page.request.get(
"https://www.apple.com/shop/fulfillment-messages",
params={
"fae": "true",
"pl": "true",
"mts.0": "regular",
"mts.1": "compact",
"cppart": "UNLOCKED/US",
"parts.0": product_id,
"location": LOCATION,
},
)
assert response.status == 200
return await response.json()
if __name__ == "__main__":
asyncio.run(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment