Skip to main content

Enconvert SDK — read any page or file into agent-ready Markdown/JSON/screenshots, every read scored. V2 perception + file conversion.

Project description

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.

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)

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

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

enconvert-0.0.2.tar.gz (33.7 kB view details)

Uploaded Source

Built Distribution

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

enconvert-0.0.2-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

Details for the file enconvert-0.0.2.tar.gz.

File metadata

  • Download URL: enconvert-0.0.2.tar.gz
  • Upload date:
  • Size: 33.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for enconvert-0.0.2.tar.gz
Algorithm Hash digest
SHA256 abfb8d6e4522c1f5dca800a34aad263db71af3700bf430cfbbfe1e04a649708e
MD5 2435d3951b9f4ac9503c8c5693d074b4
BLAKE2b-256 d70526b89b2f6ef50f9d9c7c7e282de6d9cb8b1bb3db866402ce6f0cb79d636c

See more details on using hashes here.

File details

Details for the file enconvert-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: enconvert-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 31.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for enconvert-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 77f8b82a9acdc8c870253570c465d0a4d8f0ec05ec115b727779efaf0c0ac80a
MD5 3fd8a829d78410485e1290e5c127c51d
BLAKE2b-256 20b3d96352d82bce594880f737afdc3813d264a17d28df2b84a3de27828efc67

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