DEV Community

Martin Petrov
Martin Petrov

Posted on

Check your iOS app's App Store rank in 155 countries with Python (free)

If you ship an iOS app, App Store Connect tells you downloads and revenue, but not where your app sits in the top charts of each country.

That number matters more than it looks. A jump from #80 to #30 in the Travel chart in Germany usually means a feature, a review, or a competitor slipping, and you want to know the day it happens, not a month later.

I built a small free tool for this, and in this post I show both ways: the do-it-yourself script against Apple's own feed, and the shortcut.

Why the usual approach hurts

Apple publishes the top charts as a public RSS/JSON feed, one URL per country, per chart (free, paid, grossing) and per genre. That is the honest source, and you can use it yourself.

The catch is the multiplication:

  • 155 App Store countries
  • 3 charts (free, paid, grossing)
  • the overall chart plus your genre chart

That is several hundred requests for one check. At a polite 2 requests per second it takes minutes, and if you go faster Apple may answer with HTTP 429. Commercial rank trackers solve this, but they are monthly subscriptions, often more than a small indie app earns.

Option 1: check it yourself with Apple's feed (Python)

This script checks one app in 8 countries, in its genre chart (Travel here), free and grossing. It needs only requests.

import time, requests

APP_ID = "1358823008"          # your app's numeric App Store id (from its URL: .../id1358823008)
GENRE = 6003                   # Travel; 0 = all apps
COUNTRIES = ["us", "gb", "de", "fr", "jp", "au", "ca", "in"]
CHARTS = {"free": "topfreeapplications", "paid": "toppaidapplications", "grossing": "topgrossingapplications"}
UA = {"User-Agent": "my-rank-check/1.0 (you@example.com)"}

def rank(cc, chart, genre):
    g = f"/genre={genre}" if genre else ""
    url = f"https://itunes.apple.com/{cc}/rss/{CHARTS[chart]}/limit=100{g}/json"
    r = requests.get(url, headers=UA, timeout=20)
    r.raise_for_status()
    entries = r.json().get("feed", {}).get("entry", [])
    for pos, e in enumerate(entries, 1):
        if e["id"]["attributes"]["im:id"] == APP_ID:
            return pos
    return None

for cc in COUNTRIES:
    for chart in ("free", "grossing"):
        print(f"{cc.upper()} {chart:8} {rank(cc, chart, GENRE) or '-'}")
        time.sleep(0.5)          # be polite: max 2 requests per second
Enter fullscreen mode Exit fullscreen mode

I ran it today (2026-11-09) for Flighty, a flight tracker I use as an example because it is well known. Real output:

US free     75
US grossing 2
GB free     80
GB grossing 2
DE free     -
DE grossing 2
FR free     -
FR grossing 4
JP free     -
JP grossing 6
AU free     88
AU grossing 2
CA free     -
CA grossing 2
IN free     -
IN grossing 2
Enter fullscreen mode Exit fullscreen mode

- means the app is not in the top 100 of that chart. 16 requests took 23.5 seconds on my server. Scale that to 155 countries and all charts and you see the problem. Genre ids you will need: 6000 Business, 6002 Utilities, 6003 Travel, 6005 Social Networking, 6007 Productivity, 6013 Health & Fitness, 6014 Games, 6015 Finance, 6017 Education, 6024 Shopping.

Tips if you keep the DIY route:

  • Send an honest User-Agent with a contact address.
  • Keep it at 2 requests per second or less and back off on HTTP 429.
  • Store every result with the date. The feed only shows today. Yesterday's chart is gone forever if you did not save it.

Option 2: the free rank check I built

The last tip is why I built this. Since 2026-11-07 a job on my server saves the official Apple charts every day:

  • all 155 countries: top free, top paid and top grossing (overall)
  • 20 bigger countries (US, GB, DE, FR, JP, KR, CA, AU, BR, IN, IT, ES, MX, NL, SE, TR, ID, SA, PL, BG): top free and top grossing for 24 genres

That is about 1,400 charts and 46,781 different apps so far. You can search any app by name or id here, free, no signup:

Check your app's rank in every country

There is also a plain JSON endpoint, so you can use it from a script or a cron job:

curl "https://2-29-135-242.sslip.io/apprank/q?q=1358823008"
Enter fullscreen mode Exit fullscreen mode

Real response today (trimmed to the first 2 of 25 chart positions):

{
  "q": "1358823008",
  "day": "2026-11-09",
  "apps": [
    {
      "id": 1358823008,
      "name": "Flighty – Live Flight Tracker",
      "artist": "Flighty LLC",
      "genre": "Travel",
      "n": 25,
      "best": {"cc": "IN", "chart": "grossing", "genre": "Travel", "rank": 1},
      "ranks": [
        {"cc": "IN", "chart": "grossing", "genre": "Travel", "rank": 1},
        {"cc": "AU", "chart": "grossing", "genre": "Travel", "rank": 2}
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

One call returns every chart where the app appears, across all stored countries. The numbers match the DIY script above (US Travel grossing #2), because both read the same Apple feed. The difference is that the 1,400 requests already happened at night.

The same in Python, with a small table:

import requests

r = requests.get("https://2-29-135-242.sslip.io/apprank/q",
                 params={"q": "1358823008"}, timeout=30).json()
for app in r["apps"]:
    print(app["name"], "- in", app["n"], "charts on", r["day"])
    for x in sorted(app["ranks"], key=lambda x: x["rank"])[:10]:
        print(f'  {x["cc"]}  {x["chart"]:8}  {x["genre"]:12}  #{x["rank"]}')
Enter fullscreen mode Exit fullscreen mode

You can also search by name (q=flighty); it returns up to 5 matching apps.

Run it daily

To get a history of your own app, call the endpoint once a day and append to a CSV:

import csv, datetime, requests

APP = "1358823008"
r = requests.get("https://2-29-135-242.sslip.io/apprank/q", params={"q": APP}, timeout=30).json()
with open("ranks.csv", "a", newline="") as f:
    w = csv.writer(f)
    for x in r["apps"][0]["ranks"]:
        w.writerow([r["day"], x["cc"], x["chart"], x["genre"], x["rank"]])
Enter fullscreen mode Exit fullscreen mode
# crontab -e: every day at 06:00 (the charts are saved around 01:30 UTC)
0 6 * * * cd /home/me/ranks && python3 daily.py
Enter fullscreen mode Exit fullscreen mode

Honest limits

  • It only knows the top 100 of each chart. If your app is #350 in its genre, it will show nothing. That is a limit of Apple's public feed, not something I can fix.
  • Genre charts are saved for 20 countries only. The other 135 countries have the overall free, paid and grossing charts.
  • History starts on 2026-11-07, so today there are only a few days.
  • iPhone App Store only, no Google Play, no keyword rankings.
  • The endpoint is rate-limited (30 searches per 10 minutes per IP). It is fine for your own apps, not for bulk scraping.
  • It is a one-person project on one server. If it is down, the DIY script above still works.

Price, and a question for you

The rank check and the JSON endpoint are free.

I am deciding whether to build a proper tracker on top of the archive: daily email when your app enters, leaves or moves in any country, plus charts of the history. It would be $9 per month, and the first 3 months free for people on the waitlist. I only build it if enough developers actually want it, so there is a waitlist box on the same page: apprank.

If you track ranks today, what do you use and what is missing? I read every comment.

Top comments (0)