Shopify App Store Audit: Dead Privacy Policy Links
Pricing
from $5.00 / 1,000 results
Shopify App Store Audit: Dead Privacy Policy Links
Shopify requires every app to publish a privacy policy and never rechecks the link. This audits the links a listing declares: policies that 404, policies that redirect to a homepage, domains nobody has registered - anyone can publish there - and dead developer and documentation links.
Pricing
from $5.00 / 1,000 results
Rating
0.0
(0)
Developer
Ai-Q Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
41 minutes ago
Last modified
Categories
Share
Shopify App Store Audit
Shopify requires every app in its App Store to publish a privacy policy, and shows the link on the listing. It never revisits it.
A merchant installing an app inherits whatever that policy says, and frequently cites it in their own store's privacy notice. So the link matters long after the developer typed it in — and nothing in the store checks whether it still leads to a policy.
This Actor checks. It reads the links each listing declares, follows every one of them, and asks the domain registry about the ones that failed.
The finding this exists for
A privacy policy URL whose domain nobody has registered.
That is not a broken link. It is an address anyone can move into: register the domain, publish a document, and the Shopify App Store will keep pointing every prospective merchant at your page, under the developer's name.
Two quieter versions of the same failure sit next to it:
- The policy 404s. The requirement is unmet and the merchant has nothing to read.
- The policy redirects to the site's front page. Every checker that stops at the status
code reports a healthy
200. A reader following the link lands on a homepage. Google treats this shape as a soft 404; this Actor reports it by comparing the path asked for with the path landed on.
What comes out
One row per app, plus four key-value records:
| Record | What is in it |
|---|---|
| dataset row | the listing's facts, every declared link with its verdict, and the findings |
ACTION_LIST | findings only, worst first, ranked by review count within a severity |
PRIVACY_REPORT | the policies that are takeable, dead, or redirected to a homepage |
DEVELOPER_PROFILES | the same findings grouped by developer, so a portfolio shows up as one |
SUMMARY | counts, the query, the method, and every caveat that applies to the run |
Input
Either name the apps or point at a category.
{"apps": ["klaviyo-email-marketing", "https://apps.shopify.com/judgeme"],"categories": ["store-design-site-optimization-seo"],"pagesPerCategory": 2}
An app is identified by its handle — the last segment of the listing URL. Full URLs are accepted and reduced to the handle.
What it will not do
It does not use the store's search. apps.shopify.com/robots.txt disallows any URL
carrying q= for every user agent. Apps are discovered through category pages, which are
permitted. A search URL passed in apps is refused with that reason rather than quietly
stripped of its query. robots.txt is re-read at the start of every run, and the run stops if
the listing pages stop being allowed.
It does not claim an app is abandoned. Shopify publishes a launch date and no update date. There is no honest way to get staleness out of that, so no finding is built on it. (The sibling Actors for the App Store and Google Play do make that call, because those stores publish the date.)
It does not call a refusal a death. A link answering 401, 403 or 429 is recorded as
blocked at the lowest severity: the server is up and declining to talk to a program, which
says nothing about whether a person clicking through reaches a page. Nothing here retries
behind a browser user agent — being refused is an answer, and working around it would make
this a different kind of tool.
It does not read review text, install counts, or anything behind a login. One request per listing page, one per declared link, and one per failed domain.
Severities, and why they sit where they do
Severity is chosen after measuring how common a condition is, never before. A warning that fires on healthy input erases the rows that say "nothing to report", which is the only reason the report is worth reading.
| Finding | Severity | Why there |
|---|---|---|
privacy_policy_domain_unregistered | critical | anyone can publish a policy in the developer's name at the address Shopify still shows |
declared_domain_unregistered | critical | same, for the developer's own website |
privacy_policy_dead | high | Shopify requires a working policy; there is nothing to read or cite |
developer_site_dead | high | the listing sends readers nowhere |
privacy_policy_redirects_to_homepage | medium | a 200 that is not a policy; needs a human to confirm the page moved |
privacy_policy_not_listed | medium | every listing sampled had one, so this is a listing that dropped it or a change in the page |
declared_link_server_error | medium | 5xx can be temporary — worth a second look, not a conclusion |
resource_link_dead | low | FAQ, tutorial and documentation links are not required |
declared_link_blocked | low | the server is alive and refused us |
declared_link_insecure | low | only when http:// stays http:// — a redirect to https costs the reader nothing |
developer_site_offsite | low | ordinary after a rebrand, expected after a sale; the listing does not say which |
app_not_found | critical | the handle is not on the store — removed, never published, or mistyped |
Measured with this Actor's own code on 270 live listings in July 2026 — 86 from the top of six
categories and 184 from pages 14–30 of seven, where the smaller and older apps sit. Every
listing in both samples published a privacy policy. In the long tail, 1.1% of those
policies were dead, 0.5% sat on a domain nobody had registered, 1.6% of developer
websites were dead — and 85.9% of apps had nothing to report at all. The full set is in
SUMMARY.method.baseRates.
One thing it cannot tell you
The IANA RDAP bootstrap lists about 1,200 TLDs, and several common ones — .io, .co,
.jp, .ro — are not among them. There is no standard place to ask who holds
something.io. When a link on such a domain is dead, this Actor says it is dead and records
the registration as unknown, in the finding text as well as the field. It never assumes the
domain is safely held, and it never calls it free.
Cost and courtesy
Free to run; you pay Apify only for the compute, which is small — this fetches pages and resolves names, it does not run a browser. Requests are paced with a fixed delay and capped concurrency, because these are ordinary page loads on someone else's server.
The User-Agent names this Actor and links to its page, so anyone reading their logs can see who called.
Verification
Unit tests pin the parsing and the judgement against fixed input. A separate live check runs against the real store, real websites and real RDAP before every release, because the two sibling Actors both shipped bugs that only real data could surface — a healthy site called dead because it answered 403, and an entire TLD called unregistered because the wrong RDAP server was asked.
npm test # unitnpm run test:live # against the live store, sites and registries