API Reference
Complete REST API documentation for TruthMark invisible watermarking service.
Base URL
https://truthmark-api.onrender.comFor local development: http://localhost:8000
Authentication
Include your API key in the X-API-Key header for all authenticated requests:
X-API-Key: tm_live_your_api_key_hereImportant: Free tier allows unauthenticated requests with lower rate limits (10 req/min). Paid plans require API key authentication for full rate limits and features.
/v1/encode
Embed an invisible watermark into an image. The watermark uses spread-spectrum DCT embedding with HKDF key derivation, AES-256 encryption, and Reed-Solomon error correction.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | multipart/form-data | Yes | Image file (PNG, JPG, JPEG). Max 25MB. For robust profile use 512x512+ recommended. |
message | string (form field) | Yes | Text payload to embed (max 500 characters). Supports UTF-8, JSON strings, etc. |
profile | string (form field) | No (default: auto) | auto, robust, or compact. Auto tries robust first and falls back to compact for smaller images. |
payload_mode | string (form field) | No (default: auto) | auto, full, or compact. Compact reduces payload overhead for tiny images. |
Tip: If you watermark small images (icons, thumbnails), use profile=compact with payload_mode=compact and keep messages short. For best resilience against compression and screenshots, use profile=robust on medium or large images.
Example Request
curl -X POST https://truthmark-api.onrender.com/v1/encode \
-H "X-API-Key: tm_live_abc123" \
-F "file=@photo.png" \
-F "message=Copyright 2025 - My Company" -F "profile=auto" -F "payload_mode=auto"Success Response 200 OK
{
"status": "success",
"metadata": {
"psnr": 42.5, // Peak signal-to-noise ratio (higher = better quality)
"bits_embedded": 256, // Number of data bits embedded
"profile_used": "robust",
"payload_mode_used": "full",
"image_width": 1920,
"image_height": 1080
},
"download_url": "https://truthmark-api.onrender.com/download/wm_abc123.png",
"filename": "truthmark-protected.png"
}/v1/decode
Extract a hidden watermark from an image. Returns the embedded message and a confidence score indicating extraction reliability.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | multipart/form-data | Yes | Possibly watermarked image file (PNG, JPG, JPEG) |
Example Request
curl -X POST https://truthmark-api.onrender.com/v1/decode \
-H "X-API-Key: tm_live_abc123" \
-F "file=@watermarked.png"Response — Watermark Found 200 OK
{
"found": true,
"message": "Copyright 2025 - My Company",
"confidence": 0.95 // 0.0 to 1.0 — higher is more reliable
}Response — No Watermark
{
"found": false,
"message": null,
"confidence": 0.0
}/v1/verify
Lightweight check for watermark presence. Faster than /v1/decode — returns only whether the image is watermarked and a confidence score, without decrypting the payload.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | multipart/form-data | Yes | Image file to check (PNG, JPG, JPEG) |
Example Request
curl -X POST https://truthmark-api.onrender.com/v1/verify \
-H "X-API-Key: tm_live_abc123" \
-F "file=@image.png"Response 200 OK
{
"watermarked": true,
"confidence": 0.95,
"sync_confidence": 0.92
}Quick Examples by Language
Python (requests)
import requests
BASE = "https://truthmark-api.onrender.com"
HEADERS = {"X-API-Key": "tm_live_abc123"}
# Encode
with open("photo.png", "rb") as f:
r = requests.post(f"{BASE}/v1/encode",
headers=HEADERS,
files={"file": f},
data={"message": "© 2025 MyCompany"})
print(r.json()["download_url"])
# Decode
with open("watermarked.png", "rb") as f:
r = requests.post(f"{BASE}/v1/decode",
headers=HEADERS, files={"file": f})
print(r.json())JavaScript (fetch)
const BASE = "https://truthmark-api.onrender.com";
// Encode
const form = new FormData();
form.append("file", fileInput.files[0]);
form.append("message", "© 2025 MyCompany");
const res = await fetch(`${BASE}/v1/encode`, {
method: "POST",
headers: { "X-API-Key": "tm_live_abc123" },
body: form,
});
const { download_url } = await res.json();
// Decode
const decodeForm = new FormData();
decodeForm.append("file", watermarkedFile);
const decoded = await fetch(`${BASE}/v1/decode`, {
method: "POST",
headers: { "X-API-Key": "tm_live_abc123" },
body: decodeForm,
}).then(r => r.json());Error Handling
All errors return appropriate HTTP status codes with a JSON error body:
{
"detail": "Error message description"
}Status Codes
| Code | Meaning | Resolution |
|---|---|---|
200 | Success | Request processed successfully |
400 | Bad Request | Check message length (≤ 500 chars), profile value, and image capacity for your payload |
401 | Unauthorized | Verify your API key in X-API-Key header |
413 | Payload Too Large | Image exceeds 25MB limit |
429 | Rate Limited | Wait and retry. Check Retry-After header. |
500 | Server Error | Retry request. If persistent, contact support. |
Rate Limits
Rate limits are enforced per API key. Exceeding the limit returns 429.
| Plan | Requests/Minute | Images/Month |
|---|---|---|
| Free | 10 | 100 |
| Starter | 60 | 2,500 (+ overage at $0.008/img) |
| Professional | 300 | 25,000 (+ overage at $0.004/img) |
| Enterprise | Custom | Unlimited |
Best Practices
Use images ≥ 512x512 pixels
Larger images provide more frequency-domain capacity, resulting in higher PSNR and better extraction confidence.
Keep messages under 200 characters
Shorter payloads embed more redundantly and extract more reliably. Use JSON for structured data.
Check confidence scores
Only trust decoded messages with confidence > 0.7. Lower scores may indicate image degradation.
Use PNG for maximum quality
PNG is lossless — encoding into PNG yields the highest PSNR. JPEG is supported but introduces minor quality reduction.
Start Building
Choose your SDK and start embedding watermarks in minutes.