Shipped

shipped.json v1

Location

https://yourdomain/shipped.json, or /.well-known/shipped.json as a fallback. Your profile lives at /b/yourdomain.

No file? Any live domain still gets a record and a badge — the domain counts as one product. An Apple developer ID works the same way for apps. The file is how you add more.

Fields

nameDisplay name. Optional, max 80 characters. Defaults to the domain.
appleApple developer IDs as strings. The number after /id on your App Store developer page. All apps under each ID are read automatically.
playAndroid package names, e.g. com.yourcompany.app. Each one is read from its Play listing.
webhttps URLs of products you say are live.
retiredProducts you say are dead: an https URL, apple:<appId>, or play:<package>.
filesPublic operating files — how you run things, what failed. Same domain as this profile, or a github.com/<o>/<r>/blob/… URL. Must serve as text/markdown or text/plain. The profile shows title, size, and date; it never mirrors the body.
githubowner/repo list. Stars, commit count, and last push are read from GitHub. Private or missing repos show as unavailable.

Invalid entries are ignored one by one; the file still loads.

What gets verified, and how

  • Apps — Apple Lookup API. Live count, names, icons, first release, last update. Storefronts checked: US, then KR.
  • Android — each package's Play listing. A 200 means live, a 404 means gone. Name, icon, and "Updated on" are read from the page. Play publishes no release date, so Android never moves since or last ship.
  • Web — we request each URL. A final 2xx means live. Anything else means down.
  • Since (web) — first capture in the Wayback Machine. Omitted when the archive is unreachable.
  • Retired — a URL must be down, an app must return zero results in every storefront. Otherwise it is shown as unverified and not counted.

What is not on the record

Followers. Self-reported revenue. Screenshots. Any date you type into the file. The file lists; third parties count.

Why so strict: across the builders we studied, self-reported revenue ran 1.6 to 5.6 times the payment-verified figure. A number we cannot check is not a smaller truth — it is a different kind of claim, and one bad one poisons every honest one next to it. So there is no field for it. Not labeled, not asterisked. Absent.

The one thing we cannot verify is omission. A product you never list never appears.

The badge

<a href="https://<site>/b/yourdomain"><img src="https://<site>/badge/yourdomain" alt="Shipped" height="24"></a>

Add ?live=1 for your live count, ?theme=dark for dark footers. The badge never shows retired. Cached for an hour at the edge; changes to the file show within an hour.

Tiers

Three, and your badge reflects yours automatically.

  • Member — the badge is on your site. That is the whole application.
  • Verified — one verified achievement: a revenue tier, a product count, or a sponsorship. Shield pill.
  • Steward — two contributions, ten seats, six-month terms, unpaid. Numbered pill; the number is permanent.

Tiers live in an operator-signed registry, not in your file. Nothing in shipped.json can set one. See the emblems

Public API

GET /api/record/<domain>
GET /api/record/apple/<developerId>

JSON, schema: "shipped/1", CORS open, cached for an hour at the edge. Same numbers as the profile — summary, tier, apps, play, web, retired, plus profile_url and badge_url. Build on it.

Limits in v1

  • Google Play is read per package name only. There is no developer-ID listing, so list every Android app you want counted.
  • Sites that block automated requests may show as down. We retry with a browser user agent on 403, 405, and 429.
  • No claim step. Whoever controls the domain controls the file.