Metadata-Version: 2.4
Name: enconvert
Version: 0.0.1
Summary: Python SDK for the Enconvert file conversion API
Author-email: Enconvert <support@enconvert.com>
License-Expression: MIT
Project-URL: Homepage, https://enconvert.com
Project-URL: Repository, https://github.com/conversionapi/python-sdk
Keywords: enconvert,pdf,screenshot,conversion,html-to-pdf,url-to-pdf,url-to-screenshot,url-to-markdown,image-convert,document-convert,heic-to-webp,docx-to-pdf,api,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Dynamic: license-file

# enconvert

Python SDK for the [Enconvert](https://enconvert.com) file conversion API.

Convert URLs to PDFs, capture screenshots, extract Markdown, crawl whole websites, transform images, and convert documents — all with a single API call. Python 3.9+.

## Install

```bash
pip install enconvert
```

## Quick Start

```python
from enconvert import Enconvert

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

### URL to PDF

```python
result = client.convert_url_to_pdf("https://example.com", save_to="page.pdf")
print(result.presigned_url)
```

### URL to Screenshot

```python
result = client.convert_url_to_screenshot(
    "https://example.com",
    viewport_width=1440,
    save_to="screenshot.png",
)
```

### URL to Markdown

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).

```python
result = client.convert_url_to_markdown(
    "https://example.com/article",
    save_to="article.md",
)
```

### Website to PDF / Screenshot (whole-site batch)

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

```python
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.

### Image Conversion

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

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

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

### Document Conversion

```python
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`, `epub`, `html`, `markdown`, `csv`, `json`, `xml`, `yaml`, `toml`

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:

```python
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, epub | pdf |
| jpeg, png, svg, heic, webp | each other (all 20 pairs) |
| pdf | jpeg |

### Job Status (async polling)

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

## PDF Options

```python
from enconvert import PdfHeaderFooter, PdfMargins, PdfOptions

result = client.convert_url_to_pdf(
    "https://example.com",
    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),
    ),
    save_to="report.pdf",
)
```

## Authenticated Pages (plan-gated)

All URL and website conversions accept HTTP Basic Auth, cookies, and custom headers for pages behind a login:

```python
from enconvert import BrowserCookie, HttpBasicAuth

client.convert_url_to_pdf(
    "https://internal.example.com/report",
    auth=HttpBasicAuth(username="user", password="pass"),
    # or cookies / headers:
    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.

## Error Handling

```python
from enconvert import Enconvert, APIError, AuthenticationError, RateLimitError

try:
    client.convert_url_to_pdf("https://example.com")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError:
    print("Too many requests — slow down")
except APIError as e:
    print(f"API error [{e.status_code}]: {e.message}")
```

## Configuration

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

## V2 API (`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).

### Perceive — render a URL into artifacts

```python
op = client.v2.perceive(
    "https://example.com",
    outputs=["markdown", "screenshot", "structured"],
    extract=["tables", "metadata"],
)
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 (<=10 URLs runs inline; larger returns "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)

```python
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

```python
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.outputs if hit.perceive else None)
```

### Distill — schema-driven structured extraction

```python
from enconvert import CssField, CssSchema

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:
from enconvert import DistillDiscoverFrom

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 to RAG-ready JSONL (always async)

```python
from enconvert import IngestChunkOptions

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",
)

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

```python
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

```python
from enconvert import QuotaError

try:
    client.v2.ingest(urls=["https://example.com"])
except QuotaError:
    print("Upgrade plan or wait for quota reset")
```

## Get an API Key

Sign up at [enconvert.com](https://enconvert.com) to get your API key.

## License

MIT
