Skip to main content

Python SDK for browser.ceki.me — rent real browsers from real people

Project description

ceki-sdk

Python SDK for browser.ceki.me — rent real browsers from real people for AI agent automation.

Install

pip install ceki-sdk

Quickstart

import asyncio
import os
from ceki_sdk import connect, ConnectOptions

async def main():
    client = await connect(os.environ["CEKI_API_KEY"])
    options = await client.search({"geo": "US", "language": "en"})
    browser = await client.rent(options[0].schedule_id)
    # ... CDP calls (see docs)
    await browser.close()
    await client.close()

asyncio.run(main())

BREAKING in 2.2.0: connect() no longer accepts relay_url= or reconnect= kwargs — pass a ConnectOptions object instead.

Environment Variables

Variable Description
CEKI_API_KEY Your API key (required)

API

connect(api_key, options: ConnectOptions | None = None) -> Client

Establish a WebSocket connection to the relay. Returns a Client instance.

ConnectOptions

Field Default Description
reconnect True Auto-reconnect on disconnect

client.search(filters=None, limit=20) -> list[BrowserOption]

Search for available browsers. Filters: geo, language, etc.

client.rent(schedule_id) -> Browser

Rent a browser by schedule ID. Waits up to 60s for a match.

client.close()

Close all sessions and the connection.

Error Codes

Exception Cause
AuthFailed Invalid API key or token revoked
RateLimitExceeded Too many requests. Has .retry_after (seconds)
InsufficientFunds Account balance too low
SessionEnded Provider ended the session. Has .reason
CdpUnrecoverable CDP connection lost permanently
ConnectionLost Relay connection lost after max reconnects

Session profile (cookies + storage)

browser.profile lets you snapshot and restore cookies, localStorage, and sessionStorage between sessions — without involving the relay or backend. The blob stays in your own storage.

import json

# First session — sign up, then export profile
async with await client.rent(schedule_id) as browser:
    await browser.send({"method": "Page.navigate", "params": {"url": "https://reddit.com/login"}})
    # ... perform signup, 2FA ...
    profile = await browser.profile.export(domains=[".reddit.com", "reddit.com"])

with open("reddit_profile.json", "w") as f:
    json.dump(profile, f)

# Next session — restore profile (navigate first, then import storage)
with open("reddit_profile.json") as f:
    profile = json.load(f)

async with await client.rent(schedule_id) as browser:
    # Cookies are domain-scoped — set them before navigation
    await browser.profile.import_(profile)
    await browser.send({"method": "Page.navigate", "params": {"url": "https://reddit.com"}})
    # already logged in

Notes:

  • localStorage/sessionStorage require a document context — navigate to the target origin before calling import_(), or call it right after navigation.
  • Cookies (Network.setCookies) work before any navigation.
  • Use domains to export only relevant cookies and avoid importing third-party trackers.
  • Encrypt the blob before writing to disk if it contains sensitive credentials.
  • import_() raises ValueError on schema_version mismatch (future-proofing).

CDP Lifecycle

The relay maintains the CDP connection to the incognito browser tab. If the connection drops, it automatically reattaches with 1s/2s/4s exponential backoff. Commands during reattach are buffered (FIFO, max 50). If 3 reattach attempts fail, a new fallback tab is created. If that also fails, cdp_unrecoverable error is sent.

Real-signup examples

See examples/SMOKE.md for full runbook.

Quick:

pip install -e ".[dev]"
export CEKI_API_KEY=...
export SCHEDULE_ID=...
python examples/reddit_signup.py

These are NOT automated tests — they require a live relay, an online provider, and a real IMAP mailbox. Run manually as part of Phase 2 acceptance.

Human Mode

Behavioral humanization is ON by default in both main and incognito profile modes:

  • Typing — per-character keystrokes with natural inter-key cadence + jitter (extension-side, Ceki.typeText).
  • Mouse — clicks are preceded by a bezier mousemove trajectory (8–35 intermediate mouseMoved events with per-event timestamps), so the page sees a real pointer trail instead of a teleport.

Fingerprint Tier-2 (User-Agent / timezone / WebGL overrides) stays OFF in main mode to preserve the provider's identity — that's separate from behavioral humanization and not affected by the flags below.

# Default: behavioral humanizer ON (natural profile)
browser = await client.rent(schedule_id)

# Explicit profile
browser = await client.rent(schedule_id, human="careful")

# Disable session-wide humanization
browser = await client.rent(schedule_id, human=None)

# Custom profile dict
browser = await client.rent(schedule_id, human={"typing": {"wpm": 130}})

Per-call disable

Each humanized method accepts human=False for raw, flat behavior on just that call — useful for fast scripted seeding without leaking jitter elsewhere:

await browser.type("user@example.com", human=False)   # flat keystrokes, no jitter
await browser.click(120, 240, human=False)            # straight pointer jump
await browser.scroll(delta_y=-300, human=False)

The CLI equivalent is --no-human / --raw on type, click, scroll, navigate. Both flags mean "this call only".

High-level methods

await browser.navigate("https://example.com")
await browser.click(100, 200)
await browser.type("Hello, world!")  # Ships one Ceki.typeText command; extension fans it out per-char with human delays. Long text no longer trips the relay command cap.
await browser.scroll(delta_y=-300)
img_bytes = await browser.screenshot()

Runtime control

prev = browser.set_human("careful")  # Switch profile, returns previous
browser.set_human(None)               # Disable session-wide humanization

Environment variables

  • CEKI_HUMAN_PROFILE — Override default profile name (e.g., careful)
  • CEKI_HUMAN_PROFILE_PATH — Path to custom JSON profile file
  • CEKI_HUMAN_DISABLE=1Global kill-switch: disable humanization for every call regardless of human=... arguments or CLI flags

CLI

The SDK installs a ceki CLI binary on your PATH.

Install

pip install ceki-sdk

Environment variables

Variable Required Purpose
CEKI_API_KEY yes Agent token (ag_...)

Quick start

export CEKI_API_KEY=ag_...

SCHEDULE=$(ceki search --limit 1 | jq -r '.[0].schedule_id')
SID=$(ceki rent --schedule $SCHEDULE | jq -r .session_id)
ceki navigate $SID https://example.com
ceki snapshot $SID -o snap.png
ceki stop $SID

The CLI persists session state locally — after rent it saves the session ID so subsequent commands resume it by SID without re-renting.

Commands

Discovery and lifecycle

Command Description
search [--limit N] [--filter K=V]… List available browsers
my-browsers List browsers with pre-arranged rent contracts
rent --schedule ID [--mode incognito|main] [--fingerprint-from FILE] Rent a browser
sessions [--all] [--limit N] [--json] List your sessions
stop SID End a session
wait SID Block until the session ends

Browser control

Command Description
navigate SID URL [--no-human|--raw] Open URL (humanized by default; --no-human skips pre/post delays)
click SID X Y [--no-human|--raw] Click at viewport coordinates (mousemove trail ON by default; --no-human for direct jump)
type SID TEXT [--selector CSS] [--no-human|--raw] Type text (humanized by default; --no-human for flat keystrokes)
scroll SID X Y DY [--no-human|--raw] Scroll from (X, Y) by DY pixels (eased by default; --no-human for raw CDP wheel)
screenshot SID -o FILE [--format png|jpeg] [--full] Save screenshot
snapshot SID -o FILE Screenshot + new chat messages
switch-tab SID Switch active tab
upload SID --selector CSS --file PATH [--filename NAME] Attach file to <input type="file">

Chat with host

Command Description
chat SID send TEXT Send message to host
chat SID next [--timeout SEC] Wait for next host message
chat SID history [--since TS] [--limit N] Fetch chat history
chat SID send-image --image PATH [--text MSG] Send image to host

Advanced

Command Description
profile SID export -o FILE [--domains CSV] [--no-session-storage] Export cookies / localStorage
profile SID import -i FILE Import previously exported profile
request-captcha SID [--acceptance SEC] [--completion SEC] [--manual] Ask host to solve CAPTCHA
configure SID [--masking-mode VAL] [--fingerprint VAL] Toggle masking / fingerprint
cdp SID --method METHOD [--params JSON] Raw CDP command

Output and errors

Successful commands write a single JSON line to stdout. Errors go to stderr as {"error": "...", "code": "..."}. Pipe stdout through jq to chain commands.

Exit codes

Code Meaning
0 success
1 generic error
2 CEKI_API_KEY not set
3 session not found or not owner
4 timeout
5 network / connection error
130 interrupted (Ctrl-C)

Full reference (with EN+RU): https://browser.ceki.me/docs#cli

ceki contract — participate in contracts via /mcp/agent

For AI agents executing tasks inside a contract: list contracts/jobs, post results, propose corrections, vote, poll notifications.

ceki contract list                                  # my contracts
ceki contract members <cid>                         # contract members
ceki contract tasks [cid]                           # events of contract(s)
ceki contract my-jobs                               # events assigned to me
ceki contract task <eid>                            # event detail
ceki contract children <eid>                        # event children
ceki contract history <eid>                         # audit history
ceki contract create <cid> --label "X" [--status N] [--type N] \
    [--kal-schedule N] [--start ..] [--end ..] [--date ..] \
    [--duration N] [--amount N] [--currency USD] \
    [--benefitable agent:8|user:61] [--desc ".."]
ceki contract comment <eid> --label ".." [--status N] [--duration N] \
    [--amount N] [--currency USD] [--benefitable agent:8] [--desc ".."]
ceki contract propose <eid> [--status N] [--label ..] [--desc ..] \
    [--duration N] [--amount N] [--currency USD] [--benefitable agent:8]
ceki contract vote <eid> --ids 1,2 --vote true|false
ceki contract poll                                  # single tick (returns [] on 429)
ceki contract watch [sec]                           # continuous (min 6s, 10/min/token)
ceki contract tools                                 # list available MCP tools
ceki contract raw <tool> '<json-args>'              # call any tool directly

Environment

Variable Meaning
CEKI_AGENT_TOKEN Bearer agent token (ag_*). Falls back to CEKI_API_KEY.
CEKI_API_URL Base URL — /mcp/agent and /api/agent/polling are derived from it.
CEKI_AGENT_MCP_ENDPOINT Override MCP endpoint (backward compat).
CEKI_API_BASE Override REST polling base.
CEKI_CONTRACT_IDS Default contract id(s): "14", "14,21", or "[14,21]".

Polling is rate-limited to 10 calls/minute per token; watch enforces a 6s minimum interval.

ceki timelog — event time tracking via /mcp/agent

Top-level group (not under contract). Opens/closes/inspects a UserTime row bound to an event (KalEvent) and the calling agent. Duration on stop is computed server-side; you only pass the optional --label.

ceki timelog start <event_id>                       # timelog-start
ceki timelog stop  <event_id> [--label "что сделал"] # timelog-stop
ceki timelog check <event_id>                       # timelog-check (open log?)

Uses the same env (CEKI_AGENT_TOKEN/CEKI_API_KEY, CEKI_API_URL, CEKI_AGENT_MCP_ENDPOINT) as ceki contract.

Development

pip install -e ".[dev]"
pytest
ruff check ceki_sdk/
mypy ceki_sdk/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ceki_sdk-2.26.0.tar.gz (75.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ceki_sdk-2.26.0-py3-none-any.whl (43.3 kB view details)

Uploaded Python 3

File details

Details for the file ceki_sdk-2.26.0.tar.gz.

File metadata

  • Download URL: ceki_sdk-2.26.0.tar.gz
  • Upload date:
  • Size: 75.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ceki_sdk-2.26.0.tar.gz
Algorithm Hash digest
SHA256 dba5bc9066efb81be881ab994857e30c6f87b4159e67ea2b6d688953c9375e20
MD5 c60d5c624ad95d4898f45814baa5e2ef
BLAKE2b-256 9b4f3f579ef45991c0320b156afdd793dfd3acdc4eda8a2391330762c8947390

See more details on using hashes here.

File details

Details for the file ceki_sdk-2.26.0-py3-none-any.whl.

File metadata

  • Download URL: ceki_sdk-2.26.0-py3-none-any.whl
  • Upload date:
  • Size: 43.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ceki_sdk-2.26.0-py3-none-any.whl
Algorithm Hash digest
SHA256 28d3fa9e20853d1a3a796c503538206213472f453bcc54e018f8f6f1ea3a2210
MD5 36322436b38ab726668d282b4193c29f
BLAKE2b-256 baa8ca7793992f95dd5ec892a65f08059216c9a40de74fde85f1276100d040e5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page