Documentation
¶
Overview ¶
Package appstore looks up an App Store app's public listing: its name, version, bundle identifier, store URL, description, icon and price. It is the metadata an MDM administrator otherwise copies by hand into a Mac or mobile device App Store app record.
The upstream is Apple's unauthenticated content-metadata lookup for device management, not a Jamf service, so this package shares nothing with jamfplatform's transport: no credentials, no scope header, no retries. It is standard library only and carries no consumer's vocabulary, so a Terraform provider and a command-line tool can both build on it. Apple documents the endpoint at https://developer.apple.com/documentation/devicemanagement/getting-app-and-book-information-legacy and has published an authenticated successor that needs an Apple Developer token; Query and App name no endpoint parameter, so a token-based source can be added behind the same types.
Wire behaviour ¶
Probed 2026-10-16 (unauthenticated, US and GB storefronts). Each rule below shapes the code.
- The answer is always 200, for a bad id, a wrong storefront, the wrong platform or an unauthenticated custom app alike. A miss is an empty results object, so Lookup turns it into a *NotFoundError rather than returning an empty App. It is not transient and is not retried.
- The id parameter takes a comma list and bundleId is also a lookup key. A bundle identifier is matched case-insensitively and the result is keyed by the request's spelling, while the App carries Apple's spelling. A caller that stores the bundle identifier verbatim should compare the two.
- platform=macappstore filters out iOS-only apps and returns the Mac flavour of a universal app: a different icon, description, release notes, offer and requirements string. Every other value returns the iPhone and iPad flavour. MinimumOSVersion and Kind do not switch with the flavour (Pages' Mac flavour reports kind iosSoftware and its iOS minimum), so treat both as properties of the listing rather than of the platform asked for.
- An unrecognised country code silently falls back to the US storefront. Lookup compares the storefront that answered with the one requested and returns a *StorefrontMismatchError, checked before emptiness because a real id under a bad code comes back populated.
- The language is negotiated (GB with fr answers en-gb), and URL gains the storefront path, so the URL differs per country and language.
- Apple has merged some Mac apps into their iOS listing: the old Mac Pages id 409201541 is empty everywhere and Pages is 361309726 on both platforms.
- The version is the first offer's version.display. volumestore adds a second education offer carrying a quantity; offers with a quantity are skipped. Free means a price of zero.
- A listing carries two icon images. artwork (template token {w}x{h}bb.{f}) is, for the iPhone and iPad flavour, the raw full-bleed square, opaque and unmasked; for the Mac flavour it is already shaped with transparency. iconArtwork (token {w}x{h}{c}.{f}) is the same art masked to Apple's rounded square with transparent corners on iPhone and iPad, matches artwork on Mac, and is absent from some Mac listings (Slack for Desktop 803453959, Spark Classic 1176895641). Both were objects in every listing probed; Apple's documentation also shows an array form, unprobed, so both decode. Inspected across 13 apps, 2026-10-16.
- Responses carry cache-control: max-age=900. Bursts of 400 requests at 44 per second drew no 429 and no latency growth, but the limit is undocumented, so callers should cap their own concurrency.
- Not probed: the itvt sToken cookie that exposes custom (B2B) apps.
Index ¶
Constants ¶
const DefaultCountry = "US"
DefaultCountry is the storefront Lookup uses when a Query names none.
const DefaultEndpoint = "https://uclient-api.itunes.apple.com/WebObjects/MZStorePlatform.woa/wa/lookup"
DefaultEndpoint is Apple's content-metadata lookup URL.
Variables ¶
var ErrArtworkTemplate = errors.New("appstore: artwork URL template has an unexpected shape")
ErrArtworkTemplate is wrapped when a template lacks its expected token, so a changed template fails instead of yielding a URL with the placeholders left in.
var ErrInvalidQuery = errors.New("appstore: invalid query")
ErrInvalidQuery is wrapped by every error Lookup returns for a Query it refuses to send.
var ErrNoArtwork = errors.New("appstore: listing has no artwork of that kind")
ErrNoArtwork is returned by ArtworkURL and IconArtworkURL when the listing carries no image of that kind. For IconArtworkURL that is an ordinary answer: some Mac listings (Slack for Desktop, 803453959) carry no iconArtwork at all.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
ID string
BundleID string
Name string
// Version is the first offer's display version. Empty when the listing has no offer.
Version string
// URL is the listing's store URL for the storefront and language that answered.
URL string
Description string
// ArtworkURLTemplate is the artwork URL with Apple's {w}x{h}bb.{f} placeholder left in. Empty
// when the listing carries no artwork. Fill it with ArtworkURL.
ArtworkURLTemplate string
// IconArtworkURLTemplate is the icon artwork URL with Apple's {w}x{h}{c}.{f} placeholder left
// in. Empty when the listing carries none. Fill it with IconArtworkURL.
IconArtworkURLTemplate string
Free bool
Price float64
PriceFormatted string
Genres []string
// DeviceBasedLicensing reports whether licences for the app can be assigned to devices
// rather than to users.
DeviceBasedLicensing bool
Developer string
ReleaseNotes string
// MinimumOSVersion does not follow Platform; see the package documentation.
MinimumOSVersion string
DeviceFamilies []string
// Kind does not follow Platform; see the package documentation.
Kind string
// Country is the storefront that answered, upper case.
Country string
// Language is the negotiated language tag.
Language string
}
App is one listing as the storefront that answered describes it.
func (*App) ArtworkURL ¶
ArtworkURL fills the artwork template at the given pixel size and image format ("png", "jpg").
Apple's image service resizes and transcodes on request: 512x512 and 1024x1024 PNG and 512x512 JPEG were each served at exactly the size asked for (2026-10-16). For the iPhone and iPad flavour this is the raw full-bleed square, opaque and unmasked; for the Mac flavour it is the shaped icon with transparency. See IconArtworkURL for the masked iPhone and iPad icon.
func (*App) IconArtworkURL ¶
IconArtworkURL fills the icon artwork template at the given pixel size and image format.
For the iPhone and iPad flavour this is the same art as ArtworkURL masked to Apple's rounded square with transparent corners, as a device shows it. For the Mac flavour it matches ArtworkURL. It returns ErrNoArtwork when the listing carries none, which some Mac listings do.
The image behind it is less reliably served than ArtworkURL's: for some listings the template names a layered source that Apple's image service refused with 403 from GitHub-hosted runners while serving it from other networks (2026-10-16). Prefer ArtworkURL where either will do.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client performs lookups. The zero value is not usable; build one with New.
type Logger ¶
type Logger interface {
LogRequest(ctx context.Context, method, url string, body []byte)
LogResponse(ctx context.Context, statusCode int, headers http.Header, body []byte)
}
Logger receives each request and response. Its method set matches jamfplatform.Logger, so one value can serve both packages.
type NotFoundError ¶
type NotFoundError struct {
Query Query
}
NotFoundError reports that the storefront answered with no listing for the query. It is a definitive answer, not a transient failure.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithEndpoint ¶
WithEndpoint replaces DefaultEndpoint, for a test server or a proxy.
func WithHTTPClient ¶
WithHTTPClient replaces the HTTP client. The default has no Timeout, so the context passed to Lookup is the request deadline, and it honours proxy environment variables.
func WithLogger ¶
WithLogger sets a logger for each request and response.
type Platform ¶
type Platform = string
Platform selects which flavour of a listing Lookup asks for. Its values are the wire values. It is a type alias, matching the generated enums in jamfplatform/ (see docs/STYLE.md §Enum constants), so the constants assign to a plain string with no cast.
func PlatformValues ¶
func PlatformValues() []Platform
PlatformValues returns every Platform Lookup accepts.
type Query ¶
type Query struct {
// ID is the numeric App Store id, the digits after "id" in a store URL.
ID string
// BundleID is the app's bundle identifier, matched case-insensitively.
BundleID string
// Platform is required.
Platform Platform
// Country is a two-letter storefront code, either case. Empty means DefaultCountry.
Country string
// Language is an optional language tag. Apple negotiates it against the storefront.
Language string
}
Query names one app to look up. Exactly one of ID and BundleID must be set.
type StatusError ¶
StatusError reports a non-200 response.
type StoreURL ¶
type StoreURL struct {
// ID is the numeric App Store id.
ID string
// Country is the storefront code from the URL path, upper case. Empty when the URL names none,
// as https://apps.apple.com/app/id361309726 does.
Country string
}
StoreURL is what a store URL says about an app.
func ParseStoreURL ¶
ParseStoreURL reads the id and storefront out of an App Store URL such as https://apps.apple.com/gb/app/pages-create-documents/id361309726. The query string (?mt=12, ?l=fr) is ignored. It wraps ErrInvalidQuery when the URL is not an App Store app URL.
type StorefrontMismatchError ¶
StorefrontMismatchError reports that a storefront other than the requested one answered, which is how Apple treats a country code it does not recognise.
func (*StorefrontMismatchError) Error ¶
func (e *StorefrontMismatchError) Error() string
Error implements error.