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.
- Create an account and get your API key from the dashboard
- Make a POST request to
/api/v1/verifywith a file or URL - Parse the JSON response for provenance data
Authentication
All API endpoints require a Bearer token in the Authorization header:
Authorization: Bearer provn_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAPI 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
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_FILE | Unsupported file format |
| 400 | FILE_TOO_LARGE | File exceeds 50MB limit |
| 400 | MISSING_FILE | No file in request body |
| 400 | MISSING_URL | No URL in request body |
| 400 | FETCH_FAILED | Unable to fetch file from URL |
| 401 | UNAUTHORIZED | Invalid or missing API key |
| 429 | RATE_LIMITED | Rate limit exceeded |
| 500 | INTERNAL_ERROR | Server error during verification |
All errors return the format: {"success": false, "error": {"code": "...", "message": "..."}}
Rate Limits
| Plan | Verifications/month | Rate Limit |
|---|---|---|
| Free | 100 | 10/min |
| Developer | 5,000 | 60/min |
| Pro | 25,000 | 200/min |
| Enterprise | Custom | Custom |
When you exceed a rate limit, the API returns a 429 status. Upgrade your plan at provn.dev/pricing.