popnow 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- popnow-0.1.0/.gitignore +22 -0
- popnow-0.1.0/CHANGELOG.md +22 -0
- popnow-0.1.0/LICENSE +21 -0
- popnow-0.1.0/PKG-INFO +347 -0
- popnow-0.1.0/README.md +310 -0
- popnow-0.1.0/examples/async_batch.py +33 -0
- popnow-0.1.0/examples/generate_image.py +19 -0
- popnow-0.1.0/examples/image_to_video.py +31 -0
- popnow-0.1.0/examples/quote_and_create.py +37 -0
- popnow-0.1.0/examples/save_to_library.py +24 -0
- popnow-0.1.0/examples/text_to_speech.py +28 -0
- popnow-0.1.0/examples/webhook_receiver.py +40 -0
- popnow-0.1.0/pyproject.toml +68 -0
- popnow-0.1.0/src/popnow/__init__.py +95 -0
- popnow-0.1.0/src/popnow/_base_client.py +296 -0
- popnow-0.1.0/src/popnow/_client.py +160 -0
- popnow-0.1.0/src/popnow/_exceptions.py +277 -0
- popnow-0.1.0/src/popnow/_version.py +1 -0
- popnow-0.1.0/src/popnow/py.typed +0 -0
- popnow-0.1.0/src/popnow/resources/__init__.py +1 -0
- popnow-0.1.0/src/popnow/resources/account.py +54 -0
- popnow-0.1.0/src/popnow/resources/assets.py +322 -0
- popnow-0.1.0/src/popnow/resources/files.py +324 -0
- popnow-0.1.0/src/popnow/resources/generate.py +250 -0
- popnow-0.1.0/src/popnow/resources/jobs.py +412 -0
- popnow-0.1.0/src/popnow/resources/models.py +103 -0
- popnow-0.1.0/src/popnow/types.py +519 -0
- popnow-0.1.0/src/popnow/webhooks.py +192 -0
- popnow-0.1.0/tests/__init__.py +0 -0
- popnow-0.1.0/tests/conftest.py +92 -0
- popnow-0.1.0/tests/test_async.py +103 -0
- popnow-0.1.0/tests/test_client.py +178 -0
- popnow-0.1.0/tests/test_errors.py +128 -0
- popnow-0.1.0/tests/test_files_assets.py +197 -0
- popnow-0.1.0/tests/test_generate.py +115 -0
- popnow-0.1.0/tests/test_jobs.py +233 -0
- popnow-0.1.0/tests/test_webhooks.py +139 -0
popnow-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
# Editors / OS
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
19
|
+
.DS_Store
|
|
20
|
+
|
|
21
|
+
# Local metadata (not part of the package)
|
|
22
|
+
REPO_META.json
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-10-19
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `PopNow` and `AsyncPopNow` clients (`httpx`), configured by `api_key` / `POPNOW_API_KEY`,
|
|
12
|
+
`base_url` / `POPNOW_BASE_URL`, `timeout`, `max_retries`, `language`, `http_client`.
|
|
13
|
+
- `models.list()` / `models.get()`.
|
|
14
|
+
- `jobs.quote()`, `create()` (idempotency key, quote lock, retention, server-side `wait`), `get()`,
|
|
15
|
+
`list()` / `iter()`, `wait()` with back-off, `create_and_wait()`, `retry()`, `purge()`, `download_url()`.
|
|
16
|
+
- `files.upload()` (upload ticket, PUT, commit), `create_upload_url()`, `commit()`, `import_lora()`, `download()`.
|
|
17
|
+
- `assets.*`: list, iterate, get, save (task output, upload, URL), update, delete, usage, characters.
|
|
18
|
+
- `account.balance()` and `account.usage()`.
|
|
19
|
+
- Helpers: `images.generate()` / `edit()`, `videos.generate()`, `audio.speech()` / `music()` / `transcribe()`.
|
|
20
|
+
- Typed exceptions mapped from the error envelope, with `code`, `status_code`, `request_id` and `reason`.
|
|
21
|
+
- Automatic retries for network errors and codes `429`, `500`, `503`, `50301`, honouring `Retry-After`.
|
|
22
|
+
- `popnow.webhooks`: task callback (`X-Webhook-Signature`) and Standard Webhooks endpoint verification.
|
popnow-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PopNow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
popnow-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: popnow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typed Python SDK for the PopNow API: AI video, image, music and speech generation (Kling, Seedance, Wan, Nano Banana, GPT Image, Suno and more).
|
|
5
|
+
Project-URL: Homepage, https://popnow.ai
|
|
6
|
+
Project-URL: Documentation, https://popnow.ai/docs
|
|
7
|
+
Project-URL: API reference, https://popnow.ai/docs/openapi.yaml
|
|
8
|
+
Project-URL: Source, https://github.com/popnow-ai/popnow-python
|
|
9
|
+
Project-URL: Issues, https://github.com/popnow-ai/popnow-python/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/popnow-ai/popnow-python/blob/main/CHANGELOG.md
|
|
11
|
+
Author: PopNow
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: ai-image-generator,ai-music,ai-video-generator,api-client,generative-ai,image-to-video,popnow,sdk,text-to-image,text-to-speech,text-to-video
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
27
|
+
Classifier: Topic :: Multimedia :: Video
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Classifier: Typing :: Typed
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Requires-Dist: httpx<1,>=0.25
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: anyio>=3.7; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
35
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<div align="center">
|
|
39
|
+
<a href="https://popnow.ai">
|
|
40
|
+
<picture>
|
|
41
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/popnow-ai/popnow-python/main/assets/popnow-wordmark-light.png">
|
|
42
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/popnow-ai/popnow-python/main/assets/popnow-wordmark.png">
|
|
43
|
+
<img alt="PopNow" src="https://raw.githubusercontent.com/popnow-ai/popnow-python/main/assets/popnow-wordmark.png" width="260">
|
|
44
|
+
</picture>
|
|
45
|
+
</a>
|
|
46
|
+
|
|
47
|
+
<h1>PopNow Python SDK: AI Video, Image and Music Generation API</h1>
|
|
48
|
+
|
|
49
|
+
<p><strong>popnow</strong> is a typed Python client for the PopNow API that turns text, images and audio into AI videos, images, music and speech, using the same models, prices and credits as <a href="https://popnow.ai">popnow.ai</a>.</p>
|
|
50
|
+
|
|
51
|
+
<p>
|
|
52
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
|
53
|
+
<img alt="Python 3.9 to 3.13" src="https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-3776AB.svg?logo=python&logoColor=white">
|
|
54
|
+
<a href="https://github.com/popnow-ai/popnow-python/actions/workflows/tests.yml"><img alt="Tests" src="https://github.com/popnow-ai/popnow-python/actions/workflows/tests.yml/badge.svg"></a>
|
|
55
|
+
<a href="https://popnow.ai"><img alt="Website: popnow.ai" src="https://img.shields.io/badge/site-popnow.ai-ff5a36.svg"></a>
|
|
56
|
+
<a href="https://popnow.ai/docs"><img alt="Docs" src="https://img.shields.io/badge/docs-popnow.ai%2Fdocs-111111.svg"></a>
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
PopNow is a browser-based AI studio for videos, images and music: image-to-video, text-to-video, AI images, music and speech, one-tap trending video effects and image styles. This SDK wraps its developer API (`https://api.popnow.ai/api/v1`) so you can call Kling, Seedance, Wan, Vidu, MiniMax, Nano Banana, GPT Image, Flux, Suno and more from Python. Credit packs, no subscription; failed runs are refunded. *Make it pop.*
|
|
61
|
+
|
|
62
|
+
- Sync **and** async clients (`PopNow`, `AsyncPopNow`) built on `httpx`, the only dependency.
|
|
63
|
+
- Typed results (`py.typed`) with snake_case attributes, plus the raw JSON on every object.
|
|
64
|
+
- One-line helpers: `client.images.generate()`, `client.videos.generate()`, `client.audio.speech()`.
|
|
65
|
+
- Safe retries: automatic `Idempotency-Key`, back-off on `429` / `500` / `503` honouring `Retry-After`.
|
|
66
|
+
- Local file uploads, asset library, task history, usage, balance and webhook signature verification.
|
|
67
|
+
|
|
68
|
+
## Quickstart
|
|
69
|
+
|
|
70
|
+
**1. Install** (Python 3.9+):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install "git+https://github.com/popnow-ai/popnow-python"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A PyPI release is coming; until then, install from GitHub as above (pin a commit or tag with `@<ref>` for reproducible builds).
|
|
77
|
+
|
|
78
|
+
**2. Create an API key** in the [API console](https://popnow.ai/api/console/keys) and export it:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
export POPNOW_API_KEY="sk-popnow-..."
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**3. Generate:**
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from popnow import PopNow
|
|
88
|
+
|
|
89
|
+
client = PopNow() # reads POPNOW_API_KEY
|
|
90
|
+
|
|
91
|
+
task = client.images.generate(
|
|
92
|
+
model="alibaba/z-image-turbo/text-to-image",
|
|
93
|
+
prompt="A small red fox curled up in a teacup, soft morning light",
|
|
94
|
+
)
|
|
95
|
+
print(task.state, task.credits, task.urls[0])
|
|
96
|
+
client.files.download(task.assets[0], "fox.png")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
That image costs a couple of credits (1 credit = $0.01). Buy credits on the [pricing page](https://popnow.ai/pricing); API calls and the website share one wallet.
|
|
100
|
+
|
|
101
|
+
## What you can call
|
|
102
|
+
|
|
103
|
+
| Area | SDK | API endpoint |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| Models and prices | `client.models.list()`, `client.models.get(id)` | `GET /api/v1/models`, `GET /api/v1/models/{model}` |
|
|
106
|
+
| Price a request | `client.jobs.quote()` | `POST /api/v1/jobs/quote` |
|
|
107
|
+
| Create a task | `client.jobs.create()` | `POST /api/v1/jobs/createTask` |
|
|
108
|
+
| Read / wait | `client.jobs.get()`, `client.jobs.wait()` | `GET /api/v1/jobs/recordInfo` |
|
|
109
|
+
| Task history | `client.jobs.list()`, `client.jobs.iter()` | `GET /api/v1/jobs` |
|
|
110
|
+
| Retry / purge | `client.jobs.retry()`, `client.jobs.purge()` | `POST /api/v1/jobs/retry`, `POST /api/v1/jobs/purge` |
|
|
111
|
+
| Download link | `client.jobs.download_url()` | `POST /api/v1/common/download-url` |
|
|
112
|
+
| Upload a file | `client.files.upload(path)` | `POST /api/v1/common/upload-url`, `PUT`, `POST /api/v1/files/{id}/commit` |
|
|
113
|
+
| Import a LoRA | `client.files.import_lora(url)` | `POST /api/v1/files/import` |
|
|
114
|
+
| Asset library | `client.assets.*` | `/api/v1/assets*` |
|
|
115
|
+
| Balance / usage | `client.account.balance()`, `client.account.usage()` | `GET /api/v1/chat/credit`, `GET /api/v1/usage` |
|
|
116
|
+
| Webhooks | `popnow.webhooks.verify_task_callback()` | `X-Webhook-Signature` header |
|
|
117
|
+
|
|
118
|
+
### Supported models
|
|
119
|
+
|
|
120
|
+
Model ids look like `<maker>/<model>/<task>`. Some examples; the live list with prices is `client.models.list()` or [popnow.ai/docs/models](https://popnow.ai/docs/models):
|
|
121
|
+
|
|
122
|
+
| Kind | Example model ids |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| Text-to-image | `alibaba/z-image-turbo/text-to-image`, `google/nano-banana-2.1/text-to-image`, `openai/gpt-image-2/text-to-image`, `black-forest-labs/flux-2-pro/text-to-image`, `bytedance/seedream-5.0-pro/text-to-image` |
|
|
125
|
+
| Image editing | `google/nano-banana-2.1/edit`, `openai/gpt-image-2/edit`, `popnow/background-remover-v1/edit` |
|
|
126
|
+
| Image-to-video | `alibaba/wan-3.0/image-to-video`, `kling/3.0/image-to-video`, `bytedance/seedance-2.0/image-to-video`, `vidu/q3/image-to-video`, `minimax/h3/image-to-video` |
|
|
127
|
+
| Text-to-video | `kling/3.0/text-to-video`, `alibaba/wan-3.0/text-to-video`, `bytedance/seedance-2.0/text-to-video` |
|
|
128
|
+
| Music | `suno/v6/text-to-audio`, `minimax/music-3.0/text-to-audio` |
|
|
129
|
+
| Speech | `google/gemini-3.8-flash-tts/text-to-speech`, `minimax/speech-2.8-hd/text-to-speech` |
|
|
130
|
+
| Transcription | `xai/grok-stt/speech-to-text` |
|
|
131
|
+
| Upscaling | `popnow/image-upscaler-v1/upscale`, `popnow/video-upscaler-v1/upscale` |
|
|
132
|
+
|
|
133
|
+
Every model has its own `input_schema` (`client.models.get(id).input_schema`). Unknown fields are rejected with a `400`, so typos fail fast.
|
|
134
|
+
|
|
135
|
+
## Usage
|
|
136
|
+
|
|
137
|
+
### Image to video
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
import pathlib
|
|
141
|
+
from popnow import PopNow
|
|
142
|
+
|
|
143
|
+
client = PopNow()
|
|
144
|
+
task = client.videos.generate(
|
|
145
|
+
model="alibaba/wan-3.0/image-to-video",
|
|
146
|
+
image_url=pathlib.Path("photo.jpg"), # a Path is uploaded for you; URLs work too
|
|
147
|
+
prompt="Waves roll in slowly, the camera drifts toward the lighthouse",
|
|
148
|
+
resolution="480p",
|
|
149
|
+
duration_seconds=5,
|
|
150
|
+
timeout=1800,
|
|
151
|
+
)
|
|
152
|
+
client.files.download(task.assets[0], "clip.mp4")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Music, speech and transcription
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
song = client.audio.music(model="suno/v6/text-to-audio", prompt="Upbeat lo-fi beat for a cooking video", instrumental=True)
|
|
159
|
+
voice = client.audio.speech(model="google/gemini-3.8-flash-tts/text-to-speech", text="Welcome back!", voice_id="Kore")
|
|
160
|
+
transcript = client.audio.transcribe(model="xai/grok-stt/speech-to-text", audio_url="https://example.com/interview.mp3").text
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The helpers fill the shared field names (`prompt`, `text`, `image_url`, `image_urls`, `video_url`, `audio_url`, `duration_seconds`, `resolution`, `aspect_ratio`) and pass every other keyword straight into the model `input`. They wait for the result and raise `TaskFailedError` unless the task succeeds.
|
|
164
|
+
|
|
165
|
+
### Low-level task API
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from popnow import PopNow, new_idempotency_key
|
|
169
|
+
|
|
170
|
+
client = PopNow()
|
|
171
|
+
body = {
|
|
172
|
+
"model": "alibaba/wan-3.0/image-to-video",
|
|
173
|
+
"input": {"image_url": "https://media.popnow.ai/movie/lighthouse.webp", "resolution": "720p", "duration_seconds": 5},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
quote = client.jobs.quote(**body) # nothing is created or held
|
|
177
|
+
created = client.jobs.create(
|
|
178
|
+
**body,
|
|
179
|
+
idempotency_key=new_idempotency_key(), # reuse it on every retry of this work
|
|
180
|
+
quote_id=quote.quote_id,
|
|
181
|
+
expected_credits=quote.max_credits, # QuoteMismatchError (40901) if the price changed
|
|
182
|
+
callback_url="https://your-app.example.com/hooks/popnow",
|
|
183
|
+
)
|
|
184
|
+
task = client.jobs.wait(created, timeout=1800) # polls with back-off: 2s, 3s, 4.5s ... up to 15s
|
|
185
|
+
if task.is_succeeded:
|
|
186
|
+
print(task.urls)
|
|
187
|
+
else:
|
|
188
|
+
print(task.state, task.error_code, task.error_message)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- `jobs.create(..., wait=60)` holds the connection up to 60 seconds; if the task finishes in time, `created.task` is the full record.
|
|
192
|
+
- `jobs.create_and_wait(model=..., input=...)` combines the server-side wait with polling.
|
|
193
|
+
- Task states: `queued`, `running`, then `succeeded`, `failed` or `expired`. Unknown states count as not final.
|
|
194
|
+
- `output.assets[].url` is a signed link valid for about 20 minutes. Store the `task_id`, not the link.
|
|
195
|
+
|
|
196
|
+
### Uploads and the asset library
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
uploaded = client.files.upload("reference.png") # popnow://f/fil_...
|
|
200
|
+
asset = client.assets.save_upload(uploaded.file_id, name="Product shot") # popnow://a/ast_...
|
|
201
|
+
for a in client.assets.iter(kind="image", sort="recent"):
|
|
202
|
+
print(a.name, a.uri)
|
|
203
|
+
lora = client.files.import_lora("your-name/your-lora-repo", token="hf_...") # for loras[].path
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Uploads are temporary; saved assets stay until you delete them. Images up to 10 MiB, video and audio up to 90 MiB (600 seconds), LoRA `.safetensors` up to 2 GiB.
|
|
207
|
+
|
|
208
|
+
### Async
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import asyncio
|
|
212
|
+
from popnow import AsyncPopNow
|
|
213
|
+
|
|
214
|
+
async def main() -> None:
|
|
215
|
+
async with AsyncPopNow() as client:
|
|
216
|
+
balance = await client.account.balance()
|
|
217
|
+
task = await client.images.generate(model="alibaba/z-image-turbo/text-to-image", prompt="A paper boat")
|
|
218
|
+
print(balance.available_credits, task.urls)
|
|
219
|
+
|
|
220
|
+
asyncio.run(main())
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Webhooks
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
import os
|
|
227
|
+
from popnow import webhooks, WebhookVerificationError
|
|
228
|
+
|
|
229
|
+
def handle(raw_body: bytes, headers: dict) -> int:
|
|
230
|
+
try:
|
|
231
|
+
event = webhooks.verify_task_callback(raw_body, headers, os.environ["POPNOW_WEBHOOK_SECRET"])
|
|
232
|
+
except WebhookVerificationError:
|
|
233
|
+
return 401
|
|
234
|
+
task = webhooks.task_from_event(event) # event["request_id"] is the stable delivery id
|
|
235
|
+
print(task.task_id, task.state)
|
|
236
|
+
return 200
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Task callbacks are signed as `base64(HMAC-SHA256(secret, "<taskId>.<timestamp>.<sha256_hex(raw_body)>"))`; timestamps more than five minutes away are rejected. Account endpoints use the Standard Webhooks format: `webhooks.verify_endpoint_event(raw_body, headers, "whsec_...")`. Always verify the raw request bytes.
|
|
240
|
+
|
|
241
|
+
## Configuration
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
client = PopNow(
|
|
245
|
+
api_key="sk-popnow-...", # default: $POPNOW_API_KEY
|
|
246
|
+
base_url="https://api.popnow.ai", # default: $POPNOW_BASE_URL or this
|
|
247
|
+
timeout=60.0, # seconds per request, or an httpx.Timeout
|
|
248
|
+
max_retries=2, # network errors and codes 429, 500, 503, 50301
|
|
249
|
+
language="de", # Accept-Language for error messages
|
|
250
|
+
http_client=None, # bring your own httpx.Client (proxies, mounts)
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Keep keys on your server; never ship them in a browser or mobile app.
|
|
255
|
+
|
|
256
|
+
## Error handling
|
|
257
|
+
|
|
258
|
+
Every error response is `{"code", "msg", "request_id"}`. The SDK raises a typed exception chosen by the business `code`:
|
|
259
|
+
|
|
260
|
+
| Exception | `code` |
|
|
261
|
+
| --- | --- |
|
|
262
|
+
| `BadRequestError` | `400`, `40003`, `40004` |
|
|
263
|
+
| `AuthenticationError` | `401` |
|
|
264
|
+
| `InsufficientCreditsError` / `SpendCapExceededError` (both `PaymentRequiredError`) | `40201` / `40202` |
|
|
265
|
+
| `PermissionDeniedError` | `403`, `40301`, `40302`, `40303`, `40310` |
|
|
266
|
+
| `NotFoundError` | `404` |
|
|
267
|
+
| `ConflictError` / `QuoteMismatchError` | `409` / `40901` |
|
|
268
|
+
| `PayloadTooLargeError` | `413` |
|
|
269
|
+
| `RateLimitError` | `429` (see `err.retry_after`) |
|
|
270
|
+
| `InternalServerError` | `500` |
|
|
271
|
+
| `ServiceUnavailableError` / `ModelUnavailableError` | `503`, `50302` / `50301` |
|
|
272
|
+
| `APIConnectionError` / `APITimeoutError` | no HTTP response |
|
|
273
|
+
| `TaskFailedError` | task ended `failed` or `expired` (see `err.error_code`) |
|
|
274
|
+
| `WaitTimeoutError` | the local wait budget ran out; the task keeps running |
|
|
275
|
+
|
|
276
|
+
All API errors carry `code`, `status_code`, `message`, `request_id` and, for some asset and content errors, `reason` (`data.reason`).
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from popnow import APIError, InsufficientCreditsError, RateLimitError
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
client.jobs.create(model="kling/3.0/text-to-video", input={"prompt": "A koi pond in the rain"})
|
|
283
|
+
except InsufficientCreditsError:
|
|
284
|
+
print("Top up at https://popnow.ai/pricing")
|
|
285
|
+
except RateLimitError as err:
|
|
286
|
+
print("retry in", err.retry_after, "seconds")
|
|
287
|
+
except APIError as err:
|
|
288
|
+
print(err.code, err.message, err.request_id, err.reason)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Branch on `code` (and `task.error_code`), never on message text. Quote `request_id` when contacting support.
|
|
292
|
+
|
|
293
|
+
## Examples
|
|
294
|
+
|
|
295
|
+
See [`examples/`](examples): `generate_image.py`, `image_to_video.py`, `text_to_speech.py`, `quote_and_create.py`, `async_batch.py`, `save_to_library.py`, `webhook_receiver.py`.
|
|
296
|
+
|
|
297
|
+
## FAQ
|
|
298
|
+
|
|
299
|
+
### How do I generate a video from an image with an API in Python?
|
|
300
|
+
|
|
301
|
+
Call `client.videos.generate(model="alibaba/wan-3.0/image-to-video", image_url=..., prompt=...)`. Pass a public HTTPS URL, a `popnow://` URI, or a `pathlib.Path` to a local photo, which the SDK uploads first. The call waits for the clip and returns the task with a signed download URL.
|
|
302
|
+
|
|
303
|
+
### How much does the PopNow API cost?
|
|
304
|
+
|
|
305
|
+
The API uses the same credits and prices as popnow.ai: 1 credit = $0.01, bought in credit packs with no subscription. Each model lists its price in `client.models.get(id).credits`, and `client.jobs.quote()` returns the exact amount for one request. Failed and expired tasks release their hold, so they cost nothing.
|
|
306
|
+
|
|
307
|
+
### How do I retry an AI generation request without being charged twice?
|
|
308
|
+
|
|
309
|
+
Send the same `Idempotency-Key` with every retry of one piece of work. The SDK generates one per call and reuses it across its own automatic retries; pass `idempotency_key=` yourself (for example `order-8814-render-1`) to stay safe across process restarts. Keys are remembered for 24 hours.
|
|
310
|
+
|
|
311
|
+
### Do I have to poll for results?
|
|
312
|
+
|
|
313
|
+
No. `jobs.wait()` polls with back-off for you, `jobs.create(wait=60)` returns quick results in the same call, and in production you can pass `callback_url` and verify the webhook with `popnow.webhooks.verify_task_callback()`.
|
|
314
|
+
|
|
315
|
+
### How do I upload a local file to use as a reference image?
|
|
316
|
+
|
|
317
|
+
`client.files.upload("photo.jpg")` asks for an upload ticket, PUTs the bytes and commits them, then returns a `uri` (`popnow://f/fil_...`) for `image_url` or `image_urls`. To keep a file for reuse, save it with `client.assets.save_upload()`.
|
|
318
|
+
|
|
319
|
+
### Which AI video and image models can I use from Python?
|
|
320
|
+
|
|
321
|
+
Kling, Seedance, Wan, Vidu, MiniMax, Nano Banana, GPT Image, Flux, Seedream, Suno and more. Call `client.models.list(modality="video")` for the live list with availability and prices.
|
|
322
|
+
|
|
323
|
+
### Is there an async Python client for AI video generation?
|
|
324
|
+
|
|
325
|
+
Yes. `AsyncPopNow` has the same resources and helpers as `PopNow`, with `await`. Combine it with `asyncio.gather` to run many tasks at once within the rate limits.
|
|
326
|
+
|
|
327
|
+
### Can I use PopNow from an AI assistant instead of code?
|
|
328
|
+
|
|
329
|
+
Yes. PopNow runs a remote MCP server at `https://api.popnow.ai/mcp` with tools for images, video, music, speech and effects. See [popnow.ai/mcp](https://popnow.ai/mcp).
|
|
330
|
+
|
|
331
|
+
## Links
|
|
332
|
+
|
|
333
|
+
- Website: [popnow.ai](https://popnow.ai)
|
|
334
|
+
- Docs: [popnow.ai/docs](https://popnow.ai/docs)
|
|
335
|
+
- API reference (OpenAPI): [popnow.ai/docs/openapi.yaml](https://popnow.ai/docs/openapi.yaml)
|
|
336
|
+
- Pricing: [popnow.ai/pricing](https://popnow.ai/pricing)
|
|
337
|
+
- MCP server: [popnow.ai/mcp](https://popnow.ai/mcp)
|
|
338
|
+
- llms.txt: [popnow.ai/llms.txt](https://popnow.ai/llms.txt)
|
|
339
|
+
- Other PopNow repos: [popnow-mcp](https://github.com/popnow-ai/popnow-mcp), [popnow-skills](https://github.com/popnow-ai/popnow-skills), [popnow-js](https://github.com/popnow-ai/popnow-js), [popnow-api](https://github.com/popnow-ai/popnow-api), [popnow-blender](https://github.com/popnow-ai/popnow-blender)
|
|
340
|
+
|
|
341
|
+
## Contributing
|
|
342
|
+
|
|
343
|
+
Issues and pull requests are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md). To report a vulnerability, see [SECURITY.md](SECURITY.md).
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
[MIT](LICENSE) © PopNow
|