Skip to content

Python SDK

Mark AI images, songs and clips from Python in one line: in the cloud, or images on your own machine, where they never leave it. The safety check and verification are included. MIT-licensed; the cloud client needs no dependencies.

Coming to PyPI soon.

Install

Shell
pip install kennmarq                 # the cloud API: no dependencies
pip install "kennmarq[local]"        # local mode
pip install "kennmarq[safety]"       # the safety check on your machine too

The SDK reads your key from KENNMARQ_API_KEY, or takes api_key=. Python 3.10 or later.

In the cloud

Python
from kennmarq import Kennmarq

k = Kennmarq()
result = k.mark("render.png", "render.marked.png", source="created", model="FLUX.1 schnell")
print(result.mark_id, result.watermark_id, result.marks_used)

The picture goes to POST /v1/mark: our servers in Zurich mark it in memory and keep nothing. A file over 4 MB, up to 25 MB, goes up first through POST /v1/uploads, by itself; check() and verify() do the same (an upload takes a key).

Songs and clips

Python
song = k.mark("track.mp3", "track.marked.mp3", model="Song Model 2")
print(song.marks_counted, song.audio_watermark)        # 15 (five per started minute), "claimed"

clip = k.mark("clip.mp4", "clip.marked.mp4", check="ai-act")   # waits for the job, then downloads
print(clip.watermark_id, clip.marks_counted)

job = k.mark("long.mp4", wait=False)                    # or start it now…
clip = k.wait_for(job.mark_id, "long.marked.mp4")        # …and fetch it later

mark() takes MP3, WAV and M4A songs and MP4 clips too, in the cloud: a clip is a job, which mark() waits for (up to 40 minutes) before downloading the marked clip; a clip up to 1 GB goes up first by itself. A blocked frame raises ContentBlocked, with the frame in safety. Local mode marks pictures only, for now.

On your machine

Python
result = k.mark_file("render.png", "render.marked.png", source="created", model="FLUX.1 schnell")
assert result.mode == "local"
  • The watermark and the Content Credentials are made on your machine. Only the manifest goes to POST /v1/sign, about 13 KB and never pixels, to be signed and time-stamped. Each file counts one mark.
  • Before writing anything, the SDK reads the file with our roots: it must read Trusted, with a validated time-stamp and its watermark in place. Otherwise it raises InvalidSignedFile and writes nothing.
  • Credentials the file already carries are kept as an ingredient, as the Code of Practice asks, and travel with the manifest; keep_input_credentials=False keeps none.
  • The watermark's model (65 MB) downloads once, to ~/.cache/kennmarq/models or KENNMARQ_MODELS_DIR, checked against its pinned SHA-256.
  • Where the C2PA library has no build (Windows, for now), the watermark is made on your machine and the manifest in the cloud: the watermarked picture is sent to POST /v1/mark (mode == "hybrid").
  • Without a key, watermark_file() writes the watermark and the IPTC AI label alone, with no Content Credentials.

The safety check

Python
answer = k.check("render.png", policy="ai-act")              # in the cloud: two marks
answer = k.check("render.png", policy="ai-act", local=True)  # on your machine: nothing sent, nothing counted
result = k.mark_file("render.png", "out.png", check="ai-act")  # a block raises ContentBlocked

The same check as POST /v1/check, with the same models and the same error rates. On your machine its models download once, about 2.3 GB.

Verify a file

Python
Kennmarq().verify("render.marked.png")   # free, no key needed

Errors

API errors raise KennmarqError, with the API's code, status and request_id (errors). ContentBlocked carries the check's answer in safety; MissingApiKey, LocalModeUnavailable and InvalidSignedFile say what's missing.