# Apple App Store charts > Daily snapshots of Apple App Store charts (top free / paid / grossing / new) per storefront and genre, taken from the public iTunes RSS feed generator, plus a dimension table describing every app that has appeared in one. - Dataset id: `itunes-charts` - Update cadence: daily - Upstream source: https://itunes.apple.com/us/rss/ - Tags: apps, mobile, apple, ios, charts, rankings, app-store - Current version: ingest `01M0NJGCQQ2DKP82KNNEPYSP2J`, snapshot 2026-08-22, generated 2026-08-22T20:27:37.352Z ## Licence and attribution - Licence: Apple Media Services Terms (`proprietary`) - Licence text: https://www.apple.com/legal/internet-services/itunes/ - Required attribution: Data from the Apple iTunes RSS Feed Generator - Notes: Apple publishes this feed openly but grants no explicit redistribution licence. What we publish is a derived work: chart positions plus the descriptive fields the feed itself exposes — no app binaries, no artwork copies (icon URLs point at Apple’s CDN), no reviews. Reusers must keep the attribution, must not present the data as an Apple product, and are responsible for their own compliance with the Apple Media Services Terms of Service. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - This dataset: https://data-directory.fsn1.your-objectstorage.com/public/itunes-charts/manifest.json - All datasets: https://data-directory.fsn1.your-objectstorage.com/public/index.json - Human page: https://data.bunnytech.app/datasets/itunes-charts The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE VIEW rankings AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/rankings.parquet'); CREATE OR REPLACE VIEW apps AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/apps.parquet'); ``` Example queries: -- Sample rankings: Reads the first rows in published order (snapshot_date, country, feed_type, genre_id, rank), which is also the order the parquet file is physically sorted in. ```sql SELECT * FROM rankings LIMIT 10; ``` -- Sample apps: Reads the first rows in published order (name_lc), which is also the order the parquet file is physically sorted in. ```sql SELECT * FROM apps LIMIT 10; ``` ## Live lake — DuckLake catalog with time travel Attach the published catalog read-only over plain https. This works in native DuckDB AND in duckdb-wasm in the browser — the reader catalog resolves its data files through public https URLs, so no credentials and no backend are involved. ```sql INSTALL ducklake; LOAD ducklake; ATTACH 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/itunes-charts/catalog.ducklake' AS lake (READ_ONLY); SELECT * FROM lake.main.rankings LIMIT 5; USE lake; ``` - Latest catalog: https://data-directory.fsn1.your-objectstorage.com/catalog/itunes-charts/catalog.ducklake - This exact version: https://data-directory.fsn1.your-objectstorage.com/catalog/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/catalog.ducklake (sha256 aff7130bf0ce1c55c0706f74c663fe0f1c5d38b131f523ae4bca6a54c4c08c8c) - Written by DuckDB v1.5.5 - Time travel: this ingest is snapshot 4 — e.g. `SELECT * FROM lake.rankings AT (VERSION => 4)` ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://data-directory/public/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/*.parquet'); ``` ## Schema ### rankings One row per app per chart position per day. A full daily snapshot of an Apple App Store chart; re-running a day rewrites exactly that day. - 99 rows · 2.5 KB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `country`, `feed_type`, `genre_id`, `rank` - Primary key: `snapshot_date`, `country`, `feed_type`, `genre_id`, `rank` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/rankings.parquet (99 rows) Columns: - `snapshot_date` DATE NOT NULL — The chart day (UTC) this snapshot was captured for. (range: "2026-08-22" … "2026-08-22") (~1 distinct) - `country` VARCHAR NOT NULL — Two-letter storefront code the chart was read from. (range: "us" … "us") (~1 distinct) - `feed_type` VARCHAR NOT NULL — Which chart: topfreeapplications, toppaidapplications, … (range: "topfreeapplications" … "topfreeapplications") (~1 distinct) - `genre_id` INTEGER NOT NULL — Apple genre id the chart was scoped to; 0 means the all-genres chart. Part of the snapshot key, so it is never NULL. (range: 6007 … 6007) (~1 distinct) - `rank` INTEGER NOT NULL — Chart position, 1-based, in the order Apple returned it. (range: 1 … 99) (~96 distinct) - `app_id` BIGINT NOT NULL — Apple track id of the ranked app; joins to apps.app_id. - `price_amount` DECIMAL(12,4) NOT NULL [currency of price_currency] — Listed price on the snapshot day, from the feed price attribute. - `price_currency` VARCHAR NOT NULL — ISO 4217 currency of price_amount. - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ### apps One row per app ever seen in a chart, with the descriptive fields from the feed. Upserted: first_seen_date/last_seen_date widen over time, everything else reflects the latest sighting. - 99 rows · 110 KB · write mode `upsert` - Physically sorted by: `name_lc` - Primary key: `app_id` - Text search columns: `name`, `artist_name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/itunes-charts/v/01M0NJGCQQ2DKP82KNNEPYSP2J/apps.parquet (99 rows) Columns: - `app_id` BIGINT NOT NULL — Apple track id. Stable primary key of the dimension. (~99 distinct) - `bundle_id` VARCHAR — Reverse-DNS bundle identifier, e.g. com.openai.chat. - `name` VARCHAR NOT NULL — App name as shown in the store. - `name_lc` VARCHAR NOT NULL — name, lowercased and accent-folded. The sort key and the prefix-search column. (range: "ai chat assistant – chatnow" … "zeely: ai marketing platform") - `artist_id` BIGINT — Apple developer id, parsed from the artist URL. NULL when absent. - `artist_name` VARCHAR NOT NULL — Developer name as published in the feed. - `artist_lc` VARCHAR NOT NULL — artist_name, lowercased and accent-folded, for search. - `artist_url` VARCHAR — Store URL of the developer page. - `summary` TEXT — The app description text from the feed. - `rights` VARCHAR — Copyright line published with the app. - `content_type` VARCHAR — Feed content type, e.g. "Application". - `category_id` INTEGER — Apple genre id of the app’s primary category. - `category_name` VARCHAR — Human name of the primary category. - `release_date` TIMESTAMPTZ — Release (or latest update) timestamp reported by the feed. - `icon_url_53` VARCHAR — 53×53 icon URL, picked by the image height attribute. - `icon_url_75` VARCHAR — 75×75 icon URL, picked by the image height attribute. - `icon_url_100` VARCHAR — 100×100 icon URL, picked by the image height attribute. - `app_url` VARCHAR — Canonical App Store URL of the app. - `first_seen_date` DATE NOT NULL — First chart day this app was seen in any chart we ingest. - `last_seen_date` DATE NOT NULL — Most recent chart day this app was seen. ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `rankings` prunes on `snapshot_date` → `country` → `feed_type` → `genre_id` → `rank`. - `apps` prunes on `name_lc`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://data.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://itunes.apple.com/us/rss/) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.