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_nsfw
  • image.is_safe
  • image.category
  • image.caption
  • image.scene
  • image.moderation
  • image.nouns
  • image.verbs
  • image.services

Scene summary

image.scene is derived from content_analysis and provides a product-shaped summary:

  • image.scene.type
  • image.scene.intimacy
  • image.scene.activity
  • image.scene.activities
  • image.scene.anatomy_exposed
  • image.scene.people
  • image.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