Skip to main content
  ███████╗███╗   ███╗██████╗ ███████╗██████╗ 
  ██╔════╝████╗ ████║██╔══██╗██╔════╝██╔══██╗
  █████╗  ██╔████╔██║██████╔╝█████╗  ██████╔╝
  ██╔══╝  ██║╚██╔╝██║██╔══██╗██╔══╝  ██╔══██╗
  ███████╗██║ ╚═╝ ██║██████╔╝███████╗██║  ██║
  ╚══════╝╚═╝     ╚═╝╚═════╝ ╚══════╝╚═╝  ╚═╝

Open source, lightweight headless browser for AI agents.

PyPI Python License: MIT

pip install ember-browser

No Docker. No API key to start.

ember demo


Why ember

Most web tools for agents ship with Chromium (~281 MB) or require Docker just to get started. We needed something an agent could use on a VPS, a laptop, or a Raspberry Pi without thinking about it.

ember runs at ~17 MB idle. It decides whether a page needs a browser — you just pass it a URL.

ember Crawl4AI Firecrawl OSS Playwright
Setup pip install pip install Docker + Redis + Node pip + browser install
Package size ~54 MB ~200–350 MB Thin client only ~47 MB
Browser binary Lightpanda ~12 MB Chromium ~281 MB Chromium ~281 MB Chromium ~281 MB
Docker required No No Yes No
API key required No No No No
MCP server Yes No Yes Yes
Search built-in Yes No Yes No
Zero-infra self-host Yes Yes No Yes

Quick start

pip install ember-browser
ember version                  # verify install

ember                          # start the interactive session
ember url https://example.com  # or run a one-shot command
ember serve                    # start the REST API

CLI

Interactive session

ember with no arguments opens a persistent session. Startup shows a short quick start, and help shows the full guide.

  ███████╗███╗   ███╗██████╗ ███████╗██████╗
  ██╔════╝████╗ ████║██╔══██╗██╔════╝██╔══██╗
  █████╗  ██╔████╔██║██████╔╝█████╗  ██████╔╝
  ██╔══╝  ██║╚██╔╝██║██╔══██╗██╔══╝  ██╔══██╗
  ███████╗██║ ╚═╝ ██║██████╔╝███████╗██║  ██║
  ╚══════╝╚═╝     ╚═╝╚═════╝ ╚══════╝╚═╝  ╚═╝

  v0.1.2  lightweight headless browser for AI agents

  Quick Start
  url example.com                          scrape one page
  search openai api                        search the web
  interact example.com -p "summarize"      control a page with AI
  output ./research                        change auto-save folder
  help                                     show the full guide
  quit                                     exit

  ✓ auto-save on → ember_results

ember › url andalabx.com
ember › help
ember › output ./research
ember › search "python asyncio" -n 10
ember › output clear
ember › quit

One-shot commands

Every command works standalone too:

ember url https://example.com                         # scrape a page
ember search "AI agents python" -n 10                 # web search
ember crawl https://docs.example.com --max-pages 20   # crawl a site
ember map https://example.com                         # discover all URLs
ember interact https://amazon.com \
  --prompt "find a mechanical keyboard under $100"
ember extract https://example.com/pricing \
  --prompt "list all plans and prices as JSON"

extract requires EMBER_LLM_API_KEY. interact --no-browser also uses the OpenAI-compatible LLM path, so it needs EMBER_LLM_API_KEY and optionally EMBER_LLM_BASE_URL. Use ember url when you want raw page content without an LLM.

Saving results

All commands accept -o to save that run:

ember url https://example.com -o page.md
ember search "python" -o results.json
ember crawl https://docs.example.com -o ./pages/   # one .md per page
ember map https://example.com -o urls.txt
ember extract https://example.com -o data.json

The CLI saves to ember_results/ by default. Set a different default save directory if you want:

ember config --save-dir ./research/    # persists across sessions
ember config                           # show current settings
ember config --clear-save-dir          # clear it

Or use an environment variable for the current shell:

EMBER_SAVE_DIR=./out ember url https://example.com

In a session, the main save paths are:

ember › url example.com -o page.md     # save just this run
ember › save page.md                   # save the last result
ember › output ./research/             # auto-save all results from now on

Async batch scraping

# urls.txt — one URL per line, # = comment
ember batch urls.txt                      # 5 concurrent by default
ember batch urls.txt -c 20 -o ./pages/   # 20 parallel, save to dir

On Windows, UTF-8 files with a BOM are supported.


Python API

from emb.scrape import scrape_url, scrape_markdown
from emb.search import search
from emb.crawl import crawl
from emb.map import map_url

# Scrape a page → ScrapeResult
result = scrape_url("https://example.com")
print(result.markdown)   # full page content as markdown
print(result.title)      # page title
print(result.success)    # True / False

# Just the markdown text
md = scrape_markdown("https://example.com")

# Crawl a site
result = crawl("https://docs.example.com", max_pages=20, max_depth=3)
for page in result.pages:
    print(page.url, len(page.markdown))

# Discover URLs
result = map_url("https://example.com", max_links=100)
print(result.links)   # list[str]

# Search the web
results = search("python asyncio tutorial", limit=5)
for r in results:
    print(r.title, r.url)

# Browser interaction with natural language
from emb.interact import interact

result = interact("https://example.com", prompt="click the login button")
print(result.content)   # what the agent did / saw

# LLM-powered structured extraction
from emb.agent import extract

data = extract("https://example.com/pricing", prompt="list all plans and prices")
print(data)   # dict

Async

import asyncio
from emb.scrape import scrape_url_async

async def main():
    results = await asyncio.gather(
        scrape_url_async("https://example.com"),
        scrape_url_async("https://httpbin.org/get"),
    )
    for r in results:
        print(r.url, r.success)

asyncio.run(main())

REST API

ember serve               # http://127.0.0.1:51251
ember serve --port 8080   # custom port

EMBER_API_KEY=your-secret ember serve   # require auth
curl -X POST http://localhost:51251/scrape \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-secret" \
  -d '{"url": "https://example.com"}'

curl -X POST http://localhost:51251/search \
  -H "Content-Type: application/json" \
  -d '{"query": "AI agents", "limit": 5}'

curl -X POST http://localhost:51251/crawl \
  -H "Content-Type: application/json" \
  -d '{"url": "https://docs.example.com", "max_pages": 10}'

Endpoints: /scrape /search /crawl /map /interact /extract /agent /health


MCP

Add to your Hermes config, OpenClaw config, Mercury config, or any MCP-compatible host:

{
  "mcpServers": {
    "ember": {
      "command": "ember",
      "args": ["mcp"]
    }
  }
}

Works with Hermes, OpenClaw, Mercury, and any MCP-compatible host.

Available tools: scrape, search_web, crawl_site, map_site, batch_scrape, interact_page, extract_data.

Once connected, your agent can use ember tools directly in conversation:

User: Summarise the latest posts on Hacker News

Agent: [calls scrape("https://news.ycombinator.com")]
       → returns full page markdown with titles, scores, links

Agent: Here are today's top stories on Hacker News: ...
User: Find 5 articles about AI agents and scrape each one

Agent: [calls search_web("AI agents 2025", limit=5)]
       → returns list of {title, url, description}

Agent: [calls batch_scrape(["url1", "url2", ...])]
       → returns markdown for each page

Agent: Here's a summary across all 5 articles: ...

How it works

Not every page needs a browser. ember knows the difference.

Tier 1 — trafilatura handles ~89% of the web: blogs, news, documentation, docs sites, GitHub. Pure HTTP, no browser process, no memory overhead.

Tier 2 — Lightpanda handles JavaScript-heavy pages, SPAs, and interactive content. It's a real browser engine written in Zig, built for machines rather than humans — 20 MB total. ember downloads and caches it automatically on first use, and only falls back to it when tier 1 produces thin content.

Most requests never reach the browser.

Memory footprint

State RAM
Idle ~17 MB
Scraping a static page ~20 MB
Running the browser ~140 MB

Firecrawl needs 4–8 GB in Docker. Crawl4AI imports at 171 MB before scraping anything. ember fits where your agent already runs.


Environment variables

Variable Default Description
EMBER_SAVE_DIR ember_results/ Default directory for saved results. Overrides ember config --save-dir for the current shell.
EMBER_API_KEY (none) Enables API key auth on the REST server (X-API-Key header).
EMBER_PORT 51251 Default port for ember serve. Overridden by --port flag.
EMBER_INTERACT_PROVIDER openai LLM provider for interact (openai, anthropic, ollama, etc.).
EMBER_LLM_API_KEY (none) API key for extract and for interact --no-browser.
EMBER_LLM_BASE_URL https://api.openai.com/v1 OpenAI-compatible LLM API endpoint for extract and interact --no-browser.
EMBER_LLM_MODEL gpt-4o-mini Default model for extract and the no-browser interact path.
EMBER_LIGHTPANDA_PATH (auto) Path to a custom Lightpanda binary. Skips auto-download if set.

License

MIT — open source forever.

Download files

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

Source Distribution

ember_browser-0.1.2.tar.gz (49.7 kB view details)

Uploaded Source

Built Distribution

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

ember_browser-0.1.2-py3-none-any.whl (36.9 kB view details)

Uploaded Python 3

File details

Details for the file ember_browser-0.1.2.tar.gz.

File metadata

  • Download URL: ember_browser-0.1.2.tar.gz
  • Upload date:
  • Size: 49.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ember_browser-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9e56bdfcc240be240f856e0147197fe58f7e6804390944be9e33c27af837aa8d
MD5 9c0b09f613f859babc0cb8a15b4d03c5
BLAKE2b-256 a0ffd8d75e8240223c0c99752a6167b2cbad59706d1224c1f2e44d66a3b35b5b

See more details on using hashes here.

Provenance

The following attestation bundles were made for ember_browser-0.1.2.tar.gz:

Publisher: release.yml on andalabx/ember

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ember_browser-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: ember_browser-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 36.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ember_browser-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2d4a07d9ebf72d6f7c67cf096ffd93cc0fdf256148189abd0e6801617070d0ba
MD5 682ce5428730dd5a6b03dd697187dd1c
BLAKE2b-256 5ec8a9aa33304667d3fe3b0baa8e9a753b89d32c92685a45cf0b8e9e5b41ae78

See more details on using hashes here.

Provenance

The following attestation bundles were made for ember_browser-0.1.2-py3-none-any.whl:

Publisher: release.yml on andalabx/ember

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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