> ## Documentation Index
> Fetch the complete documentation index at: https://phone-harness.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Python example

> Create a phone, wait for readiness, capture a screenshot, and request cleanup.

This example uses Python's standard library. It checks the operations advertised by the session, gives creation a stable idempotency key, and requests cleanup in `finally`.

The client wait budget is independent of the phone's selected duration. If that budget is exceeded, the example requests closure instead of leaving the phone running.

<Note>
  This is a real API example. Running it with a valid account key creates a metered session. The published example contains no credentials.
</Note>

Copy the Python code below into `phone_example.py`, then run:

```bash theme={null}
export PHONE_HARNESS_API_KEY="pck_REPLACE_WITH_YOUR_KEY"
python3 phone_example.py
```

```python theme={null}
import subprocess
import json
import os
import time
import urllib.error
import urllib.request
import uuid
from pathlib import Path

API = "https://api.phone-harness.com"
KEY = os.environ["PHONE_HARNESS_API_KEY"]


def request(method, path, body=None, request_key=None):
    headers = {"Authorization": f"Bearer {KEY}"}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    if request_key is not None:
        headers["Idempotency-Key"] = request_key
    req = urllib.request.Request(
        API + path, data=data, headers=headers, method=method
    )
    with urllib.request.urlopen(req, timeout=40) as response:
        return json.load(response)


request_key = str(uuid.uuid4())
print("Create request key:", request_key)  # A receipt key, not a credential.
sid = None
try:
    # Reuse this key and this exact body if the creation result is ambiguous.
    created = request(
        "POST", "/sessions", {"timeout_seconds": 900}, request_key
    )
    sid = created["id"]
    deadline = time.monotonic() + 180  # Example client wait budget.
    while True:
        session = request("GET", f"/sessions/{sid}")
        if session["state"] == "ready":
            break
        if session["state"] in ("error", "closing"):
            raise RuntimeError(session.get("error", session["state"]))
        if time.monotonic() >= deadline:
            raise TimeoutError("Client stopped waiting for readiness")
        time.sleep(2)

    adb = session["adb"]
    serial = f"{adb['host']}:{adb['port']}"
    subprocess.run(["adb", "connect", serial], check=True)
    subprocess.run(["adb", "-s", serial, "shell", "unlock", adb["code"]], check=True)
    png = subprocess.run(["adb", "-s", serial, "exec-out", "screencap", "-p"],
                         check=True, capture_output=True).stdout
    Path("screenshot.png").write_bytes(png)
    print("Saved screenshot.png")
finally:
    if sid is None:
        # A lost POST response can still have admitted a session.
        try:
            receipt = request("GET", f"/sessions/requests/{request_key}")
            if not receipt["cleanup_complete"]:
                sid = receipt["id"]
        except Exception as recovery_error:
            print("Receipt recovery did not complete:", type(recovery_error).__name__)
            print("Keep the create request key and follow the retry guide.")
    if sid is not None:
        try:
            closed = request("DELETE", f"/sessions/{sid}")
            print("Cleanup response:", closed)
        except Exception as cleanup_error:
            print("Closure not confirmed:", type(cleanup_error).__name__)
            print("Recover the receipt with request key:", request_key)
```

If closure returns `cleanup_pending: true`, poll the create receipt to confirm `cleanup_complete: true`. A failed receipt lookup is not proof that no phone was created. Preserve the request key and use the [retry and recovery flow](/docs/guides/retries).

The finite session deadline still applies if your process exits or loses its connection. In a production integration, store request keys durably and recover unfinished requests when your process restarts.
