iOS & Android Simulators

A phone your
agent can drive.

iPhone simulators and Android emulators in the cloud, on real Apple silicon. Install a build, drive the UI, stream the session into your own product, and let an agent run the whole loop, without a Mac on anyone’s desk.

sandal ios create
$0.01per active minute
60 fpsH.264 stream
<50 mstap to frame
Apple silicondedicated
iOS + Androidone API
Capabilities

Everything a device does, as an API.

Get a device, put an app on it, drive it, read it, record what happened, and hand the raw protocol to the test suite you already own.

Create a session

Warm devices, under a second.

Devices boot from a warm pool, so create usually resolves in well under a second. Pin an OS version or take the latest. Pause when you are done and resume without losing state, paused devices cost nothing.

const dev = await sandal.devices.create({
kind: "ios-simulator", // or "android-emulator"
os: "ios-18",
});
await dev.install("./build/Store.app");
await dev.launch("dev.example.store");
await dev.openUrl("store://cart?sku=SNDL-42");
Drive the UI

Input and video share one clock.

Taps, swipes, key input, rotation, hardware buttons. Input goes in over the same channel as the video comes out, so a gesture and the frame it produced line up exactly, no guessing whether the screen you read is the one you tapped.

await dev.tap(120, 640);
await dev.swipe({ from: [200, 800], to: [200, 200] });
await dev.type("buyer@example.com");
await dev.rotate("landscape");
await dev.press("home");
Read the screen

Assert on labels, not pixels.

A PNG at full device resolution, or the native accessibility tree, the source of truth for what the UI actually shows. A failed assertion is a value your code can branch on rather than a screenshot a human has to squint at.

const png = await dev.screenshot(); // Buffer
const tree = await dev.elementTree(); // AX / UIAutomator
expect(tree.nodes.map((n) => n.label))
.toContain("Order confirmed");
Record the run

Every session leaves evidence.

Start a recording, drive the app, stop it, get a 60fps MP4 behind a signed URL. Attach it to the failed CI run instead of writing three paragraphs. Pipe a fixed video into the camera so a scan or QR flow sees the same frames every pass.

Build the app first with Xcode Builds
const rec = await dev.startRecording();
await checkoutFlow(dev);
const clip = await rec.stop();
await dev.camera.inject({ source: "./fixtures/receipt.mp4", loop: true });
console.log(clip.url, clip.durationMs); // 60fps mp4
Artifacts
.mp460 fps recording of the whole session
.pngFull-resolution screenshots on demand
tree.jsonAccessibility tree at any frame
logsDevice console and app stdout

Behind 15-minute signed links, or written straight to your own S3, GCS or R2 bucket. Kept 14 days.

Two ways to use it

One device. Two audiences.

for Agents

Programmatic control

Tap, swipe, type, screenshot, read the accessibility tree. Every action is a function call with a return value, so a failed assertion is a value your code can branch on rather than a pixel a human has to squint at.

const dev = await sandal.devices.create({ kind: "ios-simulator" });
await dev.tap(120, 640);
const tree = await dev.elementTree();
const pay = tree.find((n) => n.label === "Pay $146.20");
if (!pay) throw new Error("checkout button missing");

for People

Embed or share the session

Mount <RemoteControl /> and the live device renders inside your app, with input forwarded into the guest. Or mint a signed URL and send it to a reviewer who never installs anything.

const token = await dev.createToken({
scope: ["stream:read", "input:write"],
ttl: "10m",
});
const url = await dev.getSignedStreamUrl({ token });
// hand `url` to a browser tab, a Slack message, a PR comment

Tokens are scoped to a single instance. One lifted off the wire cannot be replayed from another client, and it carries no authority over any other device on the account.

Speed

Faster because
the hardware is faster.

Apple silicon we own, one device per VM, booted from a warm pool instead of a cold runner. Medians across public iOS apps.

Workload Test Comparison

51.3xFaster
Simulator boot, warm pool
0.8s
41s
4.6xFaster
Install + launch a 60 MB .app
2.1s
9.6s
2xFaster
XCUITest suite
5m 05s
10m 10s
Sandal GitHub macOS
Frameworks

Bring the suite you already have.

A tunnel is cheaper than a migration. Nothing here asks you to rewrite tests against a new protocol.

Appium

Point existing WebDriver capabilities at the tunnel endpoint. Same session, same selectors, same page objects.

XCUITest

Build and test in one call, results land in the .xcresult bundle next to the recording.

Espresso & ADB

Drive Android emulators over adb connect, unchanged from your local setup.

Maestro

Run flows against a device id; artifacts and recordings come back with the run.

How it works

Build, install,
drive, read.

Boot a device from the warm pool, put your build on it, drive it the way a thumb would, and read back what the screen says, then let the agent loop.

checkout.test.ts
const dev = await sandal.devices.create({ kind: "ios-simulator", os: "ios-18" });
await dev.install("./build/Store.app");
await dev.launch("dev.example.store");
await dev.tap(120, 640); // "Pay $146.20"
const tree = await dev.elementTree();
expect(tree.labels()).toContain("Order confirmed");
// pause, memory and disk stay, the meter stops
await dev.pause();
  1. 01

    Create a session

    A device from the warm pool, usually in well under a second. Pin an OS or take the latest.

  2. 02

    Install and launch

    Push a .app, .ipa or .apk, start it by bundle id, deep-link straight to the screen under test.

  3. 03

    Drive the UI

    Taps, swipes, typing, rotation, every action a function call with a return value.

  4. 04

    Read the screen

    A full-resolution PNG or the accessibility tree. Pause when you are done; paused is free.

Plans

One balance. Every machine you run.

Simulators draw from the same credit pool as your sandboxes, runners and Xcode builds, billed by the second at one published rate card.

Starter

For a solo build or a first agent in production.

$20/ month

$20 of usage credits

≈ 550 VM-hours or 33,000 CI minutes

Get started
  • 20 concurrent machines
  • Committed in-plan rates on every product
  • Linux & Windows, sandboxes, VMs, runners
  • Per-second billing, paused is free
  • Full API, SDKs, CLI & MCP server
  • Unlimited seats
  • Community support
Most popular

Team

Apple silicon unlocked, and a balance that rolls over.

$100/ month

$100 of usage credits

≈ 2,700 VM-hours or 10,000 Mac build minutes

Get started
  • 100 concurrent machines
  • Everything in Starter, plus:
  • macOS, iOS/Android simulators & Xcode builds
  • Unused credits roll over one month
  • Usage exports & per-second line items
  • Email support

Business

Higher ceilings for a team leaning on the fleet daily.

$250/ month

$250 of usage credits

≈ 6,900 VM-hours or 25,000 Mac build minutes

Get started
  • 300 concurrent machines
  • Everything in Team, plus:
  • Raised concurrency & start rate
  • Priority support & shared Slack channel
  • Single sign-on (SAML / OIDC)
  • Volume credit rate

Enterprise

Dedicated capacity, your compliance surface, one invoice.

Custom

Committed volume rate

Committed volume rate, negotiated per account

Talk to sales
  • Reserved bare-metal capacity
  • BYOC or self-host the control plane
  • SSO & SCIM, audit logs, data residency
  • Annual invoicing, MSA, DPA
  • Support SLA & solutions engineering
  • Custom regions on request

Every plan spends its credit pool first, then usage keeps flowing and bills per second at your committed in-plan rate, no hard stop mid-run, no forced upgrade. Without a plan you pay the on-demand rate instead. Prices are illustrative pending final list pricing; the per-second billing model is final.

Rate card

The simulator rate card.

Android emulator · Linux

MachineIn-plan / device-hrOn-demand / device-hr
2 vCPU / 4 GB$0.018$0.027
4 vCPU / 8 GB$0.036$0.054
8 vCPU / 16 GB$0.072$0.108

iOS simulator · Apple silicon

Per attached device, billed at $0.01 / minute.
MachineIn-plan / device-hrOn-demand / device-hr
1 device · 4 cores / 8 GB$0.60$0.90

Android emulators run on the Linux ladder. iOS simulators run on Apple-silicon hardware, which is why they price as a Mac device. On-demand is 1.5× the in-plan rate. All usage bills by the second; paused machines are not billed.

Get started

Give your AI agents somewhere to run today.

Your agents get their own machines across Linux, macOS, Windows, iOS and Xcode in milliseconds, and you stop paying for idle.

npx sandal init