# App Store Scraper: Developer Links & EU Availability (`aiqlabs/app-store-audit`) Actor

Audits App Store listings for what the store never rechecks. Finds developer links that lead nowhere, domains behind them that lapsed or changed hands since the app was last updated, apps absent from every EU storefront while still selling elsewhere, and listings untouched for years.

- **URL**: https://apify.com/aiqlabs/app-store-audit.md
- **Developed by:** [Ai-Q Labs](https://apify.com/aiqlabs) (community)
- **Categories:** Developer tools, Automation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## App Store Audit

The App Store checks a listing once, when it is submitted. After that the developer link, the storefronts it sells in and the year it was last touched are simply displayed, whatever happened to them since.

This Actor rechecks them.

- **Developer links that lead nowhere.** The URL shown on the listing, followed the way a browser would, reported as reachable, refused, broken or gone.
- **Domains behind those links that lapsed or changed hands.** A link that 404s is a nuisance. A link whose domain is *free to register* is an address anyone can move into while Apple keeps showing it under the developer's name.
- **Apps absent from every EU storefront while still selling elsewhere.** One lookup per storefront turns a distribution question into a fact.
- **Listings nobody has updated in years**, with the base rate attached so the number means something.

Everything comes from Apple's own public lookup and chart feeds, the links themselves, and the domain registries. No key, no login, no scraping of the store pages.

### What comes out

One row per app: the store metadata, what each check found, a list of issues with severities, and an overall `riskLevel` of `critical` / `high` / `medium` / `low` / `ok`.

Four records in the key-value store:

| Record | Contents |
|---|---|
| `SUMMARY` | Counts by severity, how many apps carried each finding, the base rates, and every caveat that applies to this run |
| `ACTION_LIST` | The findings ranked by severity, then by how many people rated the app |
| `LINK_REPORT` | Links that do not work, and the registration concerns behind them |
| `AVAILABILITY_MATRIX` | One row per app, one column per storefront |

### Input

Give it apps in whatever form you have them - numeric ids, store URLs, bundle ids - or a developer id to audit a whole catalogue, or a chart name such as `top-free` to survey what is currently selling.

```json
{
  "apps": [
    "https://apps.apple.com/us/app/example/id384357434",
    "971560709",
    "com.example.app"
  ],
  "checkAvailabilityIn": ["us", "gb", "de", "fr", "es", "it", "jp"],
  "staleAfterDays": 1095
}
```

Availability needs **at least two EU storefronts** in the list for the EU finding to mean anything - a single failed request would otherwise look like a withdrawal.

### What it will not do

**It does not read App Privacy details.** Those come from `amp-api.apps.apple.com/v1/…`, and `apps.apple.com/robots.txt` disallows `/v1/*`. Other tools take them anyway. This one does not.

**It surveys by chart, not by keyword search.** `itunes.apple.com/robots.txt` names `/search*`, so where a choice existed this Actor took the path nobody had asked it to stay off: Apple's public chart feeds on `rss.applemarketingtools.com`, whose robots.txt sets no rules at all. A chart is also the better sample for an audit — it is what people are actually installing this week, not what happens to match a word.

**It does not pretend to be a browser.** Some developer sites - `whatsapp.com` among them - answer an automated request with 403. That is a live server declining to talk to a program, and it is reported as exactly that: `developer_link_blocked`, severity `low`, "the server is up and refused an automated request; it says nothing about whether a person clicking the listing reaches a page." Retrying behind a browser user agent would produce a cleaner-looking report by working around an answer we were given.

**It does not tell you why.** Apple returns the same empty answer for an app that was removed and one that never existed. A bloc-wide EU absence is consistent with the DSA trader requirement, with a licensing decision, and with a developer who simply switched EU distribution off. The finding says what the store showed; the reason is yours to establish.

### Severities, and why they sit where they do

Measured on 1,312 live App Store apps in July 2026:

- **18.4%** had shipped no update in three years — and were still on sale. So `no_update_in_years` is **medium**, never an alarm.
- **30%** carried no developer link at all. So a missing link is **not a finding**. Only a link that is *there and broken* is.
- **16%** were registered as `http://`. Most redirect to HTTPS, which costs the reader nothing, so only a link that stays on `http://` is reported, at **low**.

A warning that fires on healthy input erases the `ok` rows, which is the only thing that makes the other rows worth reading.

| Finding | Severity |
|---|---|
| `app_not_found` — not on sale in the main storefront | critical |
| `developer_domain_unregistered` — the address on the listing is free to register | critical |
| `developer_link_dead` — hostname gone, or 404/410 | high |
| `developer_domain_reregistered` — registered after the app's last update | high |
| `unavailable_across_eu` — absent from every EU storefront checked, still selling outside | high |
| `developer_link_server_error`, `developer_link_error`, `developer_link_offsite`, `developer_domain_expiring`, `no_update_in_years` | medium |
| `developer_link_blocked`, `developer_link_insecure`, `unavailable_in_some_countries`, `link_check_failed`, `domain_check_failed` | low |

A domain whose registration could not be read is `unknown`, never `ok`. Telling somebody their vendor's domain is safely registered when we merely failed to ask is the mistake that costs them something.

### Cost and courtesy

One iTunes request covers 100 apps, one per storefront for availability, **one probe per distinct developer URL** (a developer with forty apps has one site, not forty), and a registry lookup only where it changes a verdict — behind a link that failed, or an app already past the staleness threshold. RDAP lookups run two at a time because registries operate those servers as a public service.

### Verification

39 unit tests and 27 live checks against the real API, the real sites and the real registries. Every true positive in the live file has a negative control beside it, so a rule that fires on everything cannot pass.

# Actor input Schema

## `apps` (type: `array`):

Apps to audit. Any of these work: a numeric App Store id (310633997), a store URL (https://apps.apple.com/us/app/whatsapp-messenger/id310633997), or a bundle id (net.whatsapp.WhatsApp).

## `developers` (type: `array`):

Audit every app published by these developers. Use the numeric id from a developer page URL, for example 310634000 from https://apps.apple.com/us/developer/whatsapp-inc/id310634000.

## `charts` (type: `array`):

Audit the apps currently on these public App Store charts, for surveying rather than auditing a known list. Names come from Apple's chart feeds: top-free, top-paid, top-grossing. Keyword search is deliberately not offered here because itunes.apple.com/robots.txt disallows the search endpoint.

## `country` (type: `string`):

Two-letter country code for the storefront the app details are read from. Prices, ratings and availability all differ by storefront.

## `checkAvailabilityIn` (type: `array`):

Storefronts to test each app against. An app on sale outside the EU but absent from every EU storefront checked is reported separately, so include at least two EU countries for that check to mean anything. Leave empty to skip availability entirely.

## `checkDeveloperLinks` (type: `boolean`):

Follow the developer URL shown on each listing and report where it ends up. One request per distinct URL, not per app.

## `checkDomainRegistration` (type: `boolean`):

Read the registry record (RDAP) for domains behind links that failed, and for apps past the staleness threshold. This is what distinguishes a broken page from an address anyone can now register.

## `staleAfterDays` (type: `integer`):

How long without an update before it is worth reporting. Default is 1095 days (three years). For context, 18.4% of a 1,312-app sample measured in July 2026 were past that and still on sale, so this is a medium-severity note rather than an alarm.

## `chartLimit` (type: `integer`):

How many apps to take from each chart.

## `onlyIssues` (type: `boolean`):

Drop the rows where nothing was found. The counts in SUMMARY still cover every app checked.

## `maxApps` (type: `integer`):

Upper bound on how many apps are audited in one run, after developer ids and search terms are expanded.

## `maxConcurrency` (type: `integer`):

How many developer links to check at once. RDAP lookups are deliberately capped lower than this because registries run those servers as a public service.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for the App Store API, developer links and RDAP.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/1-click-flashlight-fast-simple-and-easy/id384357434",
    "971560709",
    "510453998"
  ],
  "charts": [
    "top-free"
  ],
  "country": "us",
  "checkAvailabilityIn": [
    "us",
    "gb",
    "de",
    "fr",
    "es",
    "it",
    "jp"
  ],
  "checkDeveloperLinks": true,
  "checkDomainRegistration": true,
  "staleAfterDays": 1095,
  "chartLimit": 25,
  "onlyIssues": false,
  "maxApps": 200,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `actionList` (type: `string`):

No description

## `linkReport` (type: `string`):

No description

## `availabilityMatrix` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "apps": [
        "https://apps.apple.com/us/app/1-click-flashlight-fast-simple-and-easy/id384357434",
        "971560709",
        "510453998"
    ],
    "charts": [
        "top-free"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aiqlabs/app-store-audit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "apps": [
        "https://apps.apple.com/us/app/1-click-flashlight-fast-simple-and-easy/id384357434",
        "971560709",
        "510453998",
    ],
    "charts": ["top-free"],
}

# Run the Actor and wait for it to finish
run = client.actor("aiqlabs/app-store-audit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "apps": [
    "https://apps.apple.com/us/app/1-click-flashlight-fast-simple-and-easy/id384357434",
    "971560709",
    "510453998"
  ],
  "charts": [
    "top-free"
  ]
}' |
apify call aiqlabs/app-store-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=aiqlabs/app-store-audit",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XNRKusI1nUek2QgAC/builds/Rfwl8fJKfPCv0QhF6/openapi.json
