Skip to main content

GET /api/devices/:serial/apps

List installed applications on a device. Supports filtering by app type.

Path Parameters

serial
string
required
Device serial number

Query Parameters

type
string
default:"user"
Filter apps by type:
  • user - User-installed apps only (default)
  • system - System apps only
  • all - All apps

Response

packages
string[]
required
Array of package names (e.g., com.example.app), sorted alphabetically
total
number
required
Total number of packages returned

Example Request

curl "http://localhost:3000/api/devices/ABC123/apps?type=user"
const serial = 'ABC123';
const type = 'user';
const response = await fetch(
  `http://localhost:3000/api/devices/${serial}/apps?type=${type}`
);
const data = await response.json();

Example Response

{
  "packages": [
    "com.android.chrome",
    "com.example.myapp",
    "com.spotify.music"
  ],
  "total": 3
}

POST /api/devices/:serial/apps/batch-info

Get lightweight information for multiple apps in a single request. All packages are processed in parallel for optimal performance.

Path Parameters

serial
string
required
Device serial number

Request Body

packages
string[]
required
Array of package names to fetch info for

Response

results
object
required
Object mapping package names to app info objects

Example Request

curl -X POST http://localhost:3000/api/devices/ABC123/apps/batch-info \
  -H "Content-Type: application/json" \
  -d '{"packages": ["com.android.chrome", "com.spotify.music"]}'
const response = await fetch(
  'http://localhost:3000/api/devices/ABC123/apps/batch-info',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      packages: ['com.android.chrome', 'com.spotify.music']
    })
  }
);
const data = await response.json();

Example Response

{
  "results": {
    "com.android.chrome": {
      "format": "APK",
      "isSplit": false,
      "sizeMB": "125.4 MB",
      "appName": "Chrome"
    },
    "com.spotify.music": {
      "format": "Split APK",
      "isSplit": true,
      "sizeMB": "87.2 MB",
      "appName": "Spotify"
    }
  }
}

GET /api/devices/:serial/apps/:package/info

Get detailed information about a specific app including version, install dates, and file paths.

Path Parameters

serial
string
required
Device serial number
package
string
required
Package name (e.g., com.example.app)

Response

pkg
string
required
Package name
appName
string
required
Human-readable app name
versionName
string
required
Version name (e.g., "1.2.3")
versionCode
string
required
Version code (numeric)
firstInstall
string
required
First install timestamp
lastUpdate
string
required
Last update timestamp
installer
string
required
Installer package name (e.g., "com.android.vending" for Play Store)
apkPaths
string[]
required
Array of APK file paths on the device
isSplit
boolean
required
Whether this is a split APK
format
string
required
"APK", "Split APK", or "N/A"
sizeMB
string
required
Total size in MB
sizeBytes
number
required
Total size in bytes

Example Request

curl http://localhost:3000/api/devices/ABC123/apps/com.android.chrome/info
const serial = 'ABC123';
const pkg = 'com.android.chrome';
const response = await fetch(
  `http://localhost:3000/api/devices/${serial}/apps/${pkg}/info`
);
const data = await response.json();

Example Response

{
  "pkg": "com.android.chrome",
  "appName": "Chrome",
  "versionName": "120.0.6099.144",
  "versionCode": "609914400",
  "firstInstall": "2024-01-15 10:23:45",
  "lastUpdate": "2024-02-20 14:30:12",
  "installer": "com.android.vending",
  "apkPaths": [
    "/data/app/~~abc123==/com.android.chrome-xyz456==/base.apk"
  ],
  "isSplit": false,
  "format": "APK",
  "sizeMB": "125.4 MB",
  "sizeBytes": 131534848
}

POST /api/devices/:serial/apps/:package/extract

Extract a single APK file from the device. This endpoint returns the APK file directly for download.
This endpoint only works for standard APK files. Split APKs will return an error - use the XAPK compilation endpoints instead.

Path Parameters

serial
string
required
Device serial number
package
string
required
Package name

Response

On success, returns the APK file with:
  • Content-Type: application/vnd.android.package-archive
  • Content-Disposition: attachment; filename="{package}.apk"
  • Binary APK file data

Error Response (Split APK)

Returns 400 if the app is a split APK:
{
  "error": "Split APK / XAPK detectado",
  "detail": "Esta app tiene 5 archivos APK separados y no puede exportarse como un único .apk.",
  "paths": [
    "/data/app/.../base.apk",
    "/data/app/.../split_config.arm64_v8a.apk",
    "/data/app/.../split_config.en.apk"
  ],
  "isSplit": true
}

Example Request

curl -O -J http://localhost:3000/api/devices/ABC123/apps/com.example.app/extract
const serial = 'ABC123';
const pkg = 'com.example.app';

const response = await fetch(
  `http://localhost:3000/api/devices/${serial}/apps/${pkg}/extract`,
  { method: 'POST' }
);

if (response.ok) {
  const blob = await response.blob();
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${pkg}.apk`;
  a.click();
}

GET /api/devices/:serial/apps/:package/compile-xapk

Compile a split APK into an XAPK file with real-time progress updates via Server-Sent Events.
Use this endpoint for apps with multiple APK files (Split APKs). Standard APKs should use the extract endpoint instead.

Path Parameters

serial
string
required
Device serial number
package
string
required
Package name

Response Format

Server-Sent Events stream with Content-Type: text/event-stream.
msg
string
Progress message
progress
number | null
Progress percentage (0-100)
done
boolean
Whether compilation is complete
ok
boolean
Success status (only when done: true)
fileReady
boolean
Whether the XAPK file is ready for download
size
number
File size in bytes (only when fileReady: true)
err
string | null
Error message if compilation failed

Progress Steps

  1. 5% - Getting APK paths from device
  2. 10% - Extracting APK files in parallel
  3. 10-60% - Pulling individual APK files
  4. 65% - Getting package information
  5. 70% - Creating manifest.json
  6. 80% - Packaging XAPK
  7. 95% - XAPK ready for download

Example Request

const serial = 'ABC123';
const pkg = 'com.spotify.music';
const eventSource = new EventSource(
  `http://localhost:3000/api/devices/${serial}/apps/${pkg}/compile-xapk`
);

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log(`${data.progress}%: ${data.msg}`);
  
  if (data.done) {
    eventSource.close();
    if (data.ok && data.fileReady) {
      // XAPK is ready - download it
      window.location.href = 
        `http://localhost:3000/api/devices/${serial}/apps/${pkg}/download-xapk`;
    } else {
      console.error('Compilation failed:', data.err);
    }
  }
};

Example Response Stream

data: {"msg":"Obteniendo rutas del APK en el dispositivo...","progress":5}

data: {"msg":"Encontrados 5 archivos APK. Extrayendo en paralelo...","progress":10}

data: {"msg":"Extraído 1/5: base.apk","progress":20}

data: {"msg":"Extraído 5/5: split_config.xxhdpi.apk","progress":60}

data: {"msg":"Obteniendo información del paquete...","progress":65}

data: {"msg":"Creando manifest.json...","progress":70}

data: {"msg":"Empaquetando XAPK...","progress":80}

data: {"msg":"XAPK listo. Descargando...","progress":95}

data: {"done":true,"ok":true,"fileReady":true,"size":91234567}

Error Response

If the app is not a split APK:
data: {"done":true,"ok":false,"xapkPath":null,"err":"Esta app no es un Split APK. Usa 'Extraer APK' en su lugar."}


GET /api/devices/:serial/apps/:package/download-xapk

Download the compiled XAPK file. Must be called after successful compilation via /compile-xapk.

Path Parameters

serial
string
required
Device serial number
package
string
required
Package name

Response

On success, returns the XAPK file with:
  • Content-Type: application/octet-stream
  • Content-Disposition: attachment; filename="{package}.xapk"
  • Binary XAPK file data
The XAPK file is automatically deleted from the server after download.

Error Response

Returns 404 if the XAPK file doesn’t exist:
{
  "error": "XAPK no encontrado. Vuelve a compilar."
}

Example Request

curl -O -J http://localhost:3000/api/devices/ABC123/apps/com.spotify.music/download-xapk
const serial = 'ABC123';
const pkg = 'com.spotify.music';

// After compilation is complete
const response = await fetch(
  `http://localhost:3000/api/devices/${serial}/apps/${pkg}/download-xapk`
);

if (response.ok) {
  const blob = await response.blob();
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${pkg}.xapk`;
  a.click();
}

XAPK Format

The XAPK file is a ZIP archive containing:
  • manifest.json - Package metadata (version, SDK levels, etc.)
  • base.apk - Base APK file
  • split_*.apk - Split APK files (architecture, language, DPI configs)
Example manifest.json:
{
  "xapk_version": 2,
  "package_name": "com.spotify.music",
  "name": "com.spotify.music",
  "version_code": "87201233",
  "version_name": "8.7.20.1233",
  "min_sdk_version": "21",
  "target_sdk_version": "34",
  "split_apks": [
    { "file": "base.apk", "id": "base" },
    { "file": "split_config.arm64_v8a.apk", "id": "split_config.arm64_v8a" },
    { "file": "split_config.en.apk", "id": "split_config.en" }
  ],
  "expansions": []
}

Build docs developers (and LLMs) love