Metadata-Version: 2.1
Name: zappyquote
Version: 0.2.1
Summary: Official Zappy API client for Python - a thin wrapper over the documented Zappy API v1.
Author: Zappy
License: MIT
Project-URL: Homepage, https://zappyquote.com/developers/
Project-URL: Documentation, https://zappyquote.com/developers/
Project-URL: Issues, https://zappyquote.com/contact.html
Keywords: zappy,zappyquote,ai,image-generation,api,sdk
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE

# zappyquote

The official Python client for the Zappy API v1 - a thin wrapper over the documented HTTP API
(https://zappyquote.com/developers/). The server stays authoritative for pricing, caps, idempotency and ownership.

Version 0.2.1 · contract `openapi-1.0.0@9dff9fdfbcd8c1bd` · Python 3.9+ · standard library only · MIT

```bash
# official download (always available):
curl -O https://zappyquote.com/dl/zappyquote.py      # then: import zappyquote
# PyPI, ONLY once https://zappyquote.com/developers/ lists zappyquote as published by Zappy:
#   pip install zappyquote
```

```python
from zappyquote import Zappy, ZappyError, new_idempotency_key

zappy = Zappy()                                    # reads ZAPPY_API_KEY; or Zappy(api_key=...)
caps = zappy.capabilities()["capabilities"]        # free
quote = zappy.quote("zappy-image-pro")             # free, reserves nothing

# BILLABLE. max_credits is a hard ceiling; the idempotency key makes a replay return the same job, never a second charge.
key = new_idempotency_key("order-123")
r = zappy.create_image("a lighthouse at dusk", max_credits=quote["credits_quoted"], idempotency_key=key)
job = r if r["status"] == "succeeded" else zappy.wait_for_job(r["job_id"])   # 202 => poll (free)
asset = zappy.get_asset(job["assets"][0]["asset_id"])                        # short-lived signed URL on Zappy's host
```

Every response is a `dict` with `.meta` (`request_id`, `rate_limit`, `retry_after`, `status`). Failures raise a
`ZappyError` subclass with `code`, `status`, `request_id`, `retry_after`: `ZappyAuthError`, `ZappyRateLimitError`,
`ZappySpendError`, `ZappyNotFoundError`, `ZappyUnavailableError`, `ZappyTransportError`. Messages never contain a credential.

```python
from zappyquote import verify_webhook_signature
ok = verify_webhook_signature(secret, request.headers["Zappy-Signature"], request.get_data())   # exact raw bytes
```

`pkce_pair()` / `authorize_url(...)` build the OAuth authorization request for an app you registered in Zappy; consent
and token exchange stay on Zappy's authorization server. Paid calls are never retried by this package.

## Name

The distribution and the import are both `zappyquote` (`import zappyquote`). The shorter name `zappy` is used on PyPI by an unrelated project whose distribution also installs a top-level `zappy` package; sharing that name would let either install silently replace the other. Version 0.2.0 is the first version under this name (0.1.0 was only ever a direct download, `/dl/zappy.py`, and is retired).

### Migrating from the retired `/dl/zappy.py`

1. Download `https://zappyquote.com/dl/zappyquote.py` next to your code.
2. Change `import zappy` / `from zappy import ...` to `import zappyquote` / `from zappyquote import ...` (the API is unchanged).
3. Delete your project-local `zappy.py` once you have confirmed it is the Zappy SDK (its docstring starts with
   "zappy - the official Python client for the Zappy API v1"). Left in place, it would shadow any other `zappy` package.

Registry packages are official only if https://zappyquote.com/developers/ lists them as published; until then use the download, and never install a look-alike name.
