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-Typemust beimage/jpeg,image/pngorimage/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-Usageheader:used/limitfor 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.
| Status | Error | How to fix |
|---|---|---|
| 400 | empty body | Send the raw image bytes as the request body. |
| 401 | missing or malformed API key / invalid API key | Use Authorization: Bearer aiact_… with the key from your dashboard. Repeated invalid-key attempts are rate limited per IP. |
| 403 | plan without API access | The C2PA API requires a Growth or Agency plan. |
| 413 | image too large | Max 15 MB (API) / 2 MB (playground). |
| 415 | unsupported content-type | Content-Type must be image/jpeg, image/png or image/webp. |
| 422 | could not sign image | The bytes were not a readable image of the declared type. |
| 429 | quota exhausted / rate limited | Monthly quota used up (X-C2PA-Usage header tracks usage), or playground daily limit reached. |
| 502 | signing service error | Transient upstream problem — retry with backoff. |
| 503 | C2PA service not configured | Maintenance 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.