Use responsibly. This skill drives a real Chrome browser. Before using it on any site, confirm you have authorization (your own site, your own account, permissive Terms of Service, explicit consent of the site owner). The skill does not enable anything you wouldn't be allowed to do manually.
It lets your agent open a Chrome session, navigate, click, type, scroll, take screenshots, and read the page — through one of three paths:
The skill is a thin client to the ceki-sdk CLI and Python SDK. It does not bundle credentials, does not perform any network operation other than CLI invocations you make explicitly, and does not transmit any data outside the API endpoints documented below.
robots.txt permit itrobots.txtIf you're not sure whether your use case is appropriate, default to: don't use this skill, ask the site owner.
profile export / profile import flow to manage it yourself.bash
clawhub skill install realbrowser
Or install the CLI standalone:
bash
pip install --upgrade ceki-sdk --break-system-packages # v2.18.0+
ceki --help
bash
export CEKI_API_KEY="your_key_here"
The skill does NOT transmit your API key during installation. Token verification is a separate, opt-in step you run manually after the skill is installed.
bash
# discover marketplace browsers (does not rent anything; no charges)
ceki search --limit 5
# rent a Chrome (incurs marketplace charges if you're not host_user == renter_user)
SID=$(ceki rent --schedule <schedule_id> | jq -r .session_id)
# drive
ceki navigate $SID https://example.com
ceki snapshot $SID -o /tmp/01.png
ceki click $SID 400 300
ceki type $SID "hello world"
# stop (releases the rental and settles)
ceki stop $SID
ModeWhereCostVisibilitySelfYour own Chrome (after installing the Ceki extension)Free when host_user == renter_userOnly youMarketplaceChrome contributed by another opted-in user$0.01/min, USDCThe host can see your sessionEarn (opt-in, off)Your idle Chrome contributed back to the marketplaceYou receive 90% of session priceOther agents you allow can rent your Chrome
The CLI splits into native (high-level — server bridges to the host's Chrome) and CDP ( ceki cdp — raw Chrome DevTools Protocol). Different channels.
NeedCommandChannelOpen URLceki navigatenativeCapture state (screen + chat + ts)ceki snapshot -onativeClick / type / scrollceki click / type / scrollnativeCheck whether rental is aliveceki snapshot / ceki sessionsnativeChat with the hostceki chat send/nextnative
Default state probe: ceki snapshot $SID -o PATH. One command returns {chat, screenshot, ts}.
no_session ≠ end of rental#ceki cdp $SID --method Runtime.evaluate ... may return no_session while the rental itself is alive. Check liveness via native commands ( ceki snapshot, ceki sessions).
ceki stop) also counts in the rate bucket. Keep one session open and navigate between sites instead of stop+rent per site.text
ceki rent --schedule N ───► {session_id, schedule_id, chat_topic_id}
ceki navigate <sid> <url> ───► {ok: true}
ceki snapshot <sid> -o path ───► {chat:[...], screenshot:path, ts:...}
ceki click/type/scroll/cdp ───► {ok: true}
ceki chat <sid> send/next ───► {message_id|text|null}
ceki stop <sid> ───► {ok: true}
Always stop. Without ceki stop, the rental keeps the meter running until the host disconnects or your balance runs out.
cmdargsresultrent``--schedule N (required) `[--mode incognitomain] [--fingerprint-from profile.json]`search``[--limit N] [--filter key=val]...``[BrowserOption, ...]``snapshot <sid>``-o PATH``{chat, screenshot, ts}``screenshot <sid>`-o file.png [--format pngjpeg] [--full]`navigate <sid> <url>—{ok:true}``click <sid> <x> <y>—{ok:true,pointer:[x,y]}``type <sid> "<text>"``[--no-human] (skip humanizer){ok:true}``scroll <sid> <x> <y> <dy>—{ok:true}``switch-tab <sid>—{ok:true}``chat <sid> send/next/historyvariousmessage or batchcdp <sid>``--method <M> [--params JSON]raw CDP responsewait <sid>—blocking until session endsprofile <sid> export/importvariousprofile JSON (your machine)upload <sid>``--selector ... --file ...``{ok, filename, size}``sessions—your active sessionsstop <sid>—{ok:true}
bash
# 1. Rent (or use Self mode if you're hosting your own Chrome)
SID=$(ceki rent --schedule <your_schedule_id> | jq -r .session_id)
# 2. Run through your own user flow
ceki navigate $SID https://my-app.example.com/login
ceki type $SID "test@my-app.example.com"
ceki click $SID 400 500
ceki type $SID "my-test-password"
ceki click $SID 400 600
# 3. Verify the post-login state
ceki snapshot $SID -o /tmp/after-login.png
# 4. Done
ceki stop $SID
ceki type always#Fill fields ONLY via ceki type $SID "text". Don't reach into ceki cdp Runtime.evaluate with el.value=... for input — that's the main cause of "field empty / required" on submit (React/Vue value trackers).
After filling async-validated fields (username availability, email-availability), move focus away ( blur) so validation fires:
bash
ceki type $SID "octocat-demo"
ceki cdp $SID --method Runtime.evaluate --params '{"expression":"document.activeElement.blur()"}'
bash
ceki upload $SID --selector 'input[type="file"]' --file /tmp/avatar.png
Some web apps open new tabs via window.open(url). The CLI sees this as a tab switch, which can break the sequence. Preemptively redirect window.open before the click:
bash
ceki cdp $SID --method Runtime.evaluate --params '{
"expression": "window.open=function(url){window.location.href=url;return window;};"
}'
Always honor the site's consent preferences. When a banner appears, reject non-essential cookies, or click the appropriate "necessary only" / "reject all" option. Do not click "accept all" by default.
bash
ceki cdp $SID --method Runtime.evaluate --params '{
"expression": "(()=>{const wants=[\"reject all\",\"decline\",\"necessary only\",\"strictly necessary\"];const els=[...document.querySelectorAll(\"button,a,[role=button]\")];for(const t of wants){const b=els.find(e=>(e.textContent||\"\").trim().toLowerCase().includes(t));if(b){b.click();return t}}return null})()"
}'
The server does not retain cookies or storage between rentals (stateless by design — privacy / data minimization). You can export them to a local file at the end of a session and import on the next one — but only do this for sites you have authorization to use, and only for accounts that are yours.
bash
SID=$(ceki rent --schedule N --fingerprint-from /tmp/my-app.json | jq -r .session_id)
ceki navigate $SID "https://my-app.example.com"
ceki profile $SID import -i /tmp/my-app.json
python
import asyncio
import os
from pathlib import Path
from ceki_sdk import connect, ConnectOptions
async def main():
token = os.environ.get("CEKI_API_KEY")
if not token:
raise SystemExit("Set CEKI_API_KEY")
opts = ConnectOptions(
relay_url="wss://browser.ceki.me/ws/agent",
api_url="https://api.ceki.me",
chat_url="https://chat.ceki.me/api/chat",
)
client = await connect(token, opts)
options = await client.search({})
if not options:
await client.close()
return
browser = await client.rent(options[0].schedule_id)
try:
await browser.navigate("https://my-app.example.com")
png = await browser.screenshot(format="png")
Path("/tmp/shot.png").write_bytes(png)
finally:
await browser.close()
await client.close()
asyncio.run(main())
curl POST /api/sessions + WS connect. Use ceki rent./api/browsers/search each time.profile export / import for persistence — and only for your own accounts.ceki stop, the rental keeps the meter running.bash
ceki --help | head -1
echo "$CEKI_API_KEY" | head -c 4 # should start with ag_
# Manually verify token (one-off, when you're ready):
curl -s -H "Authorization: Bearer $CEKI_API_KEY" https://api.ceki.me/api/auth/introspect | jq '.tokenable_id, .name'
# Discover available browsers
curl -s -H "Authorization: Bearer $CEKI_API_KEY" https://api.ceki.me/api/browsers/search | jq '.meta.total'
example.com / httpbin — use a headless Chrome locallyMIT. See LICENSE.