API Documentation

Everything you need to integrate content provenance verification into your application.

Getting Started

Provn verifies C2PA (Coalition for Content Provenance and Authenticity) metadata in images and videos. It tells you whether a file has provenance data, who created it, and whether it was AI-generated.

  1. Create an account and get your API key from the dashboard
  2. Make a POST request to /api/v1/verify with a file or URL
  3. Parse the JSON response for provenance data

Authentication

All API endpoints require a Bearer token in the Authorization header:

Authorization: Bearer provn_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys start with provn_sk_ and can be created in your dashboard.

POST /api/v1/verify

Upload a file for C2PA verification. Supports JPEG, PNG, TIFF, WebP, MP4, and MOV. Max 50MB.

Request

curl -X POST https://provn.dev/api/v1/verify \
  -H "Authorization: Bearer provn_sk_xxx" \
  -F "file=@photo.jpg"

Response (provenance found)

{
  "success": true,
  "hasProvenance": true,
  "isValid": true,
  "activeManifest": {
    "title": "image.jpg",
    "claimGenerator": "Adobe Photoshop 25.0",
    "signatureInfo": {
      "issuer": "Adobe Inc.",
      "certSerialNumber": "...",
      "time": "2025-01-15T10:30:00Z"
    },
    "assertions": [
      {
        "label": "c2pa.actions",
        "data": {
          "actions": [
            { "action": "c2pa.created", "softwareAgent": "Adobe Photoshop 25.0" }
          ]
        }
      }
    ],
    "ingredients": [],
    "isAiGenerated": false
  },
  "validationStatus": [],
  "manifestCount": 1
}

Response (no provenance data)

{
  "success": true,
  "hasProvenance": false,
  "isValid": null,
  "activeManifest": null,
  "validationStatus": [],
  "manifestCount": 0
}

POST /api/v1/verify/url

Verify a file by URL. The server fetches the file and processes it. Same 50MB limit and response format.

Request

curl -X POST https://provn.dev/api/v1/verify/url \
  -H "Authorization: Bearer provn_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/photo.jpg"}'

Response shape is identical to POST /api/v1/verify.

GET /api/v1/usage

Get usage statistics for the authenticated API key.

Request

curl https://provn.dev/api/v1/usage \
  -H "Authorization: Bearer provn_sk_xxx"

Response

{
  "success": true,
  "usage": {
    "currentPeriod": {
      "start": "2025-02-01T00:00:00Z",
      "end": "2025-02-28T23:59:59Z",
      "verificationsUsed": 47,
      "verificationsLimit": 100,
      "remaining": 53
    },
    "plan": "free"
  }
}

Code Examples

JavaScript / Node.js

const form = new FormData();
form.append("file", fileInput.files[0]);

const res = await fetch("https://provn.dev/api/v1/verify", {
  method: "POST",
  headers: { Authorization: "Bearer provn_sk_xxx" },
  body: form,
});

const data = await res.json();

if (data.hasProvenance) {
  console.log("Valid:", data.isValid);
  console.log("AI generated:", data.activeManifest.isAiGenerated);
} else {
  console.log("No provenance data found");
}

Python

import requests

# File upload
with open("photo.jpg", "rb") as f:
    res = requests.post(
        "https://provn.dev/api/v1/verify",
        headers={"Authorization": "Bearer provn_sk_xxx"},
        files={"file": f},
    )

data = res.json()
print(f"Has provenance: {data['hasProvenance']}")
print(f"Is valid: {data['isValid']}")

# URL verification
res = requests.post(
    "https://provn.dev/api/v1/verify/url",
    headers={
        "Authorization": "Bearer provn_sk_xxx",
        "Content-Type": "application/json",
    },
    json={"url": "https://example.com/photo.jpg"},
)
print(res.json())

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

func main() {
    file, _ := os.Open("photo.jpg")
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, _ := writer.CreateFormFile("file", "photo.jpg")
    io.Copy(part, file)
    writer.Close()

    req, _ := http.NewRequest("POST", "https://provn.dev/api/v1/verify", body)
    req.Header.Set("Authorization", "Bearer provn_sk_xxx")
    req.Header.Set("Content-Type", writer.FormDataContentType())

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    fmt.Println(result)
}

Error Reference

HTTPCodeDescription
400INVALID_FILEUnsupported file format
400FILE_TOO_LARGEFile exceeds 50MB limit
400MISSING_FILENo file in request body
400MISSING_URLNo URL in request body
400FETCH_FAILEDUnable to fetch file from URL
401UNAUTHORIZEDInvalid or missing API key
429RATE_LIMITEDRate limit exceeded
500INTERNAL_ERRORServer error during verification

All errors return the format: {"success": false, "error": {"code": "...", "message": "..."}}

Rate Limits

PlanVerifications/monthRate Limit
Free10010/min
Developer5,00060/min
Pro25,000200/min
EnterpriseCustomCustom

When you exceed a rate limit, the API returns a 429 status. Upgrade your plan at provn.dev/pricing.