ADB handles shell ops; Appium handles UI automation (taps, swipes, app launch) on cloud phones. Production farms connect vendor ADB first, then attach Appium with udid matching the remote serial. This recipe bridges ADB guide and vendor APIs (GeeLark/DuoPlus).

Stack

Cloud vendor API → start pad → adb connect host:port
Appium 2 server (local worker) → UiAutomator2 driver → Python client
Job done → driver.quit() → vendor API stop pad

Install (worker VM)

npm install -g appium@next
appium driver install uiautomator2
pip install Appium-Python-Client

Capabilities template

caps = {
    "platformName": "Android",
    "appium:automationName": "UiAutomator2",
    "appium:udid": "203.0.113.10:5555",  # from adb devices
    "appium:noReset": True,
    "appium:newCommandTimeout": 300,
    "appium:skipServerInstallation": False,
    # Optional: appPackage / appActivity for TikTok, IG, Shopee
    "appium:appPackage": "com.zhiliaoapp.musically",
    "appium:appActivity": ".splash.SplashActivity",
}

Python session wrapper

import subprocess
import random
import time
from appium import webdriver
from appium.options.common import AppiumOptions

APPIUM = "http://127.0.0.1:4723"

def adb_connect(endpoint: str) -> None:
    subprocess.run(["adb", "connect", endpoint], check=True, capture_output=True)
    out = subprocess.check_output(["adb", "devices"], text=True)
    assert endpoint in out and "device" in out, f"ADB offline: {out}"

def human_pause(lo=0.8, hi=2.4) -> None:
    time.sleep(random.uniform(lo, hi))

def with_appium(endpoint: str, caps: dict, fn):
    adb_connect(endpoint)
    opts = AppiumOptions()
    opts.load_capabilities({**caps, "appium:udid": endpoint})
    driver = webdriver.Remote(APPIUM, options=opts)
    try:
        fn(driver)
    finally:
        driver.quit()

def warm_scroll(driver):
    size = driver.get_window_size()
    x = size["width"] // 2
    for _ in range(3):
        driver.swipe(x, int(size["height"] * 0.75), x, int(size["height"] * 0.25), 600)
        human_pause()

Pair with vendor API worker

# Pseudocode — start pad via GeeLark/DuoPlus API, then Appium
async def job(pad_id: str):
    adb_ep = await vendor_start(pad_id)   # returns "host:port"
    with_appium(adb_ep, caps, warm_scroll)
    await vendor_stop(pad_id)

Anti-ban rules for Appium on cloud phones

DoAvoid
Random tap offsets ±8pxPixel-perfect same coordinates
Variable swipe duration 400–900msFixed 300ms loops
One session per account per day for warm tier24/7 scroll bots on prod devices
Stop pad when session endsLeave Appium + pad running overnight

Hybrid with Multilogin

Appium runs the native app layer. Web seller dashboards, TikTok Shop browser, Meta ads stay on Multilogin — never share login sessions between Appium app and browser profile. See hybrid architecture and TikTok Shop web guide.

Related

Disclosure: MLX-MMO affiliated with Multilogin. Appium and package names change with app updates — verify before production.