Metadata-Version: 2.5
Name: dokaz-api
Version: 0.1.0
Summary: Official client for the Dokaz API: invoice PDFs, QR codes, barcodes, Markdown to PDF, CSV/JSON/Excel, calendar .ics files, image metadata removal, email verification, site intel and text AI.
Project-URL: Homepage, https://api.dokaz.net
Project-URL: Documentation, https://api.dokaz.net/docs
Project-URL: Pricing, https://api.dokaz.net/#pricing
Project-URL: OpenAPI, https://api.dokaz.net/openapi.json
Author: Dokaz Industries
License-Expression: MIT
License-File: LICENSE
Keywords: api,barcode,csv,dokaz,email-verification,exif,ics,invoice,markdown,pdf,qr-code,xlsx
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: 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: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# dokaz-api

The official Python client for the [Dokaz API](https://api.dokaz.net): invoice PDFs, Markdown to
PDF, QR codes and barcodes, CSV/JSON/Excel conversion, calendar (.ics) files, photo metadata
removal, email verification, website intel and text AI, all from one key.

- Standard library only (urllib). No dependencies.
- Python 3.9+, fully typed (`py.typed`, TypedDicts for requests and responses).
- One method per API endpoint. PDFs, PNGs, JPEGs and spreadsheets come back as `bytes`; SVG, CSV
  and iCalendar as `str`; everything else as parsed JSON.

```sh
pip install dokaz-api
```

```python
from dokaz_api import Dokaz
```

## Free tier and plans

**100 calls a day are free with no key and no signup.** Just call it. For more, get a key at
**[api.dokaz.net/#pricing](https://api.dokaz.net/#pricing)**: Starter $9/month (10,000 calls),
Pro $29/month (100,000), Business $79/month (500,000). Every plan covers every endpoint. The key
is shown straight after checkout.

```python
dokaz = Dokaz()               # uses DOKAZ_API_KEY if set, else the free tier
dokaz = Dokaz("dk_...")       # or pass the key
```

## Quick start

### Invoice PDF

```python
pdf = dokaz.invoice_pdf({
    "number": "INV-2026-0142",
    "date": "2026-09-06",
    "due": "2026-10-06",
    "from": {"name": "Northwind Studio LLC", "address": ["1200 Market Street", "San Francisco, CA 94102"]},
    "to": {"name": "Acme Robotics Inc.", "email": "ap@acme-robotics.example"},
    "items": [
        {"description": "Brand identity design", "qty": 1, "unit_price": 2400},
        {"description": "Front-end implementation (hourly)", "qty": 32, "unit_price": 110},
    ],
    "tax_rate": 8.25,
})
open("invoice.pdf", "wb").write(pdf)

totals = dokaz.invoice_preview(same_body)   # {"subtotal", "tax", "total", "line_items", ...}
```

### Markdown to PDF

```python
pdf = dokaz.pdf_markdown("# 2.4\n\n- Faster **exports**", title="Release notes", page_size="a4")
```

### QR codes and barcodes

```python
svg = dokaz.qr("https://example.com", size=256, ec="Q")        # str
png = dokaz.qr("https://example.com", format="png")            # bytes
wifi = dokaz.qr_wifi("Cafe Guest", password="latte123", format="png")
card = dokaz.qr_vcard("Ada Lovelace", email="ada@example.com")
ean = dokaz.barcode("400638133393", type="ean13", text=True)    # SVG str
```

### CSV, JSON and Excel

```python
rows = dokaz.convert_csv_to_json('name,city\n"Hopper, Grace",Arlington')   # [{"name": ..., "city": ...}]
csv = dokaz.convert_json_to_csv([{"name": "Ada", "born": 1815}])           # RFC 4180 text
xlsx = dokaz.convert_json_to_xlsx({"rows": [{"order": "A-1001", "total": 129.5}], "sheet_name": "Orders"})
```

### Calendar (.ics) files

```python
ics = dokaz.calendar_ics_post({
    "title": "Kitchen remodel consult",
    "start": "2026-10-05T14:00:00-07:00",
    "end": "2026-10-05T15:00:00-07:00",
    "reminder_minutes": 60,
})
# An "Add to calendar" link for an email, with no request made and no key in it:
link = dokaz.calendar_ics_link("Webinar", "2026-10-08T17:00:00Z")
```

### Strip photo metadata (EXIF, GPS, XMP)

```python
clean = dokaz.image_strip(open("photo.jpg", "rb").read())   # same pixels, no metadata
no_icc = dokaz.image_strip(data, keep_icc=False)
```

### Email verification and website intel

```python
r = dokaz.email_verify("sales@gmial.com")        # r["valid"], r["score"], r["checks"]["typo_suggestion"]
batch = dokaz.email_verify_batch(["a@example.org", "b@mailinator.com"])   # up to 50, one call
site = dokaz.site_intel("https://example.com")    # title, OpenGraph, tech, contacts, socials
```

### Text AI

```python
dokaz.text_summarize(text, sentences=2, style="bullets")
dokaz.text_sentiment(text)
dokaz.text_keywords(text, max=10)
dokaz.text_classify(text, ["billing", "bug report", "other"])
dokaz.text_extract(text, ["name", "company", "phone"])
dokaz.text_rewrite(text, "professional")
```

## Errors

Every non-2xx response raises `DokazError` carrying the API's error body:

```python
from dokaz_api import DokazError

try:
    dokaz.invoice_pdf(body)
except DokazError as e:
    e.status        # 400, 401, 404, 413, 429, 503 ...
    e.error         # the API's message (e.body is the whole JSON)
    e.errors        # every validation problem, when listed
    e.upgrade       # on 429: where to upgrade
    e.retry_after   # on 503 (text AI capacity): seconds to wait
```

A request that gets no response at all (DNS, connection, timeout) raises `DokazConnectionError`,
a `DokazError` with `status` 0.

## Quota, options and raw responses

```python
dokaz = Dokaz(
    "dk_...",              # default: DOKAZ_API_KEY; None forces the anonymous free tier
    timeout=30,            # seconds, default 60
    on_response=lambda i: print(i.operation, i.status, i.quota),   # Quota(plan, limit, used)
)

# Any call with its status and headers (x-pdf-pages, x-csv-rows, x-metadata-removed ...):
r = dokaz.request("pdf_markdown", json={"markdown": "# Hi"})
r.headers["x-pdf-pages"], r.data
```

## Methods

| Method | Endpoint | Returns |
|---|---|---|
| `invoice_pdf(invoice)` | `POST /v1/invoice/pdf` | `bytes` (PDF) |
| `invoice_preview(invoice)` | `POST /v1/invoice/preview` | totals |
| `invoice_sample()` | `GET /v1/invoice/sample` | `bytes` (PDF) |
| `email_verify(email)` | `GET /v1/email/verify` | result |
| `email_verify_post(email)` | `POST /v1/email/verify` | result |
| `email_verify_batch(emails)` | `POST /v1/email/verify/batch` | `{"count", "results"}` |
| `site_intel(url)` | `GET /v1/site/intel` | dict |
| `site_intel_post(url)` | `POST /v1/site/intel` | dict |
| `text_summarize` `text_sentiment` `text_keywords` `text_classify` `text_extract` `text_rewrite` | `POST /v1/text/*` | dict |
| `qr(data, ...)` / `qr_post(data, ...)` | `GET` / `POST /v1/qr` | SVG `str`, PNG `bytes` or matrix |
| `qr_wifi(ssid, ...)` | `GET /v1/qr/wifi` | as `qr` |
| `qr_vcard(name, ...)` | `GET /v1/qr/vcard` | as `qr` |
| `barcode(data, ...)` | `GET /v1/barcode` | SVG `str` |
| `pdf_markdown(markdown, ...)` | `POST /v1/pdf/markdown` | `bytes` (PDF) |
| `convert_csv_to_json(csv, ...)` | `POST /v1/convert/csv-to-json` | rows |
| `convert_json_to_csv(rows, ...)` | `POST /v1/convert/json-to-csv` | CSV `str` |
| `convert_json_to_xlsx(body)` | `POST /v1/convert/json-to-xlsx` | `bytes` (.xlsx) |
| `calendar_ics(title, start, ...)` | `GET /v1/calendar/ics` | iCalendar `str` |
| `calendar_ics_post(event)` | `POST /v1/calendar/ics` | iCalendar `str` |
| `image_strip(data, ...)` | `POST /v1/image/strip` | `bytes` (image) |

Field names are the API's own, so the guides at [api.dokaz.net/docs](https://api.dokaz.net/docs)
and the [OpenAPI document](https://api.dokaz.net/openapi.json) apply unchanged.

## License

MIT
