v1 · Beta

API Reference

The MarkMyAI API adds four layers of verifiable provenance to AI-generated images: invisible watermark, C2PA publisher signature, audit trail with fingerprint recovery, and blockchain anchoring on Polygon.

Base URL

https://www.markmyai.com/api

Protocol

HTTPS only

Format

JSON (application/json)

Auth

Bearer token

Beta: The API is fully functional. C2PA manifests use test certificates — ContentCredentials.org will show "Unknown Signer". Production certificates will be issued before public launch.

Authentication

All API requests require a Bearer token in the Authorization header. Get your key from the Dashboard.

bash
curl -X POST https://www.markmyai.com/api/v1/mark \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://example.com/image.jpg"}'

API keys are prefixed with mk_live_.

Keys are shown once at generation time. Store them securely — we only save a hash.

Never expose your key in client-side code or public repositories.

Rate Limits

PlanRequests / secondMarks / month
Free2 / sec20
Starter5 / sec200
Business20 / sec2,000
Enterprise50 / secCustom
POST/v1/mark

Mark an Image

Creates an official publisher version of an AI image with four protection layers: invisible watermark (pixel-level), C2PA publisher signature, audit trail with fingerprint, and blockchain anchor. Returns a permanent verify URL.

Request Body

FieldTypeDescription
image_url*stringPublicly accessible URL of the image to mark. JPEG, PNG or WebP. Max 12 MB.
ai_modelstringName of the AI model used to generate the image (e.g. 'dall-e-3', 'midjourney-v6').
creatorstringName of the organization or individual publishing the image.
purposestringIntended use of the image (e.g. 'marketing', 'editorial', 'social').
wm_variantstringWatermark variant: 'standard' (default, best all-round) or 'crop-resilient' (optimized for heavy cropping, e.g. social media thumbnails).
bash
curl -X POST https://www.markmyai.com/api/v1/mark \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://example.com/ai-generated.jpg",
    "ai_model": "dall-e-3",
    "creator": "Acme Corp",
    "purpose": "marketing"
  }'

Response

json
{
  "status": "success",
  "mark_id": "mk_a3f9b2e4d1",
  "marked_image_url": "https://... (fresh signed delivery URL)",
  "delivery_channel": "api",
  "file_expires_at": "2026-03-11T10:30:00.000Z",
  "file_retention_days": 1,
  "verify_url": "https://markmyai.com/api/verify/mk_a3f9b2e4d1",
  "layers": {
    "watermark": {
      "status": "embedded",
      "type": "trustmark",
      "note": "Invisible watermark in pixel data, designed to survive common transformations"
    },
    "c2pa_embedded": {
      "status": "signed",
      "signer": "MarkMyAI (Beta)",
      "standard": "C2PA v2"
    },
    "audit_log": {
      "status": "recorded",
      "entry_hash": "a7f3c...",
      "fingerprint": "f0e1d2c3..."
    },
    "blockchain_anchor": {
      "status": "pending",
      "chain": "polygon",
      "note": "Anchoring in progress. Confirmation typically within 30 seconds."
    }
  },
  "image": {
    "sha256": "3a9f...",
    "fingerprint": "f0e1d2c3...",
    "format": "jpeg",
    "width": 1024,
    "height": 1024,
    "size_bytes": 245760
  },
  "timestamp": "2026-03-06T10:30:00.000Z",
  "request_id": "req_TAVQgHELicx6"
}

Important: publish the marked version

The marked_image_url is a short-lived signed URL for immediate delivery. Store the marked image right away. API delivery files are retained for 24 hours. If you need a fresh URL during that window, call GET /v1/download/:mark_id. The verify_url is permanent.

GET/v1/download/:mark_id

Get a Fresh Download URL

Returns a fresh signed download URL for the marked delivery file that belongs to your API key. Use this if the original marked_image_url has expired but the delivery file is still within its retention window.

bash
curl https://www.markmyai.com/api/v1/download/mk_a3f9b2e4d1 \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx"

Response

json
{
  "status": "success",
  "mark_id": "mk_a3f9b2e4d1",
  "download_url": "https://... (signed URL, valid 1h)",
  "expires_in_seconds": 3600
}

If the file has expired

json
{
  "error": "Marked file no longer available",
  "code": "delivery_file_expired",
  "mark_id": "mk_a3f9b2e4d1",
  "verify_url": "https://markmyai.com/api/verify/mk_a3f9b2e4d1",
  "proof_pdf_url": "https://markmyai.com/api/proof-pdf/mk_a3f9b2e4d1",
  "proof_json_url": "https://markmyai.com/api/proof/mk_a3f9b2e4d1"
}

Retention policy

API delivery files are retained for 24 hours. After that, this endpoint returns 410 Gone. The proof record, manifest, verify URL, Proof PDF and Proof JSON remain available.

POST/v1/detect

Detect a Mark

Checks whether an image was previously processed by MarkMyAI. Uses four detection methods: C2PA metadata, invisible watermark, exact hash match, and perceptual fingerprint. Can recover a proof path even after compression, resizing, or metadata stripping.

Request Body

FieldTypeDescription
image_url*stringURL of the image to check. JPEG, PNG or WebP.
bash
curl -X POST https://www.markmyai.com/api/v1/detect \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://example.com/suspected-ai-image.jpg"}'

Response

json
{
  "status": "success",
  "is_marked": true,
  "query_fingerprint": "f0e1d2c3...",
  "c2pa": {
    "embedded": true,
    "data": { "...full C2PA manifest JSON..." }
  },
  "watermark": {
    "found": true,
    "crc_valid": true,
    "match_mark_id": "mk_a3f9b2e4d1",
    "match_token": "a3f9b2e4d1",
    "payload_hash": "7c3e...",
    "encoding": "BCH_SUPER",
    "decode_ms": 4200
  },
  "database_match": {
    "found": true,
    "method": "c2pa_embedded",
    "mark_id": "mk_a3f9b2e4d1",
    "verify_url": "https://markmyai.com/api/verify/mk_a3f9b2e4d1",
    "audit_record": {
      "creator": "Acme Corp",
      "ai_model": "dall-e-3",
      "marked_at": "2026-03-06T10:30:00.000Z"
    }
  },
  "fingerprint_matches": [
    {
      "mark_id": "mk_a3f9b2e4d1",
      "verify_url": "https://markmyai.com/api/verify/mk_a3f9b2e4d1",
      "creator": "Acme Corp",
      "ai_model": "dall-e-3",
      "marked_at": "2026-03-06T10:30:00.000Z",
      "similarity": "100%"
    }
  ],
  "recovery_paths": {
    "c2pa": "Embedded C2PA manifest found",
    "watermark": "Invisible watermark recovered — linked to mk_a3f9b2e4d1",
    "fingerprint": "Fingerprint match: https://markmyai.com/api/verify/mk_a3f9b2e4d1",
    "watermark_verify_url": null
  },
  "image": {
    "fingerprint": "f0e1d2c3..."
  },
  "request_id": "req_TAVQgHELicx6"
}

Detection Logic

The endpoint runs all four detection methods in parallel. is_marked is true when at least one of the following succeeds:

c2pa_embedded

Embedded publisher proof found in image metadata

watermark

Invisible watermark detected and CRC validated

sha256_exact

Exact pixel match in proof history

fingerprint_fuzzy

Perceptual match after common transforms

Response Fields

FieldDescription
is_markedOverall verdict. True if any of the four detection layers returned a positive signal.
c2pa.embeddedWhether a valid C2PA manifest was found and cryptographically validated using the @contentauth/c2pa-node reader.
c2pa.dataFull parsed C2PA manifest JSON (signer, claim generator, actions, ingredients). Null if no C2PA found.
watermark.foundWhether a TrustMark invisible watermark was detected in the pixel data.
watermark.crc_validWhether the extracted watermark payload passes CRC integrity check. A valid CRC confirms the watermark is genuine.
watermark.match_mark_idThe mark_id recovered from the watermark payload, if identifiable. Links back to the original proof chain.
watermark.encodingWatermark encoding scheme used (BCH_SUPER for current marks, BCH_5 for legacy).
watermark.decode_msTime in milliseconds to decode the watermark. Typical: 3–8 seconds depending on image size.
database_matchBest matching record from the audit trail, found via perceptual fingerprint. Includes mark_id, verify_url, creator, and AI model.
fingerprint_matchesUp to 5 closest fingerprint matches from the audit log, sorted by similarity. Each includes a verify URL and similarity percentage.
recovery_pathsHuman-readable summary of which layers succeeded and how to follow the proof chain for each.

Performance note

Watermark decoding adds 3–8 seconds to the response time. The endpoint has a maxDuration of 60 seconds. If you only need C2PA + fingerprint without watermark, the response is typically under 2 seconds.

POST/api/check-watermark

Check Watermark, C2PA & Fingerprint

Public endpoint used by the free checker. Accepts an image as multipart form data and runs three detection methods: invisible watermark recovery (TrustMark), C2PA extraction (via c2pa-node with regex fallback), and perceptual fingerprint matching against the audit trail. Fingerprint matching is especially important for CMS-resized or re-encoded renditions where the watermark may have been lost. No authentication required.

Request (multipart/form-data)

FieldTypeDescription
image*fileThe image file to analyze. JPEG, PNG, or WebP. Max 20 MB.
mark_idstringOptional mark ID to verify against (enables targeted watermark decoding).
bash
curl -X POST https://www.markmyai.com/api/check-watermark \
  -F "image=@photo.jpg" \
  -F "mark_id=mk_a3f9b2e4d1"

Response

json
{
  "watermark_found": true,
  "crc_valid": true,
  "match_mark_id": "mk_a3f9b2e4d1",
  "match_token": "a3f9b2e4d1",
  "payload_hash": "7c3e...",
  "token_hex": "a3f9b2e4d1",
  "version": "1",
  "variant": "standard",
  "encoding": "BCH_SUPER",
  "decode_ms": 4200,
  "c2pa_found": true,
  "c2pa_claim_generator": "OpenAI DALL·E 3",
  "c2pa_signer": "OpenAI, Inc.",
  "c2pa_title": null,
  "fingerprint_match": {
    "mark_id": "mk_a3f9b2e4d1",
    "similarity": "98%",
    "creator": "Acme Corp",
    "ai_model": "dall-e-3",
    "verify_url": "https://markmyai.com/api/verify/mk_a3f9b2e4d1",
    "marked_at": "2026-03-06T10:30:00.000Z"
  },
  "query_fingerprint": "f0e1d2c3..."
}

C2PA Fields

The endpoint extracts C2PA metadata server-side using the @contentauth/c2pa-node Reader for proper CBOR/JUMBF parsing. If the Reader cannot process the file (e.g. truncated manifests), it falls back to regex pattern matching on the raw binary data. This two-layer approach reliably recovers generator and signer information from images produced by OpenAI, Adobe, Google, Midjourney, and other C2PA-enabled platforms.

FieldDescription
c2pa_foundWhether C2PA / JUMBF / Content Credentials markers were detected in the file.
c2pa_claim_generatorThe tool or platform that generated the image (e.g. "OpenAI DALL·E 3", "Adobe Firefly", "MarkMyAI"). Parsed from the C2PA manifest via c2pa-node Reader, with regex fallback. Null if not identifiable.
c2pa_signerThe organization that signed the C2PA manifest, extracted from the X.509 certificate or signature_info. Null if not identifiable.
c2pa_titleThe title field from the C2PA manifest, if present.

Fingerprint Matching

In addition to watermark and C2PA analysis, the endpoint computes a perceptual fingerprint of the uploaded image and compares it against the MarkMyAI audit trail. If a match is found (Hamming distance < 10), the response includes the best match with its mark_id, which is then used as an effectiveToken for targeted watermark decoding. This allows recovery of provenance even when the invisible watermark has been destroyed by aggressive resizing, re-encoding, or screenshot capture.

FieldDescription
fingerprint_matchBest matching record from the audit trail. null if no similar image was found.
fingerprint_match.mark_idThe mark_id of the best matching audit log entry.
fingerprint_match.similaritySimilarity percentage between the uploaded image and the matched record (e.g. "98%").
fingerprint_match.creatorThe creator who originally marked the image.
fingerprint_match.ai_modelThe AI model declared when the image was originally marked.
fingerprint_match.verify_urlDirect link to the full proof record for the matched mark.
fingerprint_match.marked_atISO timestamp of when the matched image was originally marked.
query_fingerprintThe perceptual fingerprint computed for the uploaded image (hex string). Useful for debugging.

Recovery via fingerprint

When the watermark cannot be decoded (e.g. image was resized by a CMS, converted to a different format, or heavily compressed), the fingerprint match provides a fallback path to recover the full proof chain. The public checker at markmyai.com/check shows this as "Recovered Provenance" with a link to the original publisher proof.

GET/api/verify/:mark_id

Verify a Mark

Public endpoint. Returns the publisher proof record for a given mark ID, including provenance, audit data, and blockchain state when available. No authentication required.

bash
curl https://markmyai.com/api/verify/mk_a3f9b2e4d1

Response

json
{
  "mark_id": "mk_a3f9b2e4d1",
  "status": "verified",
  "proof_level": "verified_provenance",
  "proof_level_description": "Core provenance signals found and verified.",
  "proof_pdf_url": "https://markmyai.com/api/proof-pdf/mk_a3f9b2e4d1",
  "proof_json_url": "https://markmyai.com/api/proof/mk_a3f9b2e4d1",
  "provenance": {
    "creator": "Acme Corp",
    "ai_model": "dall-e-3",
    "remote_manifest": "https://markmyai.com/api/verify/mk_a3f9b2e4d1"
  },
  "image": {
    "fingerprint": "f0e1d2c3...",
    "sha256": "3a9f...",
    "watermark_embedded": true
  },
  "audit": {
    "entry_hash": "a7f3c...",
    "previous_hash": "6b2d1..."
  },
  "blockchain": {
    "status": "anchored",
    "anchor_hash": "0xa3f9...",
    "tx_hash": "0x9bc4...",
    "chain": "polygon",
    "anchored_at": "2026-03-06T10:30:25.000Z",
    "explorer_url": "https://polygonscan.com/tx/0x9bc4..."
  },
  "delivery": {
    "channel": "api",
    "retention_days": 1,
    "file_expires_at": "2026-03-11T10:30:00.000Z",
    "file_available": false,
    "storage_deleted_at": "2026-03-11T10:31:02.000Z"
  },
  "marked_at": "2026-03-06T10:30:00.000Z"
}

Proof Levels

The proof_level field provides a human-readable assessment:

LevelMeaning
verified_provenanceCore provenance signals found and verified. Origin traceable with high confidence to a publishing organization.
recovered_provenanceEmbedded metadata was stripped, but recovery signals (invisible watermark, audit trail) connect this image to a verified proof chain.
no_verifiable_provenanceNo reliable provenance signals found through MarkMyAI.

Proof Export

Every verified mark has downloadable proof in two formats:

Proof PDF — for humans (legal, compliance, print):

bash
curl https://markmyai.com/api/proof-pdf/mk_a3f9b2e4d1 -o proof.pdf

Proof JSON — for systems (DAMs, archives, automation):

bash
curl https://markmyai.com/api/proof/mk_a3f9b2e4d1 -o proof.json

Both formats are self-contained: they include the blockchain anchor strings (on-chain pseudonymized + plaintext) and instructions for independent verification without MarkMyAI servers.

Recommended: Store the Proof JSON as a sidecar file alongside the image (image.jpg + image.proof.json) for searchable, machine-readable proof archival.

Errors

All errors return a JSON body with an error field and a request_id for debugging.

StatusCodeMeaning
401UnauthorizedMissing or invalid API key
400Bad RequestMissing required field (e.g. image_url)
410GoneTemporary delivery file has expired; use proof links instead
413Payload Too LargeImage exceeds 12 MB limit
422UnprocessableInvalid image format or image too large (>10k×10k px)
429Rate LimitedToo many requests or monthly limit reached
504TimeoutImage download took too long (>5s)
500Server ErrorInternal error — contact support with request_id

Code Examples

Mark an image in your language of choice.

Python

python
import requests

API_KEY = "mk_live_xxxxxxxxxxxx"
BASE_URL = "https://www.markmyai.com/api"

def mark_image(image_url: str, ai_model: str, creator: str):
    response = requests.post(
        f"{BASE_URL}/v1/mark",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "image_url": image_url,
            "ai_model": ai_model,
            "creator": creator,
        }
    )
    response.raise_for_status()
    data = response.json()
    print(f"Marked! verify_url: {data['verify_url']}")
    return data

result = mark_image(
    image_url="https://example.com/my-ai-image.jpg",
    ai_model="dall-e-3",
    creator="My Company"
)

# Detect: C2PA + Watermark + Fingerprint + DB in one call
def detect_image(image_url: str):
    response = requests.post(
        f"{BASE_URL}/v1/detect",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"image_url": image_url}
    )
    response.raise_for_status()
    data = response.json()
    print(f"Marked: {data['is_marked']}")
    if data["watermark"]["found"]:
        print(f"  Watermark: CRC valid={data['watermark']['crc_valid']}")
    if data["c2pa"]["embedded"]:
        print(f"  C2PA: embedded manifest found")
    if data["fingerprint_matches"]:
        print(f"  DB match: {data['fingerprint_matches'][0]['mark_id']}")
    return data

detect_image("https://example.com/suspicious-image.jpg")

# Free checker: watermark + C2PA + fingerprint (no auth)
def check_image(file_path: str):
    with open(file_path, "rb") as f:
        response = requests.post(
            f"{BASE_URL.replace('/api', '')}/api/check-watermark",
            files={"image": f}
        )
    response.raise_for_status()
    data = response.json()
    if data.get("fingerprint_match"):
        print(f"  Fingerprint match: {data['fingerprint_match']['mark_id']}")
    return data

check_image("photo.jpg")

Node.js

javascript
const API_KEY = "mk_live_xxxxxxxxxxxx";

async function markImage(imageUrl, aiModel, creator) {
  const res = await fetch("https://www.markmyai.com/api/v1/mark", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ image_url: imageUrl, ai_model: aiModel, creator }),
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  const data = await res.json();
  console.log("Marked! verify_url:", data.verify_url);
  return data;
}

await markImage(
  "https://example.com/my-ai-image.jpg",
  "dall-e-3",
  "My Company"
);

// Detect: C2PA + Watermark + Fingerprint + DB in one call
async function detectImage(imageUrl) {
  const res = await fetch("https://www.markmyai.com/api/v1/detect", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ image_url: imageUrl }),
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  const data = await res.json();
  console.log("Marked:", data.is_marked);
  console.log("Watermark:", data.watermark);
  console.log("C2PA:", data.c2pa.embedded);
  console.log("DB matches:", data.fingerprint_matches.length);
  return data;
}

await detectImage("https://example.com/suspicious-image.jpg");

// Free checker: watermark + C2PA + fingerprint (no auth)
async function checkImage(file) {
  const form = new FormData();
  form.append("image", file);

  const res = await fetch("https://www.markmyai.com/api/check-watermark", {
    method: "POST",
    body: form,
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  const data = await res.json();
  if (data.fingerprint_match) {
    console.log("Fingerprint match:", data.fingerprint_match.mark_id);
  }
  return data;
}

cURL

bash
# Mark an image
curl -X POST https://www.markmyai.com/api/v1/mark \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://example.com/image.jpg",
    "ai_model": "dall-e-3",
    "creator": "My Company"
  }' > response.json

# Detect: check all four layers at once
curl -X POST https://www.markmyai.com/api/v1/detect \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://example.com/suspicious-image.jpg"}'

# Free checker: watermark + C2PA + fingerprint (no auth needed)
curl -X POST https://www.markmyai.com/api/check-watermark \
  -F "image=@photo.jpg"

# Request a fresh download URL later
curl https://www.markmyai.com/api/v1/download/mk_a3f9b2e4d1 \
  -H "Authorization: Bearer mk_live_xxxxxxxxxxxx"

# Verify a mark (no auth needed)
curl https://markmyai.com/api/verify/mk_a3f9b2e4d1

PHP

php
<?php
$apiKey = "mk_live_xxxxxxxxxxxx";

$response = file_get_contents("https://www.markmyai.com/api/v1/mark", false,
  stream_context_create([
    "http" => [
      "method" => "POST",
      "header" => [
        "Authorization: Bearer {$apiKey}",
        "Content-Type: application/json",
      ],
      "content" => json_encode([
        "image_url" => "https://example.com/image.jpg",
        "ai_model"  => "dall-e-3",
        "creator"   => "My Company",
      ]),
    ],
  ])
);

$data = json_decode($response, true);
echo "verify_url: " . $data["verify_url"];

Ready to add verifiable provenance to your AI images?

Analytics Consent

We use Google Analytics 4 only if you agree, to understand which pages bring traffic and where visitors drop off. No advertising features are enabled. You can change your choice at any time in the privacy settings.