cutpro 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.
- cutpro-0.1.0/.github/workflows/ci.yml +22 -0
- cutpro-0.1.0/.github/workflows/release.yml +53 -0
- cutpro-0.1.0/.gitignore +9 -0
- cutpro-0.1.0/LICENSE +21 -0
- cutpro-0.1.0/PKG-INFO +263 -0
- cutpro-0.1.0/README.md +236 -0
- cutpro-0.1.0/pyproject.toml +47 -0
- cutpro-0.1.0/src/cutpro/__init__.py +44 -0
- cutpro-0.1.0/src/cutpro/_client.py +430 -0
- cutpro-0.1.0/src/cutpro/py.typed +0 -0
- cutpro-0.1.0/src/cutpro/types.py +212 -0
- cutpro-0.1.0/tests/test_client.py +167 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
check:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
19
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
20
|
+
- run: uv run --python ${{ matrix.python }} --group dev mypy
|
|
21
|
+
- run: uv run --python ${{ matrix.python }} --group dev pytest -q
|
|
22
|
+
- run: uv build
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*.*.*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
15
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
16
|
+
- name: Tag matches pyproject.toml
|
|
17
|
+
run: test "v$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)" = "$GITHUB_REF_NAME"
|
|
18
|
+
- run: uv run --group dev mypy
|
|
19
|
+
- run: uv run --group dev pytest -q
|
|
20
|
+
- run: uv build
|
|
21
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
22
|
+
with:
|
|
23
|
+
name: dist
|
|
24
|
+
path: dist/
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
needs: build
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment: pypi
|
|
30
|
+
permissions:
|
|
31
|
+
id-token: write
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
38
|
+
with:
|
|
39
|
+
# A project token in the `pypi` environment; trusted publishing replaces it once it is configured on PyPI.
|
|
40
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
41
|
+
attestations: false
|
|
42
|
+
|
|
43
|
+
release:
|
|
44
|
+
needs: publish
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
permissions:
|
|
47
|
+
contents: write
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
50
|
+
- name: GitHub release
|
|
51
|
+
env:
|
|
52
|
+
GH_TOKEN: ${{ github.token }}
|
|
53
|
+
run: gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 || gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --generate-notes
|
cutpro-0.1.0/.gitignore
ADDED
cutpro-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cut.Pro
|
|
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.
|
cutpro-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cutpro
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cut.Pro API client for Python: Seedance, Wan, Kling, Veo, Nano Banana, Seedream, GPT Image and Gemini TTS with one key, priced per second, image or 1,000 characters
|
|
5
|
+
Project-URL: Homepage, https://cut.pro/docs/en/generations
|
|
6
|
+
Project-URL: Source, https://github.com/getcutpro/sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/getcutpro/sdk-python/issues
|
|
8
|
+
Project-URL: Prices, https://cut.pro/en/models
|
|
9
|
+
Author: Cut.Pro
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,api,cutpro,gemini-tts,gpt-image,image-generation,image-to-video,kling,nano-banana,sdk,seedance,seedance-api,seedream,text-to-speech,text-to-video,veo,video-generation,wan
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Multimedia :: Video
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Cut.Pro Python SDK: Seedance, Wan, Kling, Veo and Nano Banana API in Python
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/cutpro/) [](LICENSE)
|
|
31
|
+
|
|
32
|
+
One API key for video, image and voice models: Seedance 2.5, Wan 3, Kling O3, Veo 3.1, Gemini Omni Flash, Nano Banana Pro, Seedream 5, Qwen Image, GPT Image, Grok Imagine, MiniMax and Gemini TTS voices. Priced per second of video, per image or per 1,000 characters of voice, in reais and dollars: see the [live price list](https://cut.pro/en/models). Pay with Pix or card; companies get an invoice.
|
|
33
|
+
|
|
34
|
+
Python 3.9 to 3.13, standard library only (no dependencies), fully typed (`py.typed`), sync and asyncio. A pure wheel, so it installs the same way in any environment:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install cutpro # pip, virtualenv, Anaconda and Miniconda environments
|
|
38
|
+
uv add cutpro # uv
|
|
39
|
+
poetry add cutpro # Poetry
|
|
40
|
+
pdm add cutpro # PDM
|
|
41
|
+
pipenv install cutpro # Pipenv
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
Create a key at [cut.pro/studio/me/api-keys](https://cut.pro/studio/me/api-keys) and set `CUTPRO_API_KEY`. Every account starts with free credits.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from cutpro import CutPro
|
|
50
|
+
|
|
51
|
+
cutpro = CutPro() # reads CUTPRO_API_KEY
|
|
52
|
+
|
|
53
|
+
video = cutpro.generations.generate(
|
|
54
|
+
model="bytedance/seedance-2.5",
|
|
55
|
+
prompt="a golden retriever surfing a wave at sunset, cinematic",
|
|
56
|
+
seconds=5,
|
|
57
|
+
resolution="720p",
|
|
58
|
+
aspect_ratio="16:9",
|
|
59
|
+
)
|
|
60
|
+
print(video["output"]["url"], video["credits_charged"], "credits")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`generate()` quotes the request, starts it with the quoted total and waits until it is ready. Each step is also its own call.
|
|
64
|
+
|
|
65
|
+
## Seedance API in Python, step by step
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import uuid
|
|
69
|
+
|
|
70
|
+
quote = cutpro.generations.quote(model="bytedance/seedance-2.5", prompt="a dog surfing", seconds=5)
|
|
71
|
+
print(quote["credits"], "credits") # show the price and wait for a yes
|
|
72
|
+
|
|
73
|
+
started = cutpro.generations.create(
|
|
74
|
+
model="bytedance/seedance-2.5",
|
|
75
|
+
prompt="a dog surfing",
|
|
76
|
+
seconds=5,
|
|
77
|
+
credits=quote["credits"],
|
|
78
|
+
idempotency_key=str(uuid.uuid4()),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
done = cutpro.generations.wait(started["id"], timeout=600)
|
|
82
|
+
if done["status"] == "ready":
|
|
83
|
+
print(done["output"]["url"])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- `create()` without `credits` quotes first and agrees to that total. With `credits`, the price is checked on the server: if it changed, nothing is charged and you get `PRICE_CHANGED` with the new total in `extra["credits"]`.
|
|
87
|
+
- `idempotency_key`: repeating the same request with the same key within 24 hours never charges twice.
|
|
88
|
+
- `webhook_url="https://..."` gets a signed POST when the generation ends, instead of waiting.
|
|
89
|
+
|
|
90
|
+
## Wan 3 image to video
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
clip = cutpro.generations.generate(
|
|
94
|
+
model="alibaba/wan-3",
|
|
95
|
+
mode="image",
|
|
96
|
+
image_urls=["https://your-cdn.com/product.jpg"],
|
|
97
|
+
prompt="the camera slowly orbits the product, soft studio light",
|
|
98
|
+
seconds=5,
|
|
99
|
+
resolution="720p",
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Local file? `media = cutpro.media.upload(open("product.jpg", "rb").read(), filename="product.jpg", content_type="image/jpeg")` and send `image_ids=[media["id"]]`.
|
|
104
|
+
|
|
105
|
+
## Nano Banana Pro image with references
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
image = cutpro.generations.generate(
|
|
109
|
+
model="google/nano-banana-pro",
|
|
110
|
+
mode="references",
|
|
111
|
+
image_urls=["https://your-cdn.com/person.jpg", "https://your-cdn.com/jacket.jpg"],
|
|
112
|
+
prompt="the person from the first image wearing the jacket from the second, street photo",
|
|
113
|
+
aspect_ratio="4:5",
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Gemini TTS voice
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
voices = cutpro.models.list(id="google/gemini-3.1-flash-tts")["models"][0]["voices"]
|
|
121
|
+
print([voice["id"] for voice in voices])
|
|
122
|
+
|
|
123
|
+
voice = cutpro.generations.generate(model="google/gemini-3.1-flash-tts", voice="Kore", prompt="Welcome to Cut.Pro. Your video is ready.")
|
|
124
|
+
print(voice["output"]["url"]) # MP3
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Models and prices
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
catalog = cutpro.models.list(kind="video", endpoint="generations")
|
|
131
|
+
featured = cutpro.models.featured(kind="image")
|
|
132
|
+
|
|
133
|
+
# Public price list, no key needed: credits, dollars and reais per unit.
|
|
134
|
+
prices = CutPro().models.prices()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| Kind | Model ids |
|
|
138
|
+
|---|---|
|
|
139
|
+
| Video | [`bytedance/seedance-2.5`](https://cut.pro/en/models/seedance-2-5), [`bytedance/seedance-2`](https://cut.pro/en/models/seedance-2), [`alibaba/wan-3`](https://cut.pro/en/models/wan-3), [`kling/kling-o3-pro`](https://cut.pro/en/models/kling-o3-pro), [`kling/kling-3-pro`](https://cut.pro/en/models/kling-3-pro), [`google/veo-3.1`](https://cut.pro/en/models/veo-3-1), [`google/gemini-omni-flash-1.1`](https://cut.pro/en/models/gemini-omni-flash-1-1), [`xai/grok-imagine-video-1.5`](https://cut.pro/en/models/grok-imagine-video-1-5), [`minimax/h3`](https://cut.pro/en/models/h3) |
|
|
140
|
+
| Image | [`google/nano-banana-pro`](https://cut.pro/en/models/nano-banana-pro), [`bytedance/seedream-5-pro`](https://cut.pro/en/models/seedream-5-pro), [`alibaba/qwen-image-3-pro`](https://cut.pro/en/models/qwen-image-3-pro), [`openai/gpt-image-2.5`](https://cut.pro/en/models/gpt-image-2-5), [`xai/grok-imagine-image-2`](https://cut.pro/en/models/grok-imagine-image-2) |
|
|
141
|
+
| Voice | [`google/gemini-3.1-flash-tts`](https://cut.pro/en/models/gemini-3-1-flash-tts), [`minimax/speech-2.8-hd`](https://cut.pro/en/models/speech-2-8-hd) |
|
|
142
|
+
|
|
143
|
+
The full catalog is in `models.list()` and at [cut.pro/en/models](https://cut.pro/en/models). Prices change: read them from the API, never hard-code them.
|
|
144
|
+
|
|
145
|
+
## Errors
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from cutpro import CutProError
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
cutpro.generations.generate(model="bytedance/seedance-2.5", prompt="a dog surfing", seconds=3)
|
|
152
|
+
except CutProError as error:
|
|
153
|
+
if error.code == "INSUFFICIENT_CREDITS":
|
|
154
|
+
print("Top up at", error.extra["top_up_url"])
|
|
155
|
+
elif error.code == "SECONDS_INVALID":
|
|
156
|
+
print("Allowed seconds:", error.extra["allowed"])
|
|
157
|
+
else:
|
|
158
|
+
raise
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`code` is stable, `status` is the HTTP status and `extra` has what to act on. `status` is `0` for errors raised by the client: `WAIT_TIMEOUT`, `INSECURE_BASE_URL`, `INSECURE_DOWNLOAD_URL`, or a generation that ended `failed` (its `error_code` becomes `code`, and the credits come back on their own). Full list in the [error catalog](https://cut.pro/docs/en/errors).
|
|
162
|
+
|
|
163
|
+
## asyncio
|
|
164
|
+
|
|
165
|
+
`AsyncCutPro` has the same methods. Waiting never blocks the event loop, so FastAPI, aiohttp or a Jupyter notebook can run many generations at once.
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
import asyncio
|
|
169
|
+
from cutpro import AsyncCutPro
|
|
170
|
+
|
|
171
|
+
async def main() -> None:
|
|
172
|
+
cutpro = AsyncCutPro()
|
|
173
|
+
videos = await asyncio.gather(*(cutpro.generations.generate(model="bytedance/seedance-2-fast", prompt=p, seconds=5) for p in ["a dog surfing", "a cat skating"]))
|
|
174
|
+
print([video["output"]["url"] for video in videos])
|
|
175
|
+
|
|
176
|
+
asyncio.run(main())
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Download to a file
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
cutpro.generations.download_to(video["id"], "dog.mp4") # streams to disk, never holds the whole file in memory
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Webhooks
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from cutpro import verify_webhook
|
|
189
|
+
|
|
190
|
+
secret = cutpro.generations.webhook_secret() # store it with your other secrets
|
|
191
|
+
|
|
192
|
+
# In your webhook route, with the raw body before any JSON parsing:
|
|
193
|
+
if not verify_webhook(raw_body, headers.get("CutPro-Signature"), secret):
|
|
194
|
+
return 401
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
It checks the HMAC of the exact body, refuses signatures older than five minutes and compares in constant time.
|
|
198
|
+
|
|
199
|
+
## A browser app on your Python server
|
|
200
|
+
|
|
201
|
+
The key spends credits, so it never goes to a page. `proxy_request` forwards only what a generation needs (models, prices, quote, create, get) with the key added on your server. With FastAPI:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from fastapi import FastAPI, Request, Response
|
|
205
|
+
from cutpro import proxy_request
|
|
206
|
+
|
|
207
|
+
app = FastAPI()
|
|
208
|
+
|
|
209
|
+
@app.api_route("/api/cutpro/{path:path}", methods=["GET", "POST"])
|
|
210
|
+
async def cutpro_proxy(path: str, request: Request) -> Response:
|
|
211
|
+
if not is_signed_in(request): # whoever reaches this route spends your credits
|
|
212
|
+
return Response(status_code=401)
|
|
213
|
+
status, body = proxy_request(request.method, f"/{path}", body=await request.body(), headers=dict(request.headers), query=request.url.query)
|
|
214
|
+
return Response(body, status_code=status, media_type="application/json")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The page uses the JavaScript SDK with `new CutPro({ baseURL: "/api/cutpro" })`.
|
|
218
|
+
|
|
219
|
+
## Options
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
CutPro(
|
|
223
|
+
api_key="...", # defaults to CUTPRO_API_KEY
|
|
224
|
+
workspace_id="...", # only for a key that reaches more than one workspace
|
|
225
|
+
base_url="https://api.cut.pro/api/v1",
|
|
226
|
+
timeout=90.0, # seconds per HTTP request
|
|
227
|
+
max_retries=2,
|
|
228
|
+
transport=my_transport, # (method, url, headers, body, timeout) -> (status, headers, body): plug in httpx or requests, or stub it in tests
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Security and reliability
|
|
233
|
+
|
|
234
|
+
- The key only travels in the `X-Api-Key` header, never in a URL, and redirects are refused so it never follows one to another host. `repr(client)` never shows it.
|
|
235
|
+
- `base_url` must be `https`, except on localhost.
|
|
236
|
+
- `generations.create` sends an `Idempotency-Key` (a fresh UUID unless you pass one), so a retry or a repeated call never charges twice.
|
|
237
|
+
- Network errors, `429` and `5xx` are retried twice with backoff, honoring `Retry-After`; uploads are never retried.
|
|
238
|
+
- Waiting uses the server's long poll instead of hammering it.
|
|
239
|
+
|
|
240
|
+
## Links
|
|
241
|
+
|
|
242
|
+
- [Generation guide](https://cut.pro/docs/en/generations) and [API reference](https://cut.pro/docs/reference)
|
|
243
|
+
- [Node SDK](https://github.com/getcutpro/sdk-js), [Vercel AI SDK provider](https://github.com/getcutpro/ai-sdk-provider), [n8n node](https://github.com/getcutpro/n8n-nodes-cutpro), [examples](https://github.com/getcutpro/examples)
|
|
244
|
+
|
|
245
|
+
## Em português
|
|
246
|
+
|
|
247
|
+
Instala igual com pip, uv, Poetry, PDM, Pipenv e em ambientes Anaconda ou Miniconda (`pip install cutpro`). Tem cliente síncrono e assíncrono (`AsyncCutPro`), verificação de webhook e proxy para usar no navegador sem expor a chave.
|
|
248
|
+
|
|
249
|
+
SDK oficial da Cut.Pro para Python: uma chave de API para Seedance 2.5, Wan 3, Kling O3, Veo 3.1, Nano Banana Pro, Seedream 5, GPT Image, Grok Imagine, MiniMax e vozes do Gemini TTS. Preço por segundo de vídeo, por imagem ou por 1.000 caracteres de voz, em reais: veja a [tabela de preços ao vivo](https://cut.pro/pt-BR/models). Pague com Pix ou cartão, com nota fiscal para empresas. Toda conta começa com créditos grátis.
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from cutpro import CutPro
|
|
253
|
+
|
|
254
|
+
cutpro = CutPro() # lê CUTPRO_API_KEY
|
|
255
|
+
voz = cutpro.generations.generate(model="google/gemini-3.1-flash-tts", voice="Kore", prompt="Olá! Seu vídeo está pronto.")
|
|
256
|
+
print(voz["output"]["url"])
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Crie a chave em [cut.pro/studio/me/api-keys](https://cut.pro/studio/me/api-keys). Guia em português: [cut.pro/docs/generations](https://cut.pro/docs/generations). Páginas dos modelos: [Seedance 2.5](https://cut.pro/pt-BR/models/seedance-2-5), [Wan 3](https://cut.pro/pt-BR/models/wan-3), [Nano Banana Pro](https://cut.pro/pt-BR/models/nano-banana-pro), [Gemini 3.1 Flash TTS](https://cut.pro/pt-BR/models/gemini-3-1-flash-tts).
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
cutpro-0.1.0/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Cut.Pro Python SDK: Seedance, Wan, Kling, Veo and Nano Banana API in Python
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/cutpro/) [](LICENSE)
|
|
4
|
+
|
|
5
|
+
One API key for video, image and voice models: Seedance 2.5, Wan 3, Kling O3, Veo 3.1, Gemini Omni Flash, Nano Banana Pro, Seedream 5, Qwen Image, GPT Image, Grok Imagine, MiniMax and Gemini TTS voices. Priced per second of video, per image or per 1,000 characters of voice, in reais and dollars: see the [live price list](https://cut.pro/en/models). Pay with Pix or card; companies get an invoice.
|
|
6
|
+
|
|
7
|
+
Python 3.9 to 3.13, standard library only (no dependencies), fully typed (`py.typed`), sync and asyncio. A pure wheel, so it installs the same way in any environment:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install cutpro # pip, virtualenv, Anaconda and Miniconda environments
|
|
11
|
+
uv add cutpro # uv
|
|
12
|
+
poetry add cutpro # Poetry
|
|
13
|
+
pdm add cutpro # PDM
|
|
14
|
+
pipenv install cutpro # Pipenv
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
Create a key at [cut.pro/studio/me/api-keys](https://cut.pro/studio/me/api-keys) and set `CUTPRO_API_KEY`. Every account starts with free credits.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from cutpro import CutPro
|
|
23
|
+
|
|
24
|
+
cutpro = CutPro() # reads CUTPRO_API_KEY
|
|
25
|
+
|
|
26
|
+
video = cutpro.generations.generate(
|
|
27
|
+
model="bytedance/seedance-2.5",
|
|
28
|
+
prompt="a golden retriever surfing a wave at sunset, cinematic",
|
|
29
|
+
seconds=5,
|
|
30
|
+
resolution="720p",
|
|
31
|
+
aspect_ratio="16:9",
|
|
32
|
+
)
|
|
33
|
+
print(video["output"]["url"], video["credits_charged"], "credits")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`generate()` quotes the request, starts it with the quoted total and waits until it is ready. Each step is also its own call.
|
|
37
|
+
|
|
38
|
+
## Seedance API in Python, step by step
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import uuid
|
|
42
|
+
|
|
43
|
+
quote = cutpro.generations.quote(model="bytedance/seedance-2.5", prompt="a dog surfing", seconds=5)
|
|
44
|
+
print(quote["credits"], "credits") # show the price and wait for a yes
|
|
45
|
+
|
|
46
|
+
started = cutpro.generations.create(
|
|
47
|
+
model="bytedance/seedance-2.5",
|
|
48
|
+
prompt="a dog surfing",
|
|
49
|
+
seconds=5,
|
|
50
|
+
credits=quote["credits"],
|
|
51
|
+
idempotency_key=str(uuid.uuid4()),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
done = cutpro.generations.wait(started["id"], timeout=600)
|
|
55
|
+
if done["status"] == "ready":
|
|
56
|
+
print(done["output"]["url"])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- `create()` without `credits` quotes first and agrees to that total. With `credits`, the price is checked on the server: if it changed, nothing is charged and you get `PRICE_CHANGED` with the new total in `extra["credits"]`.
|
|
60
|
+
- `idempotency_key`: repeating the same request with the same key within 24 hours never charges twice.
|
|
61
|
+
- `webhook_url="https://..."` gets a signed POST when the generation ends, instead of waiting.
|
|
62
|
+
|
|
63
|
+
## Wan 3 image to video
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
clip = cutpro.generations.generate(
|
|
67
|
+
model="alibaba/wan-3",
|
|
68
|
+
mode="image",
|
|
69
|
+
image_urls=["https://your-cdn.com/product.jpg"],
|
|
70
|
+
prompt="the camera slowly orbits the product, soft studio light",
|
|
71
|
+
seconds=5,
|
|
72
|
+
resolution="720p",
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Local file? `media = cutpro.media.upload(open("product.jpg", "rb").read(), filename="product.jpg", content_type="image/jpeg")` and send `image_ids=[media["id"]]`.
|
|
77
|
+
|
|
78
|
+
## Nano Banana Pro image with references
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
image = cutpro.generations.generate(
|
|
82
|
+
model="google/nano-banana-pro",
|
|
83
|
+
mode="references",
|
|
84
|
+
image_urls=["https://your-cdn.com/person.jpg", "https://your-cdn.com/jacket.jpg"],
|
|
85
|
+
prompt="the person from the first image wearing the jacket from the second, street photo",
|
|
86
|
+
aspect_ratio="4:5",
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Gemini TTS voice
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
voices = cutpro.models.list(id="google/gemini-3.1-flash-tts")["models"][0]["voices"]
|
|
94
|
+
print([voice["id"] for voice in voices])
|
|
95
|
+
|
|
96
|
+
voice = cutpro.generations.generate(model="google/gemini-3.1-flash-tts", voice="Kore", prompt="Welcome to Cut.Pro. Your video is ready.")
|
|
97
|
+
print(voice["output"]["url"]) # MP3
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Models and prices
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
catalog = cutpro.models.list(kind="video", endpoint="generations")
|
|
104
|
+
featured = cutpro.models.featured(kind="image")
|
|
105
|
+
|
|
106
|
+
# Public price list, no key needed: credits, dollars and reais per unit.
|
|
107
|
+
prices = CutPro().models.prices()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Kind | Model ids |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Video | [`bytedance/seedance-2.5`](https://cut.pro/en/models/seedance-2-5), [`bytedance/seedance-2`](https://cut.pro/en/models/seedance-2), [`alibaba/wan-3`](https://cut.pro/en/models/wan-3), [`kling/kling-o3-pro`](https://cut.pro/en/models/kling-o3-pro), [`kling/kling-3-pro`](https://cut.pro/en/models/kling-3-pro), [`google/veo-3.1`](https://cut.pro/en/models/veo-3-1), [`google/gemini-omni-flash-1.1`](https://cut.pro/en/models/gemini-omni-flash-1-1), [`xai/grok-imagine-video-1.5`](https://cut.pro/en/models/grok-imagine-video-1-5), [`minimax/h3`](https://cut.pro/en/models/h3) |
|
|
113
|
+
| Image | [`google/nano-banana-pro`](https://cut.pro/en/models/nano-banana-pro), [`bytedance/seedream-5-pro`](https://cut.pro/en/models/seedream-5-pro), [`alibaba/qwen-image-3-pro`](https://cut.pro/en/models/qwen-image-3-pro), [`openai/gpt-image-2.5`](https://cut.pro/en/models/gpt-image-2-5), [`xai/grok-imagine-image-2`](https://cut.pro/en/models/grok-imagine-image-2) |
|
|
114
|
+
| Voice | [`google/gemini-3.1-flash-tts`](https://cut.pro/en/models/gemini-3-1-flash-tts), [`minimax/speech-2.8-hd`](https://cut.pro/en/models/speech-2-8-hd) |
|
|
115
|
+
|
|
116
|
+
The full catalog is in `models.list()` and at [cut.pro/en/models](https://cut.pro/en/models). Prices change: read them from the API, never hard-code them.
|
|
117
|
+
|
|
118
|
+
## Errors
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from cutpro import CutProError
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
cutpro.generations.generate(model="bytedance/seedance-2.5", prompt="a dog surfing", seconds=3)
|
|
125
|
+
except CutProError as error:
|
|
126
|
+
if error.code == "INSUFFICIENT_CREDITS":
|
|
127
|
+
print("Top up at", error.extra["top_up_url"])
|
|
128
|
+
elif error.code == "SECONDS_INVALID":
|
|
129
|
+
print("Allowed seconds:", error.extra["allowed"])
|
|
130
|
+
else:
|
|
131
|
+
raise
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`code` is stable, `status` is the HTTP status and `extra` has what to act on. `status` is `0` for errors raised by the client: `WAIT_TIMEOUT`, `INSECURE_BASE_URL`, `INSECURE_DOWNLOAD_URL`, or a generation that ended `failed` (its `error_code` becomes `code`, and the credits come back on their own). Full list in the [error catalog](https://cut.pro/docs/en/errors).
|
|
135
|
+
|
|
136
|
+
## asyncio
|
|
137
|
+
|
|
138
|
+
`AsyncCutPro` has the same methods. Waiting never blocks the event loop, so FastAPI, aiohttp or a Jupyter notebook can run many generations at once.
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import asyncio
|
|
142
|
+
from cutpro import AsyncCutPro
|
|
143
|
+
|
|
144
|
+
async def main() -> None:
|
|
145
|
+
cutpro = AsyncCutPro()
|
|
146
|
+
videos = await asyncio.gather(*(cutpro.generations.generate(model="bytedance/seedance-2-fast", prompt=p, seconds=5) for p in ["a dog surfing", "a cat skating"]))
|
|
147
|
+
print([video["output"]["url"] for video in videos])
|
|
148
|
+
|
|
149
|
+
asyncio.run(main())
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Download to a file
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
cutpro.generations.download_to(video["id"], "dog.mp4") # streams to disk, never holds the whole file in memory
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Webhooks
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from cutpro import verify_webhook
|
|
162
|
+
|
|
163
|
+
secret = cutpro.generations.webhook_secret() # store it with your other secrets
|
|
164
|
+
|
|
165
|
+
# In your webhook route, with the raw body before any JSON parsing:
|
|
166
|
+
if not verify_webhook(raw_body, headers.get("CutPro-Signature"), secret):
|
|
167
|
+
return 401
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
It checks the HMAC of the exact body, refuses signatures older than five minutes and compares in constant time.
|
|
171
|
+
|
|
172
|
+
## A browser app on your Python server
|
|
173
|
+
|
|
174
|
+
The key spends credits, so it never goes to a page. `proxy_request` forwards only what a generation needs (models, prices, quote, create, get) with the key added on your server. With FastAPI:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from fastapi import FastAPI, Request, Response
|
|
178
|
+
from cutpro import proxy_request
|
|
179
|
+
|
|
180
|
+
app = FastAPI()
|
|
181
|
+
|
|
182
|
+
@app.api_route("/api/cutpro/{path:path}", methods=["GET", "POST"])
|
|
183
|
+
async def cutpro_proxy(path: str, request: Request) -> Response:
|
|
184
|
+
if not is_signed_in(request): # whoever reaches this route spends your credits
|
|
185
|
+
return Response(status_code=401)
|
|
186
|
+
status, body = proxy_request(request.method, f"/{path}", body=await request.body(), headers=dict(request.headers), query=request.url.query)
|
|
187
|
+
return Response(body, status_code=status, media_type="application/json")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The page uses the JavaScript SDK with `new CutPro({ baseURL: "/api/cutpro" })`.
|
|
191
|
+
|
|
192
|
+
## Options
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
CutPro(
|
|
196
|
+
api_key="...", # defaults to CUTPRO_API_KEY
|
|
197
|
+
workspace_id="...", # only for a key that reaches more than one workspace
|
|
198
|
+
base_url="https://api.cut.pro/api/v1",
|
|
199
|
+
timeout=90.0, # seconds per HTTP request
|
|
200
|
+
max_retries=2,
|
|
201
|
+
transport=my_transport, # (method, url, headers, body, timeout) -> (status, headers, body): plug in httpx or requests, or stub it in tests
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Security and reliability
|
|
206
|
+
|
|
207
|
+
- The key only travels in the `X-Api-Key` header, never in a URL, and redirects are refused so it never follows one to another host. `repr(client)` never shows it.
|
|
208
|
+
- `base_url` must be `https`, except on localhost.
|
|
209
|
+
- `generations.create` sends an `Idempotency-Key` (a fresh UUID unless you pass one), so a retry or a repeated call never charges twice.
|
|
210
|
+
- Network errors, `429` and `5xx` are retried twice with backoff, honoring `Retry-After`; uploads are never retried.
|
|
211
|
+
- Waiting uses the server's long poll instead of hammering it.
|
|
212
|
+
|
|
213
|
+
## Links
|
|
214
|
+
|
|
215
|
+
- [Generation guide](https://cut.pro/docs/en/generations) and [API reference](https://cut.pro/docs/reference)
|
|
216
|
+
- [Node SDK](https://github.com/getcutpro/sdk-js), [Vercel AI SDK provider](https://github.com/getcutpro/ai-sdk-provider), [n8n node](https://github.com/getcutpro/n8n-nodes-cutpro), [examples](https://github.com/getcutpro/examples)
|
|
217
|
+
|
|
218
|
+
## Em português
|
|
219
|
+
|
|
220
|
+
Instala igual com pip, uv, Poetry, PDM, Pipenv e em ambientes Anaconda ou Miniconda (`pip install cutpro`). Tem cliente síncrono e assíncrono (`AsyncCutPro`), verificação de webhook e proxy para usar no navegador sem expor a chave.
|
|
221
|
+
|
|
222
|
+
SDK oficial da Cut.Pro para Python: uma chave de API para Seedance 2.5, Wan 3, Kling O3, Veo 3.1, Nano Banana Pro, Seedream 5, GPT Image, Grok Imagine, MiniMax e vozes do Gemini TTS. Preço por segundo de vídeo, por imagem ou por 1.000 caracteres de voz, em reais: veja a [tabela de preços ao vivo](https://cut.pro/pt-BR/models). Pague com Pix ou cartão, com nota fiscal para empresas. Toda conta começa com créditos grátis.
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from cutpro import CutPro
|
|
226
|
+
|
|
227
|
+
cutpro = CutPro() # lê CUTPRO_API_KEY
|
|
228
|
+
voz = cutpro.generations.generate(model="google/gemini-3.1-flash-tts", voice="Kore", prompt="Olá! Seu vídeo está pronto.")
|
|
229
|
+
print(voz["output"]["url"])
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Crie a chave em [cut.pro/studio/me/api-keys](https://cut.pro/studio/me/api-keys). Guia em português: [cut.pro/docs/generations](https://cut.pro/docs/generations). Páginas dos modelos: [Seedance 2.5](https://cut.pro/pt-BR/models/seedance-2-5), [Wan 3](https://cut.pro/pt-BR/models/wan-3), [Nano Banana Pro](https://cut.pro/pt-BR/models/nano-banana-pro), [Gemini 3.1 Flash TTS](https://cut.pro/pt-BR/models/gemini-3-1-flash-tts).
|
|
233
|
+
|
|
234
|
+
## License
|
|
235
|
+
|
|
236
|
+
MIT
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cutpro"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Cut.Pro API client for Python: Seedance, Wan, Kling, Veo, Nano Banana, Seedream, GPT Image and Gemini TTS with one key, priced per second, image or 1,000 characters"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Cut.Pro" }]
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
dependencies = []
|
|
15
|
+
keywords = ["cutpro", "ai", "api", "sdk", "video-generation", "image-generation", "text-to-speech", "text-to-video", "image-to-video", "seedance", "seedance-api", "wan", "kling", "veo", "nano-banana", "seedream", "gpt-image", "gemini-tts"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Multimedia :: Video",
|
|
27
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://cut.pro/docs/en/generations"
|
|
33
|
+
Source = "https://github.com/getcutpro/sdk-python"
|
|
34
|
+
Issues = "https://github.com/getcutpro/sdk-python/issues"
|
|
35
|
+
Prices = "https://cut.pro/en/models"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = ["pytest>=8", "mypy>=1.13"]
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/cutpro"]
|
|
42
|
+
|
|
43
|
+
[tool.mypy]
|
|
44
|
+
strict = true
|
|
45
|
+
# Every response is parsed JSON, typed by the TypedDict each method returns.
|
|
46
|
+
warn_return_any = false
|
|
47
|
+
files = ["src", "tests"]
|