Python SDK
The Python SDK is the recommended integration path for Python applications. It handles authentication, uploads, polling, retries, result parsing, and streaming.
Installation
The SDK requires Python 3.9 or newer.
pip install ice9
Image censoring is optional. Install Pillow only if your application calls
image.moderation.censor():
pip install Pillow
Authentication
Pass your API key directly or use ICE9_API_KEY:
export ICE9_API_KEY=ice9_...
from ice9 import Ice9
client = Ice9()
Quickstart
from ice9 import Ice9
client = Ice9(api_key="ice9_...")
image = client.analyze("photo.jpg")
print(image.is_nsfw)
print(image.category)
print(image.scene.type if image.scene else None)
print(image.moderation.reason)
Input types
analyze() accepts:
- local file paths
- image URLs
- open binary file objects
result = client.analyze("photo.jpg")
result = client.analyze("https://example.com/photo.jpg")
with open("photo.jpg", "rb") as f:
result = client.analyze(f)
JPEG, PNG, WebP, HEIC, and HEIF file objects are identified from their file
signatures. For in-memory uploads you may also supply media_type and
filename; the SDK validates the media type and normalizes the filename
extension to match the detected image:
with open("photo.webp", "rb") as f:
image = client.analyze(
f,
media_type="image/webp",
filename="upload.webp",
)
Unsupported or unidentifiable bytes fail locally with ImageRejectedError.
Image URLs are downloaded by the SDK, limited to 10 MB, and then uploaded to
Ice9.
Sync and async clients
Use Ice9 for scripts and notebooks:
from ice9 import Ice9
client = Ice9(api_key="ice9_...")
image = client.analyze("photo.jpg", tier="basic")
print(image.caption)
Use AsyncIce9 for async applications:
from ice9 import AsyncIce9
async with AsyncIce9(api_key="ice9_...") as client:
image = await client.analyze("photo.jpg", tier="basic")
print(image.caption)
Tiers and service discovery
If you omit tier, the SDK uses the baseline basic tier.
image = client.analyze("photo.jpg")
image = client.analyze("photo.jpg", tier="basic")
tiers = client.tiers()
services = client.services()
See API Reference for the raw tier definitions.
Result model
The SDK exposes image-level helpers first and keeps raw service outputs available under image.services.
Core helpers
image.is_nsfwimage.is_safeimage.categoryimage.captionimage.sceneimage.moderationimage.nounsimage.verbsimage.services
Scene summary
image.scene is derived from content_analysis and provides a product-shaped summary:
image.scene.typeimage.scene.intimacyimage.scene.activityimage.scene.activitiesimage.scene.anatomy_exposedimage.scene.peopleimage.scene.gender
Moderation helpers
image = client.analyze("photo.jpg")
if image.is_nsfw:
print(image.moderation.reason)
image.moderation.censor("photo.jpg", output="photo-censored.jpg")
The default censoring policy covers exposed genitalia, breasts, buttocks, and
anus detections at confidence 0.5 or higher. Pass labels or
min_confidence to define your own policy. Censoring supports fill and
pixelate, returns a Pillow image, and optionally saves it to output.
Pillow is not imported for analysis or result parsing. Applications that do not use censoring do not need to install it.
Raw service access
Use image.services when you need service-level payloads:
print(image.services.nudenet)
print(image.services.colors)
print(image.services.metadata)
Streaming
Use stream=True to receive partial AnalysisResult objects as services complete:
from ice9 import Ice9
client = Ice9(api_key="ice9_...")
for partial in client.analyze("photo.jpg", tier="basic", stream=True):
if partial.is_complete:
print("final caption:", partial.caption)
else:
print("services seen:", partial.services_submitted)
For async applications:
from ice9 import AsyncIce9
async with AsyncIce9(api_key="ice9_...") as client:
async for partial in await client.analyze("photo.jpg", tier="basic", stream=True):
if partial.is_complete:
print("done")
Retrieving results later
Use the retrieval helpers if you already have an image_id:
status = client.get_status(1234)
result = client.get_result(1234)
get_status() returns the raw /status payload. get_result() returns a parsed AnalysisResult.
Timeouts, retries, and partial results
Both clients use a 95-second default analysis timeout. Set timeout in seconds
on the client or for an individual analysis. Network failures and server
errors are retried up to three times by default; configure this with
max_retries.
By default, an analysis with failed services raises PartialResultError. The
exception includes the partial result. Set raise_on_partial=False to return
it normally instead:
image = client.analyze(
"photo.jpg",
timeout=95,
raise_on_partial=False,
)
Serialization
Use to_dict() or to_json() to serialize results for other systems:
payload = image.to_dict()
pretty = image.to_json(indent=2)
Examples
Basic analysis
from ice9 import Ice9
client = Ice9(api_key="ice9_...")
image = client.analyze("photo.jpg", tier="basic")
print(image.caption)
print(image.category)
print(image.scene.type if image.scene else None)
for noun in image.nouns.validated if image.nouns else []:
print(noun["canonical"], noun["vote_count"])
Async analysis
from ice9 import AsyncIce9
async with AsyncIce9(api_key="ice9_...") as client:
image = await client.analyze("photo.jpg", tier="basic")
print(image.is_nsfw)
print(image.moderation.reason)
Full runnable examples
For complete scripts, notebooks, and local downloads:
- https://github.com/ice9innovations/ice9-sdk/tree/main/python/examples