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.
@@ -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
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .mypy_cache/
8
+ .pytest_cache/
9
+ uv.lock
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
+ [![PyPI](https://img.shields.io/pypi/v/cutpro)](https://pypi.org/project/cutpro/) [![license](https://img.shields.io/pypi/l/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
+ [![PyPI](https://img.shields.io/pypi/v/cutpro)](https://pypi.org/project/cutpro/) [![license](https://img.shields.io/pypi/l/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"]