app-store-price: how the App Store price tracker compares regions
App Store 价格查询 - 查询应用在不同地区的价格和内购信息
At a glance
- What is it?
- app-store-price is a Spring Boot web application that looks up App Store app and in-app purchase prices across a set of storefront regions, ranks popular apps, and caches what it fetches. It installs with one Docker run, and its popularity counters live only in a single process memory.
- Who is it for?
- Take app-store-price if you need a quick read on how one app is priced in a handful of storefronts, or if you want the in-app purchase prices behind a paywall, and you are content with a self hosted single container. Pass on it when the numbers have to be auditable, when counts must survive a restart, or when your region is not among the ones the service covers.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly HTML, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Price comparison across storefront regions, not a single country
The service exists to answer one narrow question: what does this app cost in a country other than the one you are in. The project description calls it a tool for querying and comparing app prices in different regions, and the feature list spells out what a row of the result contains. Each app is resolved into a name, a developer, and a price for every storefront that returns a hit, and the same lookup also covers in-app purchase items.
The regions named in the documentation are the United States, China, Taiwan, Hong Kong, Japan, South Korea, Turkey, Nigeria, India, Pakistan, and Brazil, and the caching section refers to 13 regions in total. Ratings and the app description are pulled in alongside the price.
The intended reader is anyone who has to decide whether a regional price difference makes a purchase worthwhile, or a developer checking how an app is priced across markets. It is a lookup tool with a web front end, not a pricing strategy system. Nothing in it lets you set prices, and nothing in it talks to a store account.
One caveat is stated up front in the notes: exchange rates are converted in real time, so the figure you get can differ slightly from what the App Store itself shows.
One Docker command is the recommended install
Deployment is documented Docker first, and the container image is published on GitHub Container Registry under the project path with a floating latest tag. The service listens on 8080, and the port is mapped straight through to the host, so the application is reachable as soon as the container starts.
docker run -d -p 8080:8080 ghcr.io/hypooo/app-store-price:latestA Compose file is included in the repository as the second option. It builds from the local Dockerfile, names the image app-store-price:latest, sets a container name, restarts the service unless it is stopped explicitly, and publishes the same port mapping.
services:
app-store-price:
build: .
image: app-store-price:latest
container_name: app-store-price
restart: unless-stopped
ports:
- "8080:8080"Running it with Compose is a single command.
docker compose up -dAfter either route, the entry point is the same address.
http://localhost:8080The image is built on amazoncorretto:21-alpine, which matches the Java 21 baseline the project targets, and the timezone is pinned to Asia/Shanghai through environment variables set in the Dockerfile.
Building from source with Maven and running the jar
The local workflow is the usual Spring Boot sequence of clone, package, run. Maven is the build tool, and the artifact is a single executable jar under the target directory. The version in the jar name is a placeholder in the documented command, so the actual filename has to match whatever the current pom.xml produces.
git clone https://github.com/hypooo/app-store-price.git
cd app-store-pricemvn clean packagejava -jar target/app-store-price-x.x.x.jarThe framework is Spring Boot 4 with the web starter, the validation starter for parameter checking, Lombok for boilerplate reduction, Hutool as the general purpose utility library and cache implementation, Fastjson2 for JSON handling, and Jsoup with JsoupXpath for HTML document and XPath parsing. The front end is plain HTML5, CSS3 and JavaScript with a responsive layout, which is why the primary language recorded for the repository is HTML rather than Java.
That choice of dependencies says something about the mechanism. Fastjson2 and Jsoup together point to a service that reads JSON endpoints and parses HTML pages from the store, and the JsoupXpath addition points to element selection by XPath rather than by CSS selector.
Where the data comes from and why that is the weak point
The project does not run its own store database. The notes are direct about the dependency: the application relies on the public App Store API and on web page parsing, and if Apple changes either the API or the page structure then the parsing logic needs a matching update. That is the single most important fact for anyone planning to depend on the numbers.
The caching layer, built on the Hutool cache library, exists to limit how often those upstream calls happen. The application list is cached for one day and the application detail is cached for one day, and a fine grained locking mechanism is used around the cache to guard against cache penetration and concurrency problems. A one day window means a pricing change upstream is not visible immediately, which is a real constraint for anyone chasing a launch discount.
The counterpoint is that the data is at least cheap to refresh on demand for a detail page, since a detail request that misses the cache still reaches upstream. The design is coherent for a read heavy, low stakes lookup service. It is not designed for freshness guarantees, and it does not offer any scheduler, webhook, or export of a price history, because the documentation describes no such feature.
Popularity counts ride on a browser fingerprint header
The front page ranks the ten most queried apps and games, and those counts come from the same clicks that open a detail page. The mechanism is worth understanding because it has a visible failure mode.
The front end loads FingerprintJS to obtain a visitorId and sends it in an X-Fingerprint request header on the detail request. Counting happens at the entry point of the external detail request, deduplicated on appId plus fingerprint and accumulated atomically. The underlying detail fetch method only reads data, so a price comparison that internally calls the detail method does not double count.
The edge cases are spelled out and they are sensible. A missing or blank header still returns the detail but counts nothing. If the fingerprint script fails or takes longer than three seconds, the front end continues the query without the header, so the lookup still works and the count is simply lost. A detail that comes from the cache but carries a new fingerprint still counts, and a failed lookup never counts.
Deduplication records have no expiry and live alongside the totals until the process ends, so one visitor counts once per application and repeated clicks, retries, and page refreshes never inflate a total.
Rankings and deduplication records die with the process
The counters are kept in a single instance in-memory store, consistent with the existing cache design, and that has a direct consequence for how you read the numbers. A service restart clears the rankings and the deduplication records, and separate instances never share their statistics. Scaling the container horizontally therefore produces several unrelated leaderboards rather than one shared one.
The ranking copy is managed carefully. Names and blurbs for the top ten are reused from the data returned by successful detail lookups, and they are stored per application and per region so a lookup in one region cannot overwrite the text for another. When a single region lookup fails, the previously successful text is kept. When the selected region has no record at all, the fallback prefers the US region and then follows a fixed region order.
Only the current top ten per region keep their copy, which is at most 130 entries across 13 regions. An application that drops out of the ranking has its copy cleared, and if it comes back it is rebuilt from the next successful detail lookup, while its cumulative click count and basic information are kept. Returning to the ranking reads only from memory, so browsing the leaderboard costs no upstream calls at all.
Browser history is treated as part of the interface
The home page and search interactions are specified in unusual detail, which suggests the author found the behaviour worth fixing rather than leaving to the framework. On first load the search box is empty and the list shows a popular ranking with apps and games merged into one order. The ranking is site wide and consistent, and the application names and blurbs shown depend on the selected region; changing region on the home page refreshes that text.
Typing replaces the popular search suggestions, and submitting a search switches the list to results. Clicking an application enters the global price comparison, and going back restores the list you came from, whether that was the ranking or a set of search results. The mouse back button and the browser forward and back buttons behave the same way, so a detail page returns to its source list and a search result can return to the ranking. The page level back button follows browser history too, keeping search results on the way back and refetching prices when moving forward into a detail page again.
Clearing the search restores and refreshes the popular list, and a search with no matches shows a dedicated empty result state. The ranking list distinguishes loading, load failure, and no records yet, and none of those states interfere with an active search.
Recent releases, MIT licence, and the upgrade path
The project is not archived and the last push was on 2026-10-14, so commits are landing regularly. The release tags show a similar cadence, with 1.4.0 and 1.3.9 both published on 2026-10-05 and 1.3.8 on 2026-10-01. The pattern of a patch release followed by a minor release on the same day suggests the author ships the fix first and then bumps the feature version.
The licence is MIT, and a LICENSE file sits at the top level of the repository. That is a permissive licence with no copyleft obligation, so embedding the service inside a commercial product raises fewer questions than a reciprocal licence would, though the licence text itself is the authority on what you must do.
On the upgrade side, the practical cost is low in one sense and less predictable in another. The container is a single jar behind a stable port, and the Compose file pins nothing but the local build, so an upgrade means rebuilding the image against the new tag. The less predictable part is upstream data drift, since a change in the Apple page structure can break the parsing layer at any time without a version bump. The documentation offers no changelog, no migration notes, and no compatibility matrix for the Java 21 and Spring Boot 4 baseline, so treat a version bump as something to verify against a known working query rather than as a routine patch.
Editorial conclusion
Take app-store-price if you need a quick read on how one app is priced in a handful of storefronts, or if you want the in-app purchase prices behind a paywall, and you are content with a self hosted single container. Pass on it when the numbers have to be auditable, when counts must survive a restart, or when your region is not among the ones the service covers. Verify first that the region you care about appears in the storefront list, that the price you see is the converted value rather than the raw local one, and that the front page ranking resets when the service restarts.
Frequently asked questions
How do I run the app-store-price service locally?
Clone the repository, build with mvn clean package, then start the jar with java -jar target/app-store-price-x.x.x.jar and open http://localhost:8080. You need Java 21 and Maven, and the documented container route uses a JDK 21 Alpine base image.
Does app-store-price show in-app purchase prices?
Yes. Alongside the app name, developer and price for each region, the service can look up the prices of in-app purchase items, and it also returns the rating and description of the app.
How long are app-store-price results cached?
The application list and the application detail are each cached for one day, using the Hutool cache library with a fine grained locking mechanism to guard against cache penetration and concurrency problems. A price change upstream therefore does not appear the moment it happens.
Where is the popular app ranking stored in app-store-price?
In a single instance in-memory store, shared with the existing cache design. Rankings and the deduplication records are cleared when the service restarts and are never shared between instances, so running several replicas gives you several separate rankings.
Is the app-store-price price converted to my own currency?
Yes, and the documentation warns that the exchange rate is converted in real time, so the figure you see can differ slightly from the price the App Store displays for the same region.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hypooo-app-store-price)Was this useful?