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.
Install
pip install kennmarq # the cloud API: no dependencies
pip install "kennmarq[local]" # local mode
pip install "kennmarq[safety]" # the safety check on your machine tooThe SDK reads your key from KENNMARQ_API_KEY, or takes api_key=. Python 3.10 or later.
In the cloud
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
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 latermark() 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
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
InvalidSignedFileand 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=Falsekeeps none. - The watermark's model (65 MB) downloads once, to
~/.cache/kennmarq/modelsorKENNMARQ_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
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 ContentBlockedThe 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
Kennmarq().verify("render.marked.png") # free, no key neededErrors
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.