Skip to content
AI Act Disclosure Kit

C2PA marking API

Mark AI-generated images in a machine-readable way, as required by EU AI Act Article 50(2). Send an image, receive the same image back with an embedded C2PA Content Credentials manifest whose c2pa.actions assertion declares digitalSourceType = trainedAlgorithmicMedia (the IPTC code for AI-generated media).

Try it now — no API key needed

Upload an image (JPEG/PNG/WebP, max 2 MB, 5 per day) and get it back with a real C2PA manifest embedded by our production signing service.

API reference

POST /api/c2pa/sign

Base URL: https://aiact.zalize.com

Request

  • Auth: Authorization: Bearer aiact_… — your API key is on the dashboard after signing in. Requires a Growth (2,000 images/mo) or Agency (20,000 images/mo) plan.
  • Headers: Content-Type must be image/jpeg, image/png or image/webp.
  • Body:the raw image bytes (no multipart, no base64). Max 15 MB.

Response

  • 200: the signed image in the same format, as binary body with Content-Disposition: attachment.
  • X-C2PA-Usage header: used/limit for the current calendar month (UTC), e.g. 12/2000.
  • Errors: JSON body { "error": "…" } — see the table below.

Quotas & rate limits

  • Monthly image quota by plan: Growth 2,000 / Agency 20,000 (resets on the 1st, UTC).
  • Requests with invalid API keys are limited to 20/hour per IP.
  • Playground (no key): 5 images/day per IP, 2 MB max, shared daily capacity.
StatusErrorHow to fix
400empty bodySend the raw image bytes as the request body.
401missing or malformed API key / invalid API keyUse Authorization: Bearer aiact_… with the key from your dashboard. Repeated invalid-key attempts are rate limited per IP.
403plan without API accessThe C2PA API requires a Growth or Agency plan.
413image too largeMax 15 MB (API) / 2 MB (playground).
415unsupported content-typeContent-Type must be image/jpeg, image/png or image/webp.
422could not sign imageThe bytes were not a readable image of the declared type.
429quota exhausted / rate limitedMonthly quota used up (X-C2PA-Usage header tracks usage), or playground daily limit reached.
502signing service errorTransient upstream problem — retry with backoff.
503C2PA service not configuredMaintenance window — retry later.

Examples

curl

curl -X POST https://aiact.zalize.com/api/c2pa/sign \
  -H "Authorization: Bearer aiact_YOUR_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @image.jpg \
  -o signed.jpg

Node.js

const res = await fetch("https://aiact.zalize.com/api/c2pa/sign", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIACT_API_KEY}`,
    "Content-Type": "image/png",
  },
  body: imageBuffer, // raw bytes
});
if (!res.ok) throw new Error((await res.json()).error);
console.log("quota:", res.headers.get("X-C2PA-Usage")); // e.g. "12/2000"
const signed = Buffer.from(await res.arrayBuffer());

Python

import os, requests

with open("image.jpg", "rb") as f:
    r = requests.post(
        "https://aiact.zalize.com/api/c2pa/sign",
        headers={
            "Authorization": f"Bearer {os.environ['AIACT_API_KEY']}",
            "Content-Type": "image/jpeg",
        },
        data=f.read(),
    )
r.raise_for_status()
open("signed.jpg", "wb").write(r.content)

Verify the result

# Inspect the embedded manifest with the official c2patool
c2patool signed.jpg

# or online: https://contentcredentials.org/verify
Manifests are currently signed with our own certificate chain. They are cryptographically valid and readable by any C2PA tool, but verifiers that check the C2PA public trust list will show the signer as “unknown” until our CA-issued signing certificate is live.

Need higher volume? See plans or contact us.