# findmeaduo > Live Apple Store in-store pickup stock: which stores have a given iPhone available for pickup today. 25 countries, about 1,000 stores. Data is polled from apple.com every ~2 to 5 minutes (only while a store is open, plus a margin). It tracks pickup availability only, not prices, shipping or trade-in. Base URL: https://findmeaduo.fforr.es/api/v1 All endpoints are `GET`, return JSON (`application/json`) and send `access-control-allow-origin: *`. No API key. ## Endpoints - `GET /devices`: every tracked device. Use it to find a product id. `{"products":[{"id":"iphone-17/256gb/black","family":"iPhone 17","model":"iPhone 17","color":"Black","colorKey":"black","capacity":"256GB","sortKey":120,"countries":["AE","AU","US"]}],"enabledCountries":["AE","AU","US"]}` Product ids look like `//` and contain slashes. - `GET /stores?country=US`: stores, optionally of one country (ISO 3166-1 alpha-2, case-insensitive; omitted or empty = all). `{"stores":[{"id":"apple:R001","retailer":"apple","externalId":"R001","country":"US","name":"Stanford","slug":"stanford","address":{"street":"660 Stanford Shopping Center","city":"Palo Alto","region":"CA","postalCode":"94304"},"coordinates":{"lat":37.44,"lng":-122.17},"timezone":"America/Los_Angeles"}]}` - `GET /nearby`: stores near a location, nearest first, with the availability of one device. - `product` (required): a product id from `/devices`, for example `product=iphone-17%2F256gb%2Fblack`. - Location, one of: `country=US&postal=94301`; or `lat=37.44&lng=-122.14`; or neither (then the caller's IP geolocation is used, when known). `postal` needs `country`. - One product per call: checking every color of a model costs one call per color. To see quickly whether any variant is in stock anywhere, use `/devices//geo` or `/geo` first, then `/nearby` for the ones with stock. - `radiusKm` (default 80), `only=1` (only stores where it is available), `scope=everywhere` (ignore the radius: up to 100 stores worldwide where it is available; the location is then optional). - Response: `{"scope":"nearby","origin":{"lat":37.42,"lng":-122.14},"results":[{"store":{...as in /stores},"sku":{"id":"apple:US:MG464LL/A","partNumber":"MG464LL/A"},"distanceKm":3.1,"availability":{"status":"available","quote":"Today","checkedAt":1790000000000},"stale":false,"links":{"appleMaps":"https://...","googleMaps":"https://..."}}],"generatedAt":1790000012345}` - `availability.status` is `available`, `unavailable`, `ineligible` (store cannot do pickup for it) or `unknown`. `checkedAt` is epoch milliseconds; `stale` is true when it is older than the freshness window. Times are UTC epoch ms. - `GET /geo`: every served country with its store count and, per device, the number of stores where it is available. `{"generatedAt":1790000000000,"countries":[{"country":"US","name":"United States","stores":272,"devices":[{"id":"iphone-17/256gb/black","name":"iPhone 17, Black, 256GB","available":12}]}]}` - `GET /devices//geo`: one device across countries, for example `/devices/iphone-17/256gb/black/geo` (slashes raw or percent-encoded). `{"generatedAt":1790000000000,"device":{"id":"iphone-17/256gb/black","name":"iPhone 17, Black, 256GB"},"countries":[{"country":"US","name":"United States","stores":272,"available":12}]}` `GET /api/v1` returns an index of these endpoints. ## Errors A non-2xx answer is JSON with a `_tag` and the details: `{"_tag":"InvalidRequest","message":"postal needs country (for example country=GB)"}` - 400 `InvalidRequest` (bad or missing parameter), 404 `UnknownProduct`, `PostalCodeNotFound` or `NotFound`, 422 `PostalCodesUnsupported` or `CountryNotEnabled`, 429 `RateLimited`, 503 `PersistenceError` or `Unavailable` (retry later), 405 `MethodNotAllowed` (only GET and OPTIONS). ## Limits and caching - 60 requests per minute per IP. Over the limit: 429 with `{"_tag":"RateLimited","message":"..."}` and `retry-after: 60`. Cached answers are cheap, but do not rely on that. - `/devices`, `/stores`, `/nearby` (explicit location) are cached for 30 s, `/geo` for 300 s. `/nearby` located by IP is never cached. - Freshness: stores are polled only during opening hours (about 1 h before open until 30 min after close, store local time). Overnight `checkedAt` stops advancing; that is expected, not an outage. `stale: true` means `checkedAt` is more than 600 seconds (10 minutes) old, or the store was never read. - Availability changes at most every ~2 minutes (hot stores) to 5 minutes. Polling faster than every 60 seconds returns the same data and wastes your rate limit. ## Recipe: notify me when a device is in stock near a place User: "tell me when an iPhone 17 256GB Black is in stock near 94301." 1. `GET /devices`, pick the product whose `model`, `capacity` and `color` match; keep its `id`. 2. `GET /nearby?product=&country=US&postal=94301&radiusKm=30` (add `only=1` to receive only stores with stock). 3. Repeat every 2 minutes or more (never under 60 s). Remember each store's `availability.status` by `store.id`. 4. Alert when a store's status changes to `available` (or, with `only=1`, when a new store appears in `results`). Report the store name, address, `distanceKm`, `availability.quote` and `links`. Ignore results with `stale: true` for alerting. 5. On 429 wait `retry-after` seconds; on 5xx back off and retry. Pickup stock sells out fast: tell the user to reserve on apple.com right away. This site is not affiliated with Apple. ## Optional - Web UI: https://findmeaduo.fforr.es/