Metadata-Version: 2.5
Name: formatjet
Version: 0.1.0
Summary: Convert documents, images, data and PDFs with the FormatJet API.
Project-URL: Homepage, https://formatjet.com/api
Project-URL: Documentation, https://formatjet.com/docs
Author: Taprobane Labs
License-Expression: MIT
License-File: LICENSE
Keywords: api,docx,file conversion,formatjet,pdf
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Office/Business
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: attrs>=23.1
Requires-Dist: httpx<1,>=0.27
Description-Content-Type: text/markdown

# FormatJet for Python

Convert documents, images, data and PDFs with the [FormatJet API](https://formatjet.com/docs): Word to PDF, PDF to Word, HEIC to JPG, Excel to CSV, Merge PDF and [the rest](https://formatjet.com/api).

```sh
pip install formatjet
```

Python 3.11 or later.

## Convert a file

Make an API key in the [dashboard](https://formatjet.com/app/) and set `FORMATJET_API_KEY`. A test key (`fj_test_…`) converts for free with smaller limits.

```python
from formatjet import FormatJet

formatjet = FormatJet()  # reads FORMATJET_API_KEY

result = formatjet.convert("report.docx", conversion="word-to-pdf")
result.file.save("report.pdf")
```

`convert()` sends the file in one request (up to 25 MB) and returns the converted files: `result.files` holds several for Split PDF, a PDF's pages as images or a workbook's sheets as CSV, and `file.save()` with no path writes each under its own name. Name the conversion by its slug (`GET /v1/operations` lists them), or say only the format to make: `to="pdf"`. Options go in `options`, such as `formatjet.convert("scan.pdf", conversion="pdf-to-jpg", options={"dpi": 300})`.

A file is a path, its bytes, a binary file object, or `(filename, bytes)` to name the output after the input.

## Larger files

`upload_and_convert()` uploads the file straight to storage first, so it takes files up to your plan's limit, then makes a job, waits for it and downloads the result:

```python
result = formatjet.upload_and_convert(
    "scan.pdf",
    conversion="pdf-to-word",
    metadata={"order": "8812"},
    on_progress=lambda job: print(job.progress),
)
```

Files to join (Merge PDF, Image to PDF) go as a list, in order: `formatjet.convert(["first.pdf", "second.pdf"], conversion="merge-pdf")`.

## Jobs

```python
job = formatjet.jobs.create(
    {
        "input": {"url": "https://example.com/report.docx"},
        "steps": [{"op": "convert", "conversion": "word-to-pdf"}],
        "webhook": "https://example.com/hooks/formatjet",
    }
)
done = formatjet.jobs.wait(job.id)
formatjet.download(done.outputs[0]).save()

for summary in formatjet.jobs.iterate():
    print(summary.id, summary.status)
formatjet.jobs.delete(job.id)  # files deleted now; returns the signed receipt
```

`jobs.get()`, `jobs.list()`, `jobs.cancel()` and `upload()` cover the rest. Every endpoint is also in `formatjet.openapi`, generated from the OpenAPI document; pass `formatjet.api` as its client:

```python
from formatjet.openapi.api.account import get_account

account = get_account.sync(client=formatjet.api)
```

## Errors and retries

Failed calls and failed jobs raise `FormatJetError`. Branch on its `code` ([the list](https://formatjet.com/docs/errors)); `request_id` identifies the request if you contact us.

```python
from formatjet import FormatJetError

try:
    formatjet.convert("report.docx", conversion="word-to-pdf")
except FormatJetError as error:
    if error.code == "insufficient_credits":
        ...
```

The client retries 429s, 5xx answers and network errors twice, honouring `Retry-After`. Each POST carries an `Idempotency-Key`, the same on every retry, so a retry never makes a second job. Set `max_retries` and `timeout` in the constructor; use the client as a context manager, or call `close()`, to free its connections.

## Webhooks

Check each delivery's signature with the raw body, before parsing it:

```python
from formatjet import WebhookSignatureError, parse_webhook

try:
    event = parse_webhook(request.body, request.headers.get("FormatJet-Signature"), secret)
except WebhookSignatureError:
    return Response(status=400)
if event["type"] == "job.completed":
    print(event["data"]["job"]["outputs"])
```

`verify_webhook()` returns the result instead, with the reason when it fails.

## Licence

MIT
