imagestep 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.
- imagestep-0.1.0/.gitignore +7 -0
- imagestep-0.1.0/LICENSE +21 -0
- imagestep-0.1.0/PKG-INFO +126 -0
- imagestep-0.1.0/README.md +92 -0
- imagestep-0.1.0/THIRD-PARTY-NOTICES.md +603 -0
- imagestep-0.1.0/examples/remove_bg.py +24 -0
- imagestep-0.1.0/imagestep/__init__.py +28 -0
- imagestep-0.1.0/imagestep/_compat.py +22 -0
- imagestep-0.1.0/imagestep/_generated.py +3281 -0
- imagestep-0.1.0/imagestep/client.py +2456 -0
- imagestep-0.1.0/imagestep/errors.py +66 -0
- imagestep-0.1.0/imagestep/py.typed +0 -0
- imagestep-0.1.0/imagestep/webhooks.py +72 -0
- imagestep-0.1.0/pyproject.toml +59 -0
- imagestep-0.1.0/scripts/gen_types.sh +82 -0
- imagestep-0.1.0/tests/test_client.py +1404 -0
- imagestep-0.1.0/tests/test_docs_calls.py +113 -0
- imagestep-0.1.0/tests/test_docs_webhook_snippet.py +62 -0
imagestep-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jun Zhang
|
|
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.
|
imagestep-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: imagestep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ImageStep SDK — the image step for your automations
|
|
5
|
+
Project-URL: Homepage, https://imagestep.dev
|
|
6
|
+
Project-URL: Documentation, https://imagestep.dev/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/jun-zhang-pro/imagestep
|
|
8
|
+
Project-URL: Issues, https://imagestep.dev/support
|
|
9
|
+
Author: ImageStep
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: THIRD-PARTY-NOTICES.md
|
|
13
|
+
Keywords: api,automation,image,image-generation,imagestep,mcp,n8n,remove-background,upscale
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Requires-Dist: httpx>=0.27
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# imagestep
|
|
36
|
+
|
|
37
|
+
The image step for your automations — one API key, one job handle, one stable URL.
|
|
38
|
+
Generate, edit, remove backgrounds, upscale, convert and read metadata from Python 3.10+.
|
|
39
|
+
One runtime dependency (`httpx`), a sync and an async client, typed (TypedDicts generated from
|
|
40
|
+
the API's OpenAPI document).
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import os
|
|
44
|
+
from imagestep import ImageStep
|
|
45
|
+
|
|
46
|
+
client = ImageStep(api_key=os.environ["IMAGESTEP_API_KEY"])
|
|
47
|
+
|
|
48
|
+
asset = client.assets.upload("./product.jpg") # stage → PUT → finish → ready
|
|
49
|
+
job = client.ops.remove_bg(asset["id"], wait=True) # or any op: upscale, resize, convert, generate…
|
|
50
|
+
[cutout] = client.jobs.outputs(job)
|
|
51
|
+
[published] = client.assets.publish(cutout["id"])
|
|
52
|
+
print(published["publicUrl"]) # https://cdn.imagestep.dev/<id>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Only want the image back?** One call, nothing stored:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
small = client.images.transform("resize", file="./product.jpg", parameters={"width": 1200})
|
|
59
|
+
open("out.jpg", "wb").write(small)
|
|
60
|
+
|
|
61
|
+
# One op does one thing. Resize AND re-encode is two steps — save them as a preset and run it in one call:
|
|
62
|
+
webp = client.images.transform(None, file="./product.jpg", preset="web-optimize")
|
|
63
|
+
# your own render template, then a PNG from it (pin a version with f"{card['id']}@1")
|
|
64
|
+
card = client.templates.create({"name": "price-card", "html": "<h1>{{ title }}</h1>", "width": 1200, "height": 630})
|
|
65
|
+
png = client.images.render(card["id"], {"title": "Hello"})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Two paths, and the line between them is not speed — it is **who carries the retry**. `ops.*` gives
|
|
69
|
+
you a job: this service promises to finish it, which is what buys progress, cancellation, webhooks
|
|
70
|
+
and an `asset_id`. `images.*` runs while you wait and stores nothing, because you are still holding
|
|
71
|
+
the input, so a failure costs you one re-send. AI ops and batches are always jobs.
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
pip install imagestep
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`ImageStep()` with no arguments reads `IMAGESTEP_API_KEY` (and `IMAGESTEP_BASE_URL`, default
|
|
80
|
+
`https://api.imagestep.dev`). `AsyncImageStep` has the identical surface with every method
|
|
81
|
+
awaitable — `async with AsyncImageStep() as client: await client.ops.upscale(...)`, and that
|
|
82
|
+
includes `images.*` (`await client.images.transform("resize", file=…, parameters={"width": 1200})`).
|
|
83
|
+
|
|
84
|
+
`construct_webhook_event` raises `WebhookSignatureError` when the signature does not check out; the
|
|
85
|
+
JavaScript SDK throws a plain `Error` there, and takes its key as an argument rather than from the
|
|
86
|
+
environment (it also runs on edge runtimes that have no `process.env`).
|
|
87
|
+
|
|
88
|
+
## What you get
|
|
89
|
+
|
|
90
|
+
| | |
|
|
91
|
+
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `client.ops.run(op, variants=[...])` | one call, one asset per variant — the whole set of social sizes (contract §8) |
|
|
93
|
+
| `client.ops` | `list` `get` `run` `estimate` `remove_bg` `upscale` `restore_face` `colorize` `analyze` `generate` `edit` `resize` `convert` `compress` `crop` `pad` `grayscale` `rotate` `flip` `flop` `trim` `flatten` `adjust` `mask` `blur_region` `overlay` `caption` `read_metadata` — the atomic-op vocabulary: `run(op, **opts)` submits any op as a job, `estimate` prices one without creating anything, and the rest are one helper per op |
|
|
94
|
+
| `client.images` | `sync_endpoints` `supports` `transform` `transform_result` `render` `metadata` — the synchronous face (contract §9): `transform(op, file=… \| url=… \| asset_id=…, parameters={…})` → `bytes`, `transform_result(…)` → a `BinaryResult` with `content_type` / `width` / `height` as well, `render(template_id, data)` → PNG, `metadata(file)` → dict. Which ops may go this way is `GET /api/v1/ops`, never a list in this package |
|
|
95
|
+
| `client.assets` | `upload` `upload_many` `from_url` `wait_ready` `status` `download` `get` `list` `iterate` `collections` `iterate_collections` `rename_collection` `publish` `unpublish` `set_collection` `tag` `delete` — `upload()` takes a path · bytes · a binary file object, dedupes by sha1 and waits for ingest; `upload_many()` does many at once (one stage and one finish call per 500, `concurrency` PUTs, one status call per tick); `from_url()` has the SERVICE fetch each link instead, 20 to a request |
|
|
96
|
+
| `client.jobs` | `submit` `estimate` `get` `items` `iterate_items` `list` `iterate` `cancel` `resume` `wait` `outputs` — `wait(job_id, on_progress=…)` waits for completion (the service holds each read open — `GET /jobs/{id}?wait=` — so a five-second job costs one request, not a poll loop) and `outputs(job)` reads what it produced as list rows — one paged `GET /assets?job_id=`, not one read per item (#441) — or subscribe to `job.completed` webhooks instead of polling. The totals by status and type have no method — `client.get("/api/v1/jobs/counts")`; for one status, `list(status=…, per_page=1)` and read `meta["total"]` |
|
|
97
|
+
| `client.presets` | `list` `get` `create` `update` `delete` `delete_version` `import_` `run` — versioned lists of steps you save once and run by slug: `presets.run(slug, asset_ids, wait=True)` (`"slug@3"` pins version 3); `presets.import_()` (keyword clash); `presets.create({"name": …, "steps": [{"op": "generate", "prompt": "{{subject.hero}} on a rooftop"}], "subjects": [{"name": "hero", "referenceAssetIds": [asset_id], "descriptor": "a matte black bottle…"}]})` — the images pin the geometry (max 4 across all subjects, sent with the preset's `generate` / `edit` step) and the descriptor pins the words, expanding into the prompt wherever you write `{{subject.hero}}`; `get(slug)` exports `steps` + `subjects` + `version` + `versions`. A preset whose steps mix a model with other steps runs as **one** `chain` job, the image in between handed on for you; `jobs.estimate` prices it per segment in `steps` A preset keeps 50 versions on record: at the ceiling `update` is `422 resource_limit_exceeded` rather than dropping the oldest, and `delete_version(slug, n)` makes room — `slug@n` answers 404 from then on, so it is for versions nothing pins. |
|
|
98
|
+
| `client.templates` | `list` `iterate` `get` `versions` `create` `update` `delete` `import_` — HTML/CSS render templates (#24 / #234); `list` is one page of rows without html / css (#497), `get` the whole document; versioned: `update` saves `version + 1`, and `id@version` reads or renders one frozen version; a batch is a job: `ops.run("render_template", template_id=…, items=[…])` |
|
|
99
|
+
| `client.models` | `list` — the model catalogue with prices; `list("ai_image")` (default) or `list("analyze")` |
|
|
100
|
+
| `client.webhooks` | `list` `get` `create` `update` `delete` `rotate_secret` `test` `deliveries` `iterate_deliveries` `verify` `construct_event` — `create()` answers the signing secret once; `verify(raw_body, header, secret)` and `construct_event(…)` check a delivery's signature in your own handler |
|
|
101
|
+
| `client.agent` | `guidelines` `feedback` `reports` `iterate_reports` — what this API expects of an agent, and the channel for telling us an op you needed is missing |
|
|
102
|
+
| `client.usage` | `get` — credits charged, jobs created and items settled over a window, grouped by `op`, `key` or `day` (contract §11) |
|
|
103
|
+
| `ImageStepError` | every failure the API answers: `code` (closed set), `retryable` (what to branch on — without the service's own, true for a 429 or a 5xx), `param`, `details`, `retry_after`, and `request_id` — `error.requestId`, else the `X-Request-Id` header; quote it when reporting a failure (contract §11). Writes carry an `Idempotency-Key` per call, reused across the SDK's own retries; pass `idempotency_key` to make your own retry the same submission |
|
|
104
|
+
|
|
105
|
+
## Pagination
|
|
106
|
+
|
|
107
|
+
Every list takes `page` (from 0) and `per_page` (100 by default and at most) and answers a `Page` —
|
|
108
|
+
`items` plus `meta` (`total`, `page`, `perPage`, `hasMore`, `nextCursor`). Out of range is clamped, not
|
|
109
|
+
refused, and `meta` reports the page actually served. Pass `cursor=meta["nextCursor"]` instead of a page
|
|
110
|
+
number to read the rows after it: the service counts nothing then (no `total`, no `page`), and page 1 000
|
|
111
|
+
costs what page 1 did.
|
|
112
|
+
|
|
113
|
+
Don't write the loop — each listing has an iterator that walks to the end, following the `nextCursor` each
|
|
114
|
+
*answer* carries:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
for asset in client.assets.iterate(collection="shoot-01"):
|
|
118
|
+
print(asset["id"])
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`assets.iterate` · `assets.iterate_collections` · `jobs.iterate` · `jobs.iterate_items(id)` · `templates.iterate` ·
|
|
122
|
+
`webhooks.iterate_deliveries(id)` · `agent.iterate_reports`, on both clients (`async for` on `AsyncImageStep`).
|
|
123
|
+
Pass `cursor=` (or `page=`) to resume a walk.
|
|
124
|
+
|
|
125
|
+
Every signature, parameter and return shape: **<https://imagestep.dev/docs/sdk>** — one reference for
|
|
126
|
+
both SDKs, held against these sources by a test (imagestep#297).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# imagestep
|
|
2
|
+
|
|
3
|
+
The image step for your automations — one API key, one job handle, one stable URL.
|
|
4
|
+
Generate, edit, remove backgrounds, upscale, convert and read metadata from Python 3.10+.
|
|
5
|
+
One runtime dependency (`httpx`), a sync and an async client, typed (TypedDicts generated from
|
|
6
|
+
the API's OpenAPI document).
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import os
|
|
10
|
+
from imagestep import ImageStep
|
|
11
|
+
|
|
12
|
+
client = ImageStep(api_key=os.environ["IMAGESTEP_API_KEY"])
|
|
13
|
+
|
|
14
|
+
asset = client.assets.upload("./product.jpg") # stage → PUT → finish → ready
|
|
15
|
+
job = client.ops.remove_bg(asset["id"], wait=True) # or any op: upscale, resize, convert, generate…
|
|
16
|
+
[cutout] = client.jobs.outputs(job)
|
|
17
|
+
[published] = client.assets.publish(cutout["id"])
|
|
18
|
+
print(published["publicUrl"]) # https://cdn.imagestep.dev/<id>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Only want the image back?** One call, nothing stored:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
small = client.images.transform("resize", file="./product.jpg", parameters={"width": 1200})
|
|
25
|
+
open("out.jpg", "wb").write(small)
|
|
26
|
+
|
|
27
|
+
# One op does one thing. Resize AND re-encode is two steps — save them as a preset and run it in one call:
|
|
28
|
+
webp = client.images.transform(None, file="./product.jpg", preset="web-optimize")
|
|
29
|
+
# your own render template, then a PNG from it (pin a version with f"{card['id']}@1")
|
|
30
|
+
card = client.templates.create({"name": "price-card", "html": "<h1>{{ title }}</h1>", "width": 1200, "height": 630})
|
|
31
|
+
png = client.images.render(card["id"], {"title": "Hello"})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Two paths, and the line between them is not speed — it is **who carries the retry**. `ops.*` gives
|
|
35
|
+
you a job: this service promises to finish it, which is what buys progress, cancellation, webhooks
|
|
36
|
+
and an `asset_id`. `images.*` runs while you wait and stores nothing, because you are still holding
|
|
37
|
+
the input, so a failure costs you one re-send. AI ops and batches are always jobs.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
pip install imagestep
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`ImageStep()` with no arguments reads `IMAGESTEP_API_KEY` (and `IMAGESTEP_BASE_URL`, default
|
|
46
|
+
`https://api.imagestep.dev`). `AsyncImageStep` has the identical surface with every method
|
|
47
|
+
awaitable — `async with AsyncImageStep() as client: await client.ops.upscale(...)`, and that
|
|
48
|
+
includes `images.*` (`await client.images.transform("resize", file=…, parameters={"width": 1200})`).
|
|
49
|
+
|
|
50
|
+
`construct_webhook_event` raises `WebhookSignatureError` when the signature does not check out; the
|
|
51
|
+
JavaScript SDK throws a plain `Error` there, and takes its key as an argument rather than from the
|
|
52
|
+
environment (it also runs on edge runtimes that have no `process.env`).
|
|
53
|
+
|
|
54
|
+
## What you get
|
|
55
|
+
|
|
56
|
+
| | |
|
|
57
|
+
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `client.ops.run(op, variants=[...])` | one call, one asset per variant — the whole set of social sizes (contract §8) |
|
|
59
|
+
| `client.ops` | `list` `get` `run` `estimate` `remove_bg` `upscale` `restore_face` `colorize` `analyze` `generate` `edit` `resize` `convert` `compress` `crop` `pad` `grayscale` `rotate` `flip` `flop` `trim` `flatten` `adjust` `mask` `blur_region` `overlay` `caption` `read_metadata` — the atomic-op vocabulary: `run(op, **opts)` submits any op as a job, `estimate` prices one without creating anything, and the rest are one helper per op |
|
|
60
|
+
| `client.images` | `sync_endpoints` `supports` `transform` `transform_result` `render` `metadata` — the synchronous face (contract §9): `transform(op, file=… \| url=… \| asset_id=…, parameters={…})` → `bytes`, `transform_result(…)` → a `BinaryResult` with `content_type` / `width` / `height` as well, `render(template_id, data)` → PNG, `metadata(file)` → dict. Which ops may go this way is `GET /api/v1/ops`, never a list in this package |
|
|
61
|
+
| `client.assets` | `upload` `upload_many` `from_url` `wait_ready` `status` `download` `get` `list` `iterate` `collections` `iterate_collections` `rename_collection` `publish` `unpublish` `set_collection` `tag` `delete` — `upload()` takes a path · bytes · a binary file object, dedupes by sha1 and waits for ingest; `upload_many()` does many at once (one stage and one finish call per 500, `concurrency` PUTs, one status call per tick); `from_url()` has the SERVICE fetch each link instead, 20 to a request |
|
|
62
|
+
| `client.jobs` | `submit` `estimate` `get` `items` `iterate_items` `list` `iterate` `cancel` `resume` `wait` `outputs` — `wait(job_id, on_progress=…)` waits for completion (the service holds each read open — `GET /jobs/{id}?wait=` — so a five-second job costs one request, not a poll loop) and `outputs(job)` reads what it produced as list rows — one paged `GET /assets?job_id=`, not one read per item (#441) — or subscribe to `job.completed` webhooks instead of polling. The totals by status and type have no method — `client.get("/api/v1/jobs/counts")`; for one status, `list(status=…, per_page=1)` and read `meta["total"]` |
|
|
63
|
+
| `client.presets` | `list` `get` `create` `update` `delete` `delete_version` `import_` `run` — versioned lists of steps you save once and run by slug: `presets.run(slug, asset_ids, wait=True)` (`"slug@3"` pins version 3); `presets.import_()` (keyword clash); `presets.create({"name": …, "steps": [{"op": "generate", "prompt": "{{subject.hero}} on a rooftop"}], "subjects": [{"name": "hero", "referenceAssetIds": [asset_id], "descriptor": "a matte black bottle…"}]})` — the images pin the geometry (max 4 across all subjects, sent with the preset's `generate` / `edit` step) and the descriptor pins the words, expanding into the prompt wherever you write `{{subject.hero}}`; `get(slug)` exports `steps` + `subjects` + `version` + `versions`. A preset whose steps mix a model with other steps runs as **one** `chain` job, the image in between handed on for you; `jobs.estimate` prices it per segment in `steps` A preset keeps 50 versions on record: at the ceiling `update` is `422 resource_limit_exceeded` rather than dropping the oldest, and `delete_version(slug, n)` makes room — `slug@n` answers 404 from then on, so it is for versions nothing pins. |
|
|
64
|
+
| `client.templates` | `list` `iterate` `get` `versions` `create` `update` `delete` `import_` — HTML/CSS render templates (#24 / #234); `list` is one page of rows without html / css (#497), `get` the whole document; versioned: `update` saves `version + 1`, and `id@version` reads or renders one frozen version; a batch is a job: `ops.run("render_template", template_id=…, items=[…])` |
|
|
65
|
+
| `client.models` | `list` — the model catalogue with prices; `list("ai_image")` (default) or `list("analyze")` |
|
|
66
|
+
| `client.webhooks` | `list` `get` `create` `update` `delete` `rotate_secret` `test` `deliveries` `iterate_deliveries` `verify` `construct_event` — `create()` answers the signing secret once; `verify(raw_body, header, secret)` and `construct_event(…)` check a delivery's signature in your own handler |
|
|
67
|
+
| `client.agent` | `guidelines` `feedback` `reports` `iterate_reports` — what this API expects of an agent, and the channel for telling us an op you needed is missing |
|
|
68
|
+
| `client.usage` | `get` — credits charged, jobs created and items settled over a window, grouped by `op`, `key` or `day` (contract §11) |
|
|
69
|
+
| `ImageStepError` | every failure the API answers: `code` (closed set), `retryable` (what to branch on — without the service's own, true for a 429 or a 5xx), `param`, `details`, `retry_after`, and `request_id` — `error.requestId`, else the `X-Request-Id` header; quote it when reporting a failure (contract §11). Writes carry an `Idempotency-Key` per call, reused across the SDK's own retries; pass `idempotency_key` to make your own retry the same submission |
|
|
70
|
+
|
|
71
|
+
## Pagination
|
|
72
|
+
|
|
73
|
+
Every list takes `page` (from 0) and `per_page` (100 by default and at most) and answers a `Page` —
|
|
74
|
+
`items` plus `meta` (`total`, `page`, `perPage`, `hasMore`, `nextCursor`). Out of range is clamped, not
|
|
75
|
+
refused, and `meta` reports the page actually served. Pass `cursor=meta["nextCursor"]` instead of a page
|
|
76
|
+
number to read the rows after it: the service counts nothing then (no `total`, no `page`), and page 1 000
|
|
77
|
+
costs what page 1 did.
|
|
78
|
+
|
|
79
|
+
Don't write the loop — each listing has an iterator that walks to the end, following the `nextCursor` each
|
|
80
|
+
*answer* carries:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
for asset in client.assets.iterate(collection="shoot-01"):
|
|
84
|
+
print(asset["id"])
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`assets.iterate` · `assets.iterate_collections` · `jobs.iterate` · `jobs.iterate_items(id)` · `templates.iterate` ·
|
|
88
|
+
`webhooks.iterate_deliveries(id)` · `agent.iterate_reports`, on both clients (`async for` on `AsyncImageStep`).
|
|
89
|
+
Pass `cursor=` (or `page=`) to resume a walk.
|
|
90
|
+
|
|
91
|
+
Every signature, parameter and return shape: **<https://imagestep.dev/docs/sdk>** — one reference for
|
|
92
|
+
both SDKs, held against these sources by a test (imagestep#297).
|