@perttu/testflight-scraper
v1.0.0
Published
Modern TypeScript library for scraping public TestFlight invitation data
Maintainers
Readme
@perttu/testflight-scraper
Modern TypeScript library for scraping app data from public Apple TestFlight invitations.
It follows the package shape of @perttu/app-store-scraper: native fetch, a typed interface, dual ESM/CommonJS builds, injectable request behavior, deterministic unit tests, and opt-in live-network tests.
Features
- Full TypeScript types
- Accepts either a complete public invitation URL or its eight-character code
- Extracts the app title, public beta description, icon, platforms, and invite status
- Handles available, full, and closed invitation pages
- Supports timeouts, retries, cancellation, custom headers, and custom
fetch - Dual ESM/CommonJS package output
Installation
npm install @perttu/testflight-scraperNode.js 20.19 or newer is required.
Usage
import { app } from '@perttu/testflight-scraper';
const result = await app({
url: 'https://testflight.apple.com/join/kfbMFnTx',
});
console.log(result);At the time of writing, the supplied invitation returns:
{
code: 'kfbMFnTx',
url: 'https://testflight.apple.com/join/kfbMFnTx',
deepLink: 'itms-beta://testflight.apple.com/join/kfbMFnTx',
title: 'Mythika: Puzzle Tournament',
description: 'app full en beta multiplayer',
icon: 'https://is1-ssl.mzstatic.com/.../152x152ia-80.png',
platforms: ['iOS'],
status: 'available'
}The text “app full en beta multiplayer” is the developer-provided beta description; it does not mean the invitation is full. Availability is read from Apple’s invitation status area.
You can also pass only the invitation code:
const result = await app({ code: 'kfbMFnTx' });Interface
app(options)
Returns a Promise<TestFlightApp>.
interface AppOptions {
url?: string;
code?: string;
requestOptions?: RequestOptions;
}
interface TestFlightApp {
code: string;
url: string;
deepLink: string;
title: string | null;
description: string | null;
icon: string | null;
platforms: string[];
status: 'available' | 'full' | 'closed';
}Either url or code is required. URLs are validated and normalized to https://testflight.apple.com/join/{code}. If both are provided, they must reference the same invitation.
The public beta description is optional, so description may be null on an available invitation. Apple sometimes hides all app metadata after an invitation closes. In that case, status is closed while title, description, and icon are null and platforms is empty.
Request options
const result = await app({
code: 'kfbMFnTx',
requestOptions: {
timeout: 5_000,
retries: 3,
retryDelay: 500,
signal: controller.signal,
fetch: myProxiedFetch,
headers: { 'X-Custom': '1' },
},
});Retries apply to HTTP 429, HTTP 5xx, and network/timeout failures. Backoff doubles after each attempt, while Apple’s Retry-After header takes precedence.
Public data limitations
This library only reads the public invitation page. Apple does not expose private TestFlight details there, so this scraper cannot return build numbers, tester counts, developer contact details, crash data, or internal App Store Connect metadata.
TestFlight pages are controlled by Apple and can change without notice. Unrecognized status text or page markup produces an explicit parser error instead of being guessed as available.
Development
npm install
npm run test:run
npm run typecheck
npm run lint
npm run build
npm run exampleLive-network tests are skipped by default:
npm run test:networkLicense
MIT
