ScreenshotNeo

BlogHow-to

How to Scrape the Apple App Store

Learn which Apple APIs provide App Store data, how to authenticate and paginate safely, and why storefront scraping can violate Apple’s terms.

By the ScreenshotNeo team1 October 20268 min read

Short answer: use Apple’s documented App Store Connect API for data Apple expressly makes available. It is a REST API with JSON resources and JWT authentication. Do not assume that a headless browser, proxy pool, rotating IPs, or robots.txt interpretation makes storefront scraping permitted. Apple’s developer agreement restricts automated retrieval of Apple or licensor data except data Apple expressly provides through its services.

The right implementation depends on what you need:

  • Your own apps, versions, localizations, reviews, reports, or performance data: use the App Store Connect API.
  • Broad market intelligence, public rankings, or historical storefront snapshots: check whether Apple expressly provides the field. If it does not, obtain written permission or use a licensed data provider.
  • A visual record of a page you are authorized to capture: use a screenshot service or an approved browser workflow, while keeping that separate from extracting structured App Store data.

What Apple provides through its API

Apple documents the App Store Connect API as a REST API for automating App Store Connect. Its documented areas include app metadata, TestFlight, in-app purchases and subscriptions, customer reviews and responses, reporting, and performance data. Responses use linked JSON resources, so preserve pagination and relationship links rather than assuming one response contains everything.

App information is localized. Depending on the resource and permissions, fields can include an app identifier, platform, localized name, subtitle, description, privacy-policy URL, content rights, age rating, pricing, availability, versions, ratings, and reviews. Apple documents a maximum of 30 characters for both the app name and subtitle.

Storefront search ordering is not a stable exportable ranking. Apple’s Media Services terms say ordering considers provider metadata, customer engagement with apps and the App Store, and app popularity. Treat any ranking dataset as a separate, authorized data product rather than something guaranteed by the API.

Authorization and compliance checklist

  1. Define ownership and purpose. Write down whether you are working with apps your organization controls in App Store Connect or public storefront information.
  2. Use the narrowest supported resource. Choose an app, version, localization, review, analytics, sales, or performance resource that actually exposes the field you need.
  3. Create an API key. In your developer account, create an App Store Connect API key and retain its issuer ID, key ID, and private key securely.
  4. Sign short-lived JWTs. Apple requires JSON Web Tokens created with App Store Connect API keys. Keep the private key on a server or protected job runner; never ship it in browser code or a public repository.
  5. Audit permissions. Grant the least privilege available, rotate keys when staff or systems change, and revoke unused keys.
  6. Check the agreement. Apple’s Developer Program License Agreement restricts using robots, spiders, site-search tools, or other retrieval applications to scrape, mine, retrieve, cache, analyze, or index Apple or licensor data except data Apple expressly makes available.
  7. Stop when a field is not provided. Do not silently switch from the API to HTML scraping. Seek written permission or a licensed provider.

Set up a repeatable extraction job

1. Model the data before requesting it

Use a schema that preserves context instead of flattening everything into one table:

Field Why it matters
App identifier Stable key for joins and updates
Platform Prevents mixing iOS, iPadOS, macOS, or other platform records
Locale and storefront Names, subtitles, descriptions, pricing, and availability vary by region
Resource type and version Lets you distinguish current metadata from historical records
Retrieval timestamp Supports change detection and audit trails
Source links and pagination cursors Allows reproducible traversal of linked JSON resources

2. Store credentials as environment variables

export APPSTORE_ISSUER_ID='your-issuer-id'
export APPSTORE_KEY_ID='your-key-id'
export APPSTORE_PRIVATE_KEY_PATH='/secure/path/AuthKey_XXXXXXXXXX.p8'
export APPSTORE_API_ENDPOINT='https://api.appstoreconnect.apple.com/v1/RESOURCE'

Set APPSTORE_API_ENDPOINT to the exact resource URL and query documented by Apple for the data you need. Do not guess endpoint paths or permissions.

3. Create a JWT and call the documented resource

The following example shows the complete request shape. Install a JWT library appropriate for your language, then use the endpoint and query parameters from Apple’s current documentation.

Python

import os
import time
from pathlib import Path
import jwt
import requests

issuer_id = os.environ["APPSTORE_ISSUER_ID"]
key_id = os.environ["APPSTORE_KEY_ID"]
private_key = Path(os.environ["APPSTORE_PRIVATE_KEY_PATH"]).read_text()
endpoint = os.environ["APPSTORE_API_ENDPOINT"]

now = int(time.time())
token = jwt.encode(
    {"iss": issuer_id, "iat": now, "exp": now + 120, "aud": "appstoreconnect-v1"},
    private_key,
    algorithm="ES256",
    headers={"kid": key_id, "typ": "JWT"},
)

response = requests.get(
    endpoint,
    headers={"Authorization": f"Bearer {token}"},
    timeout=60,
)
response.raise_for_status()
payload = response.json()
print(payload)

Node.js

import fs from 'node:fs';
import jwt from 'jsonwebtoken';

const issuerId = process.env.APPSTORE_ISSUER_ID;
const keyId = process.env.APPSTORE_KEY_ID;
const privateKey = fs.readFileSync(process.env.APPSTORE_PRIVATE_KEY_PATH, 'utf8');
const endpoint = process.env.APPSTORE_API_ENDPOINT;

const now = Math.floor(Date.now() / 1000);
const token = jwt.sign(
  { iss: issuerId, iat: now, exp: now + 120, aud: 'appstoreconnect-v1' },
  privateKey,
  { algorithm: 'ES256', keyid: keyId, header: { typ: 'JWT' } }
);

const response = await fetch(endpoint, {
  headers: { Authorization: `Bearer ${token}` }
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
console.log(await response.json());

cURL

curl --fail-with-body \
  -H "Authorization: Bearer $APPSTORE_JWT" \
  "$APPSTORE_API_ENDPOINT"

Generate APPSTORE_JWT with the same issuer ID, key ID, ES256 private key, audience, and short expiration shown above. Keeping token creation separate from the request makes key handling easier to audit.

Pagination, localization, and normalization

Process every page until Apple’s response indicates there is no next page. Preserve the response’s links and pagination fields; never assume a fixed page size. For each record, retain its locale and storefront context. Normalize text only after storing the original value, because punctuation, casing, and localized wording can matter when comparing releases.

For incremental jobs, keep a checksum of the normalized fields and compare it with the previous record. Save the retrieval time and API resource identifier so a later change can be explained. If you collect reviews or responses, store the review identifier and response relationship rather than deduplicating on the review text.

Retries, rate limits, and reliability

  • Follow the current endpoint documentation for quotas and permissions; Apple’s general documentation does not establish one universal rate-limit number for every resource.
  • Retry transient network failures and server errors with exponential backoff and jitter.
  • Do not retry authentication or permission failures unchanged. Fix the issuer, key, token claims, role, or endpoint first.
  • Bound concurrency. A small worker pool is easier to monitor and less likely to trigger avoidable throttling.
  • Make writes idempotent using the Apple resource identifier plus locale and storefront.
  • Log status code, request identifier if supplied, resource, page cursor, and retry count. Never log private keys or complete bearer tokens.

What to do when the API does not contain the data

Common requests such as a complete public ranking history, every storefront field across every country, or an unrestricted review corpus may not be available through the documented developer resources. Apple’s API documentation describes supported developer-account resources; it does not promise a public historical ranking export.

At that point, choose one of these paths:

  1. Ask Apple for written authorization.
  2. Use a licensed app-intelligence provider whose terms cover your use.
  3. Reduce the scope to fields Apple expressly provides.

Do not present a browser scraper as a compliant substitute. Apple’s agreements separately restrict page-scrape and similar processes used to obtain information through means not purposely made available.

Or skip the browser setup

When you need an authorized visual capture of an App Store page or another website, ScreenshotNeo provides a single screenshot request. Its cleanup steps accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled.

Only clean shots are billed. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://apps.apple.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://apps.apple.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://apps.apple.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

Troubleshooting

Symptom Likely cause Fix
401 or 403 Expired JWT, wrong key ID, issuer, audience, or role Create a new short-lived token and verify the key’s permissions.
Signature validation error Wrong private key, algorithm, or key header Use the matching ES256 key and key ID; check file permissions and line endings.
Empty or incomplete results Wrong resource, locale, relationship, or page traversal Check the endpoint documentation and follow every pagination and relationship link.
Repeated 429 or 5xx responses Concurrency too high or transient service failure Reduce workers, add exponential backoff with jitter, and record failed cursors for replay.
Different values between runs Locale, storefront, version, or availability changed Persist locale, storefront, version, and retrieval timestamp with each record.
Need public rankings not exposed by API The desired dataset is outside documented resources Obtain permission or use a licensed provider; do not switch to unapproved scraping.

Performance and cost considerations

  • Request only the fields and relationships required by the job.
  • Use incremental synchronization and checksums instead of downloading unchanged data.
  • Cache your own normalized results with a documented retention period, while respecting Apple’s agreements and your data-retention policy.
  • Separate API usage costs, key-management work, storage, and any licensed-provider fees in your estimate.
  • For visual captures, choose the smallest viewport and output format that meets the requirement; cache repeat requests when appropriate.

FAQ

Can I scrape App Store HTML with Playwright or Selenium?

A browser can technically retrieve a page, but Apple’s agreements address robots, spiders, site-search tools, and other retrieval applications. Use the documented API or obtain permission instead.

Does App Store Connect provide every public App Store field?

No guarantee is given. Select a documented resource and confirm that it exposes the field, locale, and storefront you need.

Can I build a historical ranking database from the API?

Do not assume so. Apple describes ranking signals in its terms but does not promise a public historical ranking export in the cited API documentation.

Where should JWT private keys live?

On a server or protected job runner with restricted access. Never embed them in client-side JavaScript, mobile apps, or public repositories.

How should I handle localized metadata?

Store locale and storefront as first-class fields, retain the original text, and compare records within the same locale and storefront.

Primary sources