photo2url Developer API
Turn photo2url into an image pipeline for AI agents and automation: upload an image with an API key, receive CDN-ready URLs, and get structured metadata back. Create keys from the API Keys page in your dashboard.
Authentication
Every request requires a key from your dashboard, sent as a Bearer token. Keep keys secret — the full key is shown only once, at creation.
Authorization: Bearer p2k_YOUR_API_KEY
Disable a key from the dashboard to revoke it immediately; delete it to remove it permanently.
Upload Image
Upload one image per request as multipart/form-data. The file field is file. Supported formats: PNG, JPG, WEBP, GIF.
POST https://photo2url.com/api/v1/images/upload Content-Type: multipart/form-data Authorization: Bearer p2k_YOUR_API_KEY file (required) image bytes expires (optional, reserved for a future phase)
Analyze Image
Analyze a previously uploaded image and receive structured, machine-readable information: dimensions, format, transparency, animation, and deterministic technical-quality signals. Analysis is computed locally from image headers — there is no vision model, and the result is not an “AI quality score”.
A typical agent workflow is upload → file_id → analyze → decide:
POST /api/v1/images/analyze
Content-Type: application/json
Authorization: Bearer p2k_YOUR_API_KEY
{ "file_id": "b2d0f1c6-0000-0000-0000-000000000000" }The file must belong to the authenticated account. A file_id that does not exist or belongs to another user returns the same 404 file_not_found. Results are cached per file, so repeated analyses are cheap.
Successful response:
HTTP/1.1 200 OK
{
"success": true,
"request_id": "0f26a4a4-...",
"data": {
"file_id": "b2d0f1c6-...",
"metadata": {
"width": 1200,
"height": 800,
"format": "png",
"mime_type": "image/png",
"file_size": 412900,
"aspect_ratio": 1.5
},
"properties": {
"has_transparency": true,
"animated": false
},
"technical_quality": {
"score": 100,
"issues": []
}
}
}technical_quality.issues currently reports very_low_resolution, low_resolution, extreme_aspect_ratio, and inefficient_file using fixed, documented thresholds. Orientation is not returned because EXIF metadata is stripped before storage.
Response Format
A successful upload returns CDN URLs plus the file identifier:
HTTP/1.1 200 OK
{
"success": true,
"request_id": "0f26a4a4-...",
"data": {
"file_id": "b2d0f1c6-...",
"url": "https://cdn.photo2url.com/uploads/2026/09/....jpg",
"markdown": "",
"html": "<img src=\"https://cdn.photo2url.com/uploads/...\" alt=\"photo\" />"
}
}Errors
Failures use non-2xx status codes and a machine-readable error code. Responses never include stack traces, database internals, or storage details.
| Status | Error | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing, unknown, wrong, or disabled key |
| 403 | account_suspended | Key is valid but the account is suspended |
| 400 | invalid_file | Missing file, unsupported type, or content mismatch |
| 400 | file_too_large | File exceeds your plan limit |
| 400 | storage_full | Your plan storage is full |
| 400 | invalid_request | Analyze body is not JSON or has no valid file_id |
| 400 | unsupported_file | Stored file is not a supported image or is malformed |
| 404 | file_not_found | File does not exist or belongs to another account |
| 503 | service_unavailable | Storage is not configured (local/dev) |
| 500 | server_error | Unexpected failure; retry with the request_id |
{
"success": false,
"error": "invalid_api_key",
"request_id": "0f26a4a4-..."
}Examples
curl:
curl -X POST \ -H "Authorization: Bearer p2k_YOUR_API_KEY" \ -F [email protected] \ https://photo2url.com/api/v1/images/upload
curl (analyze):
curl -X POST \
-H "Authorization: Bearer p2k_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"file_id":"b2d0f1c6-0000-0000-0000-000000000000"}' \
https://photo2url.com/api/v1/images/analyzeNode.js / fetch:
const form = new FormData();
const file = new File([bytes], "photo.png", { type: "image/png" });
form.append("file", file);
const res = await fetch("https://photo2url.com/api/v1/images/upload", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
});
const json = await res.json();Node.js / fetch (analyze):
const res = await fetch(
"https://photo2url.com/api/v1/images/analyze",
{
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ file_id: fileId }),
}
);
const json = await res.json();Python:
import requests
resp = requests.post(
"https://photo2url.com/api/v1/images/analyze",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"file_id": file_id},
timeout=60,
)
data = resp.json()["data"]
print(data["metadata"], data["properties"], data["technical_quality"])Agent workflow example:
# 1. Agent receives an image
# 2. Upload it, get back file_id + CDN url
# 3. Analyze the image with file_id
analysis = analyze(file_id)
# 4. Agent decides what to do next from structured facts
if analysis.metadata.width < 500:
resize_or_warn() # too small for the target use
elif analysis.properties.animated:
pick_first_frame() # animated files may not fit
else:
proceed(analysis)