web2cli / v0.2
pip install web2cli

Terminal-native web automation

Every website is a Unix command.

Browse Hacker News, search X.com, write Discord messages, and read Reddit straight from your terminal. No browser loops, no API keys, no $100/mo plans.

Star on GitHub See performance web2cli <site> <command>
demo.sh
$ web2cli hn top --limit 3
┌──────┬──────────────────────────────────────────┬───────┬──────────┐
│ RANK │ TITLE                                    │ SCORE │ COMMENTS │
├──────┼──────────────────────────────────────────┼───────┼──────────┤
│ 1    │ Show HN: I built a CLI for every website │ 313   │ 37       │
│ 2    │ Why agents don't need browsers           │ 271   │ 89       │
│ 3    │ The Unix philosophy, 50 years later      │ 198   │ 64       │
└──────┴──────────────────────────────────────────┴───────┴──────────┘

$ web2cli x search --query "build for agents" --limit 1 --format json

Demo

Real terminal run from the project README

web2cli terminal demo
  • web2cli hn top --limit 3
  • web2cli x search --query "agents" --limit 5
  • web2cli discord messages --server ... --channel ...
  • web2cli discord send --server ... --channel ... --message "deployed"

Why web2cli

One interface for humans and agents

For agents

HTTP GET, not Chromium. 50ms, not 5s. About $0.000001, not $0.10 per loop.

For humans

It is curl for the modern web. Pipe it, grep it, automate it.

For both

One command model everywhere: web2cli <site> <command>.

Performance

Direct HTTP requests. No browser, no DOM, no screenshots.

Metric Browser automation web2cli
Fetch 10 top news from HN ~20s (launch + render) 0.5s
Memory per request ~821.3MB (Chromium) ~5MB (HTTP)
Cost at 10k req/day $20/day (LLM) or ~$23.3/day (LLM + remote browser) ~$0 (HTTP)
Tokens to parse ~8647 (HTML/DOM estimate) ~300 (Markdown table)

Some sites have great APIs. Some have expensive ones. Some have none. web2cli gives one interface for all of them.

Real-World Benchmarks

Official API vs browser vs web2cli

Task Official API Browser web2cli Speedup
Read Discord messages Has API 26s 0.63s 41x
Send a Slack message Has API 35s 0.60s 58x
Search X $100/mo API 75s 1.54s 50x
Search Stack Overflow 300 req/day 41s 0.65s 63x
Fetch HN submissions partial API 36s 1.42s 25x

What This Means For Agents

Cost profile for persistent bot workloads

Scenario Browser automation web2cli
Monitor Discord (1 check/min) $2.88/day $0.0015/day
Scan X every 5 min, 24/7 $1.58/day $0.0003/day
10k daily actions (typical bot) ~$50/day ~$0.01/day
Monthly infra for active agent $50+/mo $4/mo

Browser automation is still the right tool for JS-heavy flows. web2cli targets the 80% that do not need rendering.

More Code Examples

Production-like examples from README

Daily HN Top Stories Summary

web2cli hn top --limit 3 --fields title,url --format md | \
claude -p "Summarize each story URL in 1 sentence. Output bullets." \
  --allowedTools "WebFetch" | \
web2cli discord send --server "ZENO.blue" --channel "testy-mo" > /dev/null

Minimal Discord Answering Bot

import json, subprocess, time, anthropic

NICK = "your_nickname"
SERVER = "YOUR_SERVER_NAME"
CHANNEL = "channel_name_here"
SYSTEM = "You are a bot on Discord. Respond briefly, in user language, without markdown."

seen = set()


def web2cli(*args):
    result = subprocess.run(["web2cli", "discord", *args, "--format", "json"], capture_output=True, text=True)
    return json.loads(result.stdout or "[]")


def fetch():
    return web2cli("messages", "--server", SERVER, "--channel", CHANNEL, "--limit", "20")


def send(text):
    web2cli("send", "--server", SERVER, "--channel", CHANNEL, "--message", text)


def fmt(msgs):
    return "\n".join(f'{m["author"]}: {m["content"]}' for m in msgs)


def ask(context, new_msgs):
    resp = anthropic.Anthropic().messages.create(
        model="claude-sonnet-4-6", max_tokens=512, system=SYSTEM,
        messages=[{"role": "user", "content": f"Last messages:\n{context}\n\nNew for you:\n{new_msgs}"}],
    )
    return resp.content[0].text


# Seed seen IDs
for m in fetch():
    seen.add(m["id"])

print(f"Watching #{CHANNEL} for @{NICK}...")

while True:
    time.sleep(30)
    msgs = fetch()
    new = [m for m in msgs if m["id"] not in seen and NICK in m.get("content", "").lower()]
    for m in msgs:
        seen.add(m["id"])
    if not new:
        continue
    reply = ask(fmt(msgs), fmt(new))
    print(f"→ {reply}")
    send(reply)

Built-In Adapters

Current domains and actions in v0.2

discord.com (dc, discord) WORKING
me, servers, channels, messages, send, dm, dm-messages, dm-send

Auth: login via browser capture or token/cookies where supported.

news.ycombinator.com (hn) WORKING
top, new, item, search, saved, upvoted, submissions

Auth: optional for saved/upvoted.

reddit.com (reddit) WORKING
posts, thread, search

Auth: not required for public endpoints.

slack.com (slack) WORKING
me, channels, messages, send, dm, dm-messages, dm-send

Auth: session-based login capture for production use.

stackoverflow.com (so) WORKING
search, question, tagged

Auth: not required for core reads.

x.com (x, twitter) WORKING
tweet, profile, search, timeline, following

Auth: browser login capture, encrypted local session store.

Documentation + Custom Adapters

Spec-driven workflow for contributors and agent builders

Custom Adapter Quickstart

Create a minimal adapter end-to-end with httpbin.org.

terminal
mkdir -p ~/.web2cli/adapters/httpbin.org
# create ~/.web2cli/adapters/httpbin.org/web2cli.yaml

web2cli adapters validate
web2cli adapters lint httpbin.org
web2cli adapters info hb
web2cli hb ip

web2cli login hb --token "abc123"
web2cli hb bearer-check --trace --verbose

Debugging + Browser Login

Inspect parser quality and auth capture flow

Debugging and Quality

terminal
web2cli adapters validate
web2cli adapters lint

web2cli reddit posts --sub python --limit 3 --trace
web2cli so question --id 79861629 --format json --no-truncate

web2cli doctor browser
web2cli doctor browser --deep

Browser Login

Capture cookies and runtime tokens directly from a real browser. Sessions are encrypted in ~/.web2cli/sessions/<domain>.json.enc.

terminal
web2cli login x.com --browser
web2cli login x.com --status
web2cli login slack --browser --browser-debug

web2cli Cloud

OAuth-style auth layer for websites that do not have OAuth

Building an agent for other people? Users click a link, log in in a sandboxed browser, and your agent receives an opaque session token. No raw cookies on your backend.

Join the waitlist