fetcha — Python SDK
Official Python client for Fetcha, the intelligent web-access API. Standard library only, Python 3.9+.
bash
pip install fetchapython
from fetcha import Fetcha
client = Fetcha(api_key="fch_live_...")
# Fetch a page — Fetcha picks the network, geography and retries for you
result = client.fetch("https://example.com", country="CA")
print(result.status, result.metadata["network"], result.metadata["mode"], result.metadata["attempts"])
print(result.content[:500])
# Readable text, Markdown or JSON helpers
text = client.text("https://example.com")
md = client.markdown("https://example.com/article") # main content as Markdown
data = client.json("https://api.example.com/items")
# Page metadata and links come with every HTML response
r = client.fetch("https://example.com", links=True)
print(r.page["title"], r.page["links_count"], r.links[:3])
# Managed browser rendering (same network / country / session as a plain fetch)
r = client.render("https://app.example.com/dashboard", wait_for="table.results", screenshot=True)
open("dashboard.png", "wb").write(__import__("base64").b64decode(r.screenshot))
# Blocked HTTP attempts escalate to the browser automatically (browser_fallback=True by default);
# r.mode tells you whether the final attempt was "http" or "browser".
# Sticky sessions keep the same exit identity across requests
session = client.sessions.create(country="CA", ttl=600)
page1 = client.fetch("https://example.com/login", session=session.id)
page2 = client.fetch("https://example.com/account", session=session.id)
# Crawl a site into Markdown (asynchronous job)
job = client.crawl.create(url="https://docs.example.com/", max_pages=200, max_depth=3, include_patterns=["/docs/*"])
job = client.crawl.wait(job.id, poll_s=2.0, timeout_s=600) # polls until completed | failed | cancelled
print(job.status, job.stats) # {'discovered': …, 'fetched': …, 'ok': …, 'blocked': …, 'failed': …, 'bytes': …}
for page in client.crawl.iter_pages(job.id, status="success"):
print(page.url, page.title, len(page.content or ""))
# or page manually: client.crawl.pages(job.id, cursor=None, limit=100).next_cursor
client.crawl.cancel(job.id)
# Map a site's URLs (sitemap + links) without fetching every page
m = client.map("https://docs.example.com/", search="/docs/*", limit=500)
print(m.count, m.urls[:5], m.sources, m.truncated)Errors raise fetcha.FetchaError with .code, .status, .request_id and .details.