Skip to main content

Invi Labs Python SDK

Automate real iPhones through the Invi Labs API using your API key and phone ID. Non-jailbroken iPhones require Invi Dongle.

Install

python -m pip install invilabs

Requires Python 3.10 or newer. No third-party runtime dependencies.

Connect and run an action

Set INVI_API_KEY and INVI_PHONE_ID in your environment. Keep your API key private.

import os
from invilabs import Client

client = Client(os.environ["INVI_API_KEY"])
phone = client.phone(os.environ["INVI_PHONE_ID"])

print(phone.details())
phone.tap(100, 200)
phone.save_screenshot("screen.png")

Coordinates use full-resolution screenshot pixels. Calls block until completion; the SDK waits and retries when the phone reports device_busy. A definite device_offline response waits for reconnect with delays of 1, 2, and 3 seconds, up to three retries (six seconds total by default). Each offline rejection uses a new operation key because the phone received no input. Uncertain execution is never automatically repeated; use the operation key to look up its status. Set Client(..., offline_retries=0) to disable reconnect waiting, or choose an integer from 0 to 30 for longer outages.

AI generation

result = client.generate(
    "Generate an email address using example.com",
    result_type="email",
)
print(result["data"]["text"])
print(client.balance())

Generation supports text, usernames and email address strings, optionally using a local PNG/JPEG image. It does not create mailboxes. OCR, AI prompts and generation use your shared Account AI balance. No AI-provider key is required.

Features

  • Tap, swipe, scroll, type, explicit key presses and opening installed apps.
  • Screenshots as PNG bytes or saved to your computer.
  • Grouped OCR with text and bounding boxes.
  • Single-action AI prompts and structured screen answers.
  • Image/video import through Invi Helper on supported dongle phones.
  • Device details, balance and operation recovery.

All network operations use the public HTTPS API with your API key. The package contains client code and response contracts; phone control and AI processing run on Invi Labs services. API keys remain on your computer and are sent only as an authentication header to the configured API. HTTP redirects are not followed.

This is an initial release. Device support and feature verification limits are listed in the release notes.

Documentation · Quickstart · Generation and outputs · Errors and recovery

Typing completion

Version 0.1.1 waits for the dongle's final typing acknowledgment. Long typing and AI actions remain synchronous to Python while the SDK waits for the same server operation to finish. It never starts a second copy while waiting. Call phone.press("enter") after phone.type(text) returns to submit a search. A lost connection can still produce ExecutionUncertainError; inspect the recorded operation and phone state before deciding on another action.

Tap text with OCR (0.1.2)

result = phone.tap_text("Add a comment")
result = phone.tap_text(r"^(?:add|write) (?:a )?comment[.…]*$", regex=True)
print(result["point"], result["ocr"]["credits_charged"])

Matches the full trimmed OCR block case-insensitively by default; regex uses re.search. The tap lands randomly inside an inset of the block's bounds. Use randomize=False for its center. Multiple matches raise AmbiguousTextError unless a zero-based occurrence is supplied; no match raises TargetNotFoundError. Neither error taps. Regex substring matches use the whole block's box. Keep the screen stable between OCR and tap: these are two operations.

Returns match (OCR block), point (x, y pixels), ocr (blocks, credit charge, balance), and tap (execution status and coordinates). Only the normal OCR credit charge applies, including when no match is found. A tap failure retains the OCR result in error.details["ocr"]. This helper makes no VLM request.

Check text presence with OCR (0.1.2)

check = phone.text_exists("Continue", region="middle")
if check["exists"]:
    print(check["matches"])

check = phone.text_exists(r"^(?:add|write) (?:a )?comment[.…]*$", regex=True, region="bottom")
check = phone.text_exists("Comments", region=(0, 900, 1170, 600))

Omit region for the full screen. Named regions are full-width horizontal thirds; custom tuples are (x,y,width,height) in original screenshot pixels. A block's center determines region membership. Literal and regex rules match tap_text. Returns exists (bool), matches (OCR blocks), region (resolved pixel box or None), and ocr (full result with charge and balance). Check result["exists"], not the dictionary's truthiness. No match is False; OCR failures raise errors. One billed full-screen OCR request is made regardless of region or match count. Named regions also read dimensions before and after OCR and reject geometry changes. This method never taps or calls a VLM.

Jailbroken-only extensions (0.1.5)

The phone.jailbroken namespace is available only for jailbroken iPhones with our current daemon, UI tweak and container service. Dongle phones reject these operations; their existing controls and OCR helpers are unchanged.

jb = phone.jailbroken
print(jb.apps())
print(jb.containers.list("com.example.app"))
created = jb.containers.create("com.example.app", "testing")
jb.open_app("com.example.app", cid=created["cid"])
print(jb.containers.active("com.example.app"))
jb.containers.set_active("com.example.app", "default")
jb.containers.delete("com.example.app", created["cid"])
print(jb.ui_dump())
jb.tap_text("Settings")

Containers use the account's existing slot pool and appear in the panel. Container names contain 1–64 letters, digits, underscores or hyphens. The default container cannot be deleted. Switching stops the app; open_app(..., cid=...) switches and launches under one lease. A lost acknowledgment is never automatically replayed.

jb.tap_text uses exact trimmed native UI text and converts point bounds to pixel coordinates. Matching is case insensitive by default. Duplicate matches require occurrence=0 (or another zero-based occurrence, in screen reading order). Disabled and offscreen elements are excluded. UI dump targeting is unbilled and has no OCR fallback. Native dumps require the UI tweak in the foreground app; some custom-rendered content is unavailable. phone.tap_text retains its existing OCR behavior on all phones.

Use the common phone.upload_media(path) and phone.prompt(...) methods on both phone types. Jailbroken uploads use an exact native Photos transaction and wait for its asset acknowledgment before cleaning Library storage. Image/video limits, permissions, shared credits for AI prompts and operation recovery remain the same.

Release files for invilabs 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for invilabs 0.1.6
File Size Uploaded
invilabs-0.1.6.tar.gz 24.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for invilabs 0.1.6
File Interpreter ABI Platform
invilabs-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 49.9 kB

Release files / invilabs-0.1.6.tar.gz

Download URL invilabs-0.1.6.tar.gz
Size 24.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b5cf7e41439a667739398e29a2126aaaa29c9ea787d3743675c735da6e2ac330
BLAKE2b-256 checksum
How to use checksums
0291f6b45d7598d6d1526f2205b87d10f246ea5acd4309aa84627f4cd9f2ed6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / invilabs-0.1.6-py3-none-any.whl

Download URL invilabs-0.1.6-py3-none-any.whl
Size 25.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02cc0f7e14e5f2d6aa9d1f6fc3d602c78fa03e85390cf33c4d8a661fdfab9d8d
BLAKE2b-256 checksum
How to use checksums
5cab8ae8c108b82da35622393104ce7791f75fadf9591fabefa39f2641a8ca82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.6 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page