Skip to main content

enconvert (Python)

Honest eyes for your AI agent — the Python SDK for Enconvert. Python 3.9+.

Read any web page or file into clean Markdown, JSON, or screenshots, and get a render_quality score (0.0–1.0) on every read — so a blocked, challenge, or empty-SPA page comes back flagged with a low score and warnings, never mistaken for real content. Perceive, discover, look up, distill, ingest, and watch the web; convert 40+ file and document formats through the same key.

Wiring an agent (Claude, Cursor, Windsurf, n8n, …)? The MCP server is the native path — npx @enconvert/mcp setup. This SDK is the programmatic REST path for everything else.

Install

pip install enconvert

Quick Start

from enconvert import Enconvert

client = Enconvert(api_key="sk_...")

# Read a page the way your agent should — with a quality score attached.
op = client.v2.perceive("https://example.com", outputs=["markdown", "structured"])
print(op.outputs["markdown"].url, op.render_quality)  # e.g. 0.93

V2 — agent-ready data (client.v2)

The V2 namespace turns web pages into agent-ready data: render, search, extract, ingest, and monitor. All V2 endpoints require a private API key and are plan-gated — a disabled feature or exhausted monthly quota raises QuotaError (HTTP 402).

Every render carries render_quality (0.0–1.0). A low score means the page didn't render cleanly (challenge page, cookie wall, empty shell); the content is still returned, flagged, so a bad read never quietly enters your agent's context.

A content-free block (challenge page, bot wall) comes back as HTTP 200 with is_blocked=True, empty outputs and billed=False; reads whose deductions include http_error or login_wall are also unbilled. Treat render_quality under 0.40 as not-content.

Perceive — render a URL into artifacts

op = client.v2.perceive(
    "https://example.com",
    outputs=["markdown", "screenshot", "structured"],
    extract=["tables", "metadata"],
)
print(op.render_quality)            # honesty score, 0.0-1.0
print(op.outputs["markdown"].url)   # 15-min signed URL
print(op.structured)

# Re-sign artifact URLs later:
again = client.v2.get_perceive_operation(op.operation_id)

# Batch (<=1000 URLs; small batches run inline, larger return "queued" — poll):
batch = client.v2.perceive_batch(
    ["https://a.com", "https://b.com"],
    outputs=["markdown"],
    output_mode="zip",
)
done = client.v2.get_perceive_batch(batch.job_id)

# Direct download: the response body IS the artifact bytes (no signed URL).
# Requires exactly one artifact-producing output; metadata rides in headers.
direct = client.v2.perceive_direct("https://example.com", outputs=["markdown"])
print(direct.filename, direct.content_type, len(direct.content))

# Re-download a stored artifact from an earlier operation:
raw = client.v2.download_perceive_artifact(op.operation_id, output="markdown")

Discover — enumerate a site's URLs (no rendering)

found = client.v2.discover(
    "https://example.com",
    mode="hybrid",              # "sitemap" | "crawl" | "hybrid"
    max_urls=200,
    exclude_patterns=["/tag/"],
)
print(found.total, found.urls)

Lookup — web search with optional auto-perceive

search = client.v2.lookup(
    "best static site generators",
    category="web",             # web | news | images | scholar | patents | maps
    num_results=10,
    perceive_top=3,              # auto-render top 3 results (uses perceive quota)
)
for hit in search.results:
    print(hit.title, hit.url, hit.perceive.render_quality if hit.perceive else None)

Distill — schema-driven structured extraction

from enconvert import CssField, CssSchema, DistillDiscoverFrom

extraction = client.v2.distill(
    urls=["https://example.com/pricing"],
    schema={"plans": "list of plan names with monthly prices"},
    css_schema=CssSchema(               # optional free CSS pass before the LLM tier
        base_selector=".plan-card",
        fields=[
            CssField(name="name", type="text", selector="h3"),
            CssField(name="price", type="text", selector=".price"),
        ],
    ),
)
print(extraction.results[0].data, extraction.results[0].extraction_tier)

# Or discover-then-distill:
client.v2.distill(
    discover_from=DistillDiscoverFrom(url="https://example.com", mode="sitemap", max_pages=10),
    schema={"title": "page title", "summary": "one-line summary"},
)

Ingest — site or files to RAG-ready JSONL (always async)

Turn a whole site — or a set of uploaded documents — into chunked, RAG-ready JSONL through one pipeline.

from enconvert import IngestChunkOptions

# From a site:
job = client.v2.ingest(
    mode="sitemap",
    url="https://docs.example.com",
    max_pages=100,
    chunk=IngestChunkOptions(max_words=512, sentence_overlap=1),
    webhook_url="https://my.app/hooks/enconvert",
)

# Or from uploaded files (PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, legacy/ODF office):
file_job = client.v2.ingest_files(
    ["handbook.pdf", "notes.docx"],
    chunk=IngestChunkOptions(max_words=512, sentence_overlap=1),
)

status = client.v2.get_ingest_job(job.job_id)   # poll
if status.status == "completed":
    print(status.output_url)  # JSONL

client.v2.list_ingest_jobs(limit=20)
client.v2.cancel_ingest_job(job.job_id)         # idempotent

# Webhook signing (HMAC):
secret = client.v2.get_webhook_secret()
print(secret.secret, secret.signature_header)
client.v2.rotate_webhook_secret()               # invalidates old secret
client.v2.retry_ingest_webhook(job.job_id)      # re-deliver

Watch — recurring change monitoring

watcher = client.v2.create_watcher(
    "https://example.com/pricing",
    frequency_minutes=60,        # hourly floor
    diff_mode="auto",            # auto | text | structured | tables | metadata
    webhook_url="https://my.app/hooks/changes",
    notify_email=True,
)

client.v2.list_watchers()
client.v2.get_watcher(watcher.watcher_id)
client.v2.get_watcher_snapshots(watcher.watcher_id, limit=10)
client.v2.update_watcher(watcher.watcher_id, status="paused")
client.v2.update_watcher(watcher.watcher_id, webhook_url="")  # clears webhook
client.v2.delete_watcher(watcher.watcher_id)  # soft-delete, idempotent

V2 error handling

from enconvert import QuotaError

try:
    client.v2.ingest(mode="sitemap", url="https://example.com")
except QuotaError:
    print("Upgrade plan or wait for quota reset")

File conversion

The same key also converts 40+ formats. Two "anything → X" endpoints auto-detect the input; the format-specific methods below give you a validated, typed path.

Anything to Markdown / PDF

# Any document -> clean Markdown (a RAG-ingestion building block):
client.convert_to_markdown("report.docx", save_to="report.md")
# PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, and legacy/ODF office. (Images not supported.)

# Almost anything -> PDF:
client.convert_to_pdf("slides.pptx", save_to="slides.pdf")
# office/ODF/Pages/Numbers/RTF/CSV, HTML, Markdown, text, images, SVG, EPUB, or a PDF passthrough.

# Only pdf_options.grayscale is honored on this endpoint:
from enconvert import PdfOptions

client.convert_to_pdf("scan.pdf", pdf_options=PdfOptions(grayscale=True), save_to="gray.pdf")

Image Conversion

result = client.convert_image(
    "photo.heic",
    output_format="webp",
    save_to="photo.webp",
)

Any pair among jpeg, png, svg, heic, webp — plus PDF rasterization:

client.convert_image("scan.pdf", output_format="jpeg", save_to="scan.jpeg")

Document Conversion

client.convert_document("report.docx", save_to="report.pdf")
client.convert_document("data.json", output_format="yaml", save_to="data.yaml")
client.convert_document("notes.md", output_format="html", save_to="notes.html")

Supported inputs: doc/docx, xls/xlsx, ppt/pptx, odt, ods, odp, ots, pages, numbers, html, markdown, csv, json, xml, yaml, toml. (EPUB → use convert_to_pdf / convert_to_markdown.)

The SDK validates every {input}-to-{output} pair against the conversions the API actually implements and raises immediately — with the list of valid outputs for that input — instead of sending a doomed request. Introspect programmatically:

from enconvert import IMPLEMENTED_CONVERSIONS, valid_outputs_for

valid_outputs_for("json")  # ["csv", "toml", "xml", "yaml"]
valid_outputs_for("pdf")   # ["jpeg"]

Supported conversions

Input Outputs
json csv, toml, xml, yaml
xml csv, json
yaml json
csv json, xml
toml json
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp each other (all 20 pairs)
pdf jpeg

URL to PDF / Screenshot / Markdown

client.convert_url_to_pdf("https://example.com", save_to="page.pdf")
client.convert_url_to_screenshot("https://example.com", viewport_width=1440, save_to="shot.png")
client.convert_url_to_markdown("https://example.com/article", save_to="article.md")

Extract clean GitHub-Flavored Markdown from any URL — strips nav/footer/ads/scripts, keeps the main article content, and adds YAML frontmatter (title, description, url, links, images).

Website to PDF / Screenshot (whole-site batch)

Discover every page of a website (via sitemap, or full crawl on higher plans), convert each one in the background, and receive a single ZIP. Requires a private API key with crawl access.

batch = client.convert_website_to_pdf(
    "https://example.com",
    crawl_mode="sitemap",             # "auto" (default) | "sitemap" | "full"
    exclude_patterns=["/blog/tag/"],  # full crawl mode only
)
print(batch.batch_id, batch.url_count, batch.discovery_method)

# Block until done and save the ZIP:
status = client.wait_for_batch(batch.batch_id, save_to="site.zip")
print(status.completed, "of", status.total, "pages converted")

# Or poll yourself:
s = client.get_batch_status(batch.batch_id)
if s.status != "processing":
    print(s.zip_download_url)

convert_website_to_screenshot works the same way and produces a ZIP of PNGs.

PDF options & authenticated pages

from enconvert import BrowserCookie, HttpBasicAuth, PdfHeaderFooter, PdfMargins, PdfOptions

client.convert_url_to_pdf(
    "https://internal.example.com/report",
    pdf_options=PdfOptions(
        page_size="A4",              # or custom dimensions via page_width + page_height
        orientation="landscape",
        margins=PdfMargins(top=10, bottom=10, left=15, right=15),
        header=PdfHeaderFooter(content="Quarterly Report", height=15),
        footer=PdfHeaderFooter(content="Confidential", height=12),
    ),
    auth=HttpBasicAuth(username="user", password="pass"),     # or cookies / headers, plan-gated
    cookies=[BrowserCookie(name="session", value="abc123", domain="internal.example.com")],
    headers={"X-Tenant": "acme"},
    save_to="report.pdf",
)

Do not combine auth with an Authorization header — the API rejects the conflict.

Job status (async polling)

status = client.get_job_status("job_abc123")
if status.status == "success":
    print(status.presigned_url)

Error Handling

from enconvert import Enconvert, APIError, AuthenticationError, RateLimitError, QuotaError

try:
    client.v2.perceive("https://example.com")
except AuthenticationError:
    print("Invalid API key")
except QuotaError:
    print("Plan feature off or quota exhausted")
except RateLimitError:
    print("Too many requests — slow down")
except APIError as e:
    print(f"API error [{e.status_code}]: {e.message}")

Configuration

client = Enconvert(
    api_key="sk_...",
    timeout=300.0,  # seconds, default
    base_url="https://api.enconvert.com",  # default
)

Get an API Key

Sign up at enconvert.com. Free tier: 100 ops/month, no credit card.

License

MIT

Metadata

Release files for enconvert 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for enconvert 0.1.1
File Size Uploaded Provenance Actions
enconvert-0.1.1.tar.gz 36.2 kB Without provenance Details for enconvert-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for enconvert 0.1.1
File Interpreter ABI Platform Actions
enconvert-0.1.1-py3-none-any.whl Python 3 none any Details for enconvert-0.1.1-py3-none-any.whl

Total release size: 69.9 kB

Release files / enconvert-0.1.1.tar.gz

Download URL enconvert-0.1.1.tar.gz
Size 36.2 kB
Tags Source
SHA-256 checksum
How to use checksums
dff64389ce26d76f00583f34a5cd6b9ac9d6b32a366095ade6477d9e2ac70354
BLAKE2b-256 checksum
How to use checksums
e9e76272a0ecc5e890d5c93411dc2e8c51bb5f090fafe069a39fde0261065122
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Release files / enconvert-0.1.1-py3-none-any.whl

Download URL enconvert-0.1.1-py3-none-any.whl
Size 33.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0cd206e1683ad81d731a09de6002083fcb15f6cc50179104b6a82985b583ad21
BLAKE2b-256 checksum
How to use checksums
85aabc1468dc3e2a36e4972312c33508376d3c822e4b87c425679d87e7660c6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Security for enconvert 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page