Install
openclaw skills install @iwedmak/realbrowserLets your AI agent drive a real Chrome session (yours or a marketplace participant's) for tasks where headless or scripted requests are not enough — your own QA flows, accessibility audits of sites you control, synthetic monitoring, support-dashboard automation, and data collection on sites whose Terms of Service and robots.txt permit it.
openclaw skills install @iwedmak/realbrowserUse 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.clawhub skill install realbrowser
Or install the CLI standalone:
pip install --upgrade ceki-sdk --break-system-packages # v2.18.0+
ceki --help
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.
# 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
| Mode | Where | Cost | Visibility |
|---|---|---|---|
| Self | Your own Chrome (after installing the Ceki extension) | Free when host_user == renter_user | Only you |
| Marketplace | Chrome contributed by another opted-in user | $0.01/min, USDC | The host can see your session |
| Earn (opt-in, off) | Your idle Chrome contributed back to the marketplace | You receive 90% of session price | Other 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.
| Need | Command | Channel |
|---|---|---|
| Open URL | ceki navigate | native |
| Capture state (screen + chat + ts) | ceki snapshot -o | native |
| Click / type / scroll | ceki click / type / scroll | native |
| Check whether rental is alive | ceki snapshot / ceki sessions | native |
| Chat with the host | ceki chat send/next | native |
Default state probe: ceki snapshot $SID -o PATH. One command returns {chat, screenshot, ts}.
no_session ≠ end of rentalceki 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.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.
| cmd | args | result |
|---|---|---|
rent | --schedule N (required) `[--mode incognito | main] [--fingerprint-from profile.json]` |
search | [--limit N] [--filter key=val]... | [BrowserOption, ...] |
snapshot <sid> | -o PATH | {chat, screenshot, ts} |
screenshot <sid> | `-o file.png [--format png | jpeg] [--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/history | various | message or batch |
cdp <sid> | --method <M> [--params JSON] | raw CDP response |
wait <sid> | — | blocking until session ends |
profile <sid> export/import | various | profile JSON (your machine) |
upload <sid> | --selector ... --file ... | {ok, filename, size} |
sessions | — | your active sessions |
stop <sid> | — | {ok:true} |
# 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 alwaysFill 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:
ceki type $SID "octocat-demo"
ceki cdp $SID --method Runtime.evaluate --params '{"expression":"document.activeElement.blur()"}'
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:
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.
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.
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
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.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.