API Documentation
Integrate EXIF Service into your applications, CMS, or workflows. All endpoints accept multipart file uploads and return processed images.
Getting Started
The EXIF Service API lets you programmatically process images — inject SEO metadata, strip EXIF data, add GPS coordinates, or read metadata. All file-processing endpoints accept multipart/form-data and return the processed file as a download.
Base URL: https://your-domain.com
Content Type: multipart/form-data for uploads
Max File Size: 25 MB per file
Authentication
Most endpoints require authentication. There are two methods:
API Key (Recommended for Integrations)
Generate an API key from your Dashboard → API Keys page (Pro plan required). Include it in the Authorization header:
Authorization: Bearer exs_your_api_key_here
API keys are prefixed with exs_. You can create up to 5 keys. Keys are hashed on our servers — the full key is only shown once at creation.
Session Cookies (Browser-based)
For browser-based usage, sign in via the web form or the auth API. Session cookies are sent automatically.
POST /api/auth/sign-in/email
Content-Type: application/json
{
"email": "user@example.com",
"password": "your-password"
}
Rate Limits
Usage is tracked per user (shared across web and API). Limits reset daily at midnight UTC.
| Plan | Images / Day | API Access | Bulk Upload |
|---|---|---|---|
| Free | 5 | — | — |
| Pro ($9/mo) | 300 | ✓ | ✓ |
When the limit is exceeded, the API returns 429 Too Many Requests with an upgradeUrl field to direct users to plan upgrade.
Storage and Retention
Processed files are persisted in object storage and linked to your account history. Files are downloadable for 24 hours, then automatically marked deleted (soft delete).
- Availability window: 24 hours from processing time.
- After expiry: history rows remain visible, but downloads return
410 Gone. - Soft delete: expired files are marked with
deleted=trueandsoftDeletedAt. - Purge: admins can permanently purge soft-deleted files using the admin files endpoint.
Retention policy summary:
- T+0 to T+24h: user download allowed
- After T+24h: soft deleted, download disabled
- Admin purge: permanent blob + DB delete
View EXIF Data
/api/images/view-exif
Public
Upload an image and get all its metadata as JSON. No authentication required. If GPS data is present, it's returned in a normalized gps field.
Fields
| file | Image file (required) |
Example
curl -X POST https://your-domain.com/api/images/view-exif \
-F "file=@photo.jpg"
Response
{
"ok": true,
"metadata": {
"Make": "Canon",
"Model": "EOS R5",
"ExposureTime": "1/250",
"GPSLatitude": 48.8566,
...
},
"gps": { "lat": 48.8566, "lng": 2.3522 }
}
SEO EXIF Injection
/api/images/seo-exif
Auth Required
Admin Only
Upload an image and inject SEO metadata (EXIF, IPTC, XMP). This endpoint is reserved for admin workflows only.
Fields
| file | Image file (required) |
| name | Author name (required) |
| title | Image title (required) |
| description | Image description (required) |
| destination | Destination (required) |
| city | City (required) |
| keywords | Comma-separated keywords (required) |
| category | Category (required) |
| url | URL / license (required) |
| webFileName | Custom download filename (optional) |
Example
curl -X POST https://your-domain.com/api/images/seo-exif \
-H "Authorization: Bearer exs_your_api_key" \
-F "file=@photo.jpg" \
-F "name=John Doe" \
-F "title=Sunset Beach" \
-F "description=Beautiful sunset at the beach" \
-F "destination=Hawaii" \
-F "city=Honolulu" \
-F "keywords=sunset,beach,hawaii" \
-F "category=Travel" \
-F "url=https://example.com" \
--output seo-photo.jpg
SEO Metadata Editor (Simple)
/api/images/edit-exif
Auth Required
Upload an image and set common SEO metadata fields (title, description, author, keywords, copyright), plus rights/licensing fields used in Google and IPTC metadata workflows. Returns the processed image as a file download.
Fields
| file | Image file (required) |
| title | Image title (optional) |
| description | Image description (optional) |
| author | Author / artist name (optional) |
| keywords | Comma-separated keywords (optional) |
| copyright | Copyright notice (optional) |
| creditLine | Credit line / attribution text (optional) |
| source | IPTC source/origin text (optional) |
| webStatementUrl | Rights or license page URL (optional) |
| licensorName | Licensor display name (optional) |
| licensorUrl | Licensor URL/contact page (optional) |
| usageTerms | Usage terms / legal instructions (optional) |
| creatorUrl | Creator website URL (optional) |
| creatorEmail | Creator contact email (optional) |
| digitalSourceType | IPTC DigitalSourceType URI (optional) |
| mode | append (default) or overwrite |
Example
curl -X POST https://your-domain.com/api/images/edit-exif \
-H "Authorization: Bearer exs_your_api_key" \
-F "file=@photo.jpg" \
-F "title=Mountain Sunset" \
-F "description=A beautiful sunset over the mountains" \
-F "author=Jane Doe" \
-F "keywords=sunset,mountains,landscape" \
-F "copyright=2026 Jane Doe" \
-F "creditLine=Jane Doe Photography" \
-F "source=EXIFCut" \
-F "webStatementUrl=https://example.com/license/mountain-sunset" \
-F "licensorName=Jane Doe Licensing" \
-F "licensorUrl=https://example.com/contact-license" \
-F "usageTerms=Editorial use only unless licensed" \
-F "creatorUrl=https://janedoe.com" \
-F "creatorEmail=rights@janedoe.com" \
-F "digitalSourceType=http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia" \
--output edited-photo.jpg
Advanced EXIF Editor
/api/images/advanced-edit
Auth Required
Upload an image and set arbitrary EXIF/IPTC/XMP fields using a JSON object. Supports any writable ExifTool tag including nested fields with dot notation (e.g. IPTC.Keywords). Returns the processed image as a file download.
Fields
| file | Image file (required) |
| fields | JSON string of key-value pairs to write (required) |
| mode | append (default) or overwrite — overwrite strips existing metadata first |
Example
curl -X POST https://your-domain.com/api/images/advanced-edit \
-H "Authorization: Bearer exs_your_api_key" \
-F "file=@photo.jpg" \
-F 'fields={"Title":"Sunset","Artist":"Jane","Orientation":1,"GPSLatitude":48.8566,"GPSLongitude":2.3522}' \
-F "mode=append" \
--output edited-photo.jpg
Bulk Advanced Edit
/api/images/advanced-edit-bulk
Auth Required
Pro
Upload up to 20 images and apply the same metadata fields to all of them. Returns a ZIP archive containing all edited images.
Fields
| files | Image files, up to 20 (required). Use multiple -F "files=@..." flags. |
| fields | JSON string of key-value pairs to write (required) |
| mode | append (default) or overwrite |
Example
curl -X POST https://your-domain.com/api/images/advanced-edit-bulk \
-H "Authorization: Bearer exs_your_api_key" \
-F "files=@photo1.jpg" \
-F "files=@photo2.jpg" \
-F "files=@photo3.jpg" \
-F 'fields={"Copyright":"2026 Jane Doe","Artist":"Jane Doe"}' \
--output edited-images.zip
Strip EXIF Data
/api/images/strip-exif
Auth Required
Upload an image and remove all EXIF metadata. Returns the clean image as a file download.
Fields
| file | Image file (required) |
Example
curl -X POST https://your-domain.com/api/images/strip-exif \
-H "Authorization: Bearer exs_your_api_key" \
-F "file=@photo.jpg" \
--output clean-photo.jpg
Bulk Strip EXIF
/api/images/strip-exif-bulk
Auth Required
Pro
Upload up to 20 images and strip all metadata. Returns a ZIP archive containing all cleaned images.
Fields
| files | Image files, up to 20 (required). Use multiple -F "files=@..." flags. |
Example
curl -X POST https://your-domain.com/api/images/strip-exif-bulk \
-H "Authorization: Bearer exs_your_api_key" \
-F "files=@photo1.jpg" \
-F "files=@photo2.jpg" \
-F "files=@photo3.jpg" \
--output cleaned-images.zip
Geo Data Injection
/api/images/geo-exif
Auth Required
Upload an image and inject GPS coordinates. Returns the geotagged image as a file download.
Fields
| file | Image file (required) |
| latitude | GPS latitude, -90 to 90 (required) |
| longitude | GPS longitude, -180 to 180 (required) |
| altitude | Altitude in meters (optional) |
Example
curl -X POST https://your-domain.com/api/images/geo-exif \
-H "Authorization: Bearer exs_your_api_key" \
-F "file=@photo.jpg" \
-F "latitude=48.8566" \
-F "longitude=2.3522" \
-F "altitude=35" \
--output geotagged-photo.jpg
Processing History
/api/images/history
Auth Required
Returns the authenticated user's last 50 processed images with lifecycle status. Set Accept: application/json for JSON output, otherwise returns an HTML fragment for dashboard rendering.
Example
curl https://your-domain.com/api/images/history \
-H "Authorization: Bearer exs_your_api_key" \
-H "Accept: application/json"
JSON Response
{
"ok": true,
"images": [
{
"id": "...",
"fileName": "photo.jpg",
"tool": "strip-exif",
"createdAt": "2026-03-01T12:00:00.000Z",
"downloadExpiresAt": "2026-03-02T12:00:00.000Z",
"status": "available",
"canDownload": true,
"downloadUrl": "/api/images/history/.../download"
}
]
}
History Download
/api/images/history/:id/download
Auth Required
Downloads a previously processed file owned by the authenticated user. The endpoint enforces the 24-hour retention window.
Path Params
| id | History item id from /api/images/history |
Example
curl -L https://your-domain.com/api/images/history/65f.../download \
-H "Authorization: Bearer exs_your_api_key" \
--output my-image.jpg
Status Codes
| 200 | File stream download started |
| 404 | Record not found |
| 410 | Download window expired |
API Key Management
API keys are available to Pro users and are used for server-to-server access. Use session auth in the dashboard to create and revoke keys.
GET /api/keys
Lists masked keys for the authenticated user with creation and last-used timestamps.
POST /api/keys
Creates a new API key. Request body: name (optional). Returns the raw key once.
curl -X POST https://your-domain.com/api/keys \
-H "Content-Type: application/json" \
-H "Cookie: your_session_cookie" \
-d '{"name":"CI Pipeline"}'
DELETE /api/keys/:id
Revokes a key by id for the authenticated owner.
Billing API
Billing endpoints require an authenticated user session and are used by the dashboard to manage upgrades and subscriptions.
POST /api/billing/checkout
Creates a Stripe Checkout session for the Pro plan and returns url for redirect.
POST /api/billing/portal
Creates a Stripe Billing Portal session so users can manage their active subscription.
GET /api/billing/status
Returns current plan, daily usage, plan limit, and global limits for free/pro plans.
Feedback API
Feedback and feature requests can be submitted by authenticated users or public visitors.
/api/feedback
Public
Fields
| type | feedback or feature |
| subject | Short summary (optional) |
| message | Required, minimum 10 characters |
| name | Optional display name |
| Required for guests, optional for authenticated users | |
| sourcePath | Optional source page/path hint |
curl -X POST https://your-domain.com/api/feedback \
-H "Content-Type: application/json" \
-d '{
"type": "feature",
"subject": "Bulk presets",
"message": "Please add reusable metadata presets for bulk edits.",
"name": "Jane",
"email": "jane@example.com",
"sourcePath": "/dashboard/feedback"
}'
Admin File Management
These endpoints require an authenticated admin user session.
GET /api/admin/files
Returns an HTML table of uploaded files with user, status, and download action. Supports query params: status=all|available|deleted, userId, limit.
GET /api/admin/files/:id/download
Downloads a stored file by id for support or compliance workflows.
POST /api/admin/files/purge
Permanently deletes soft-deleted files older than 24 hours from storage and database.
curl -X POST https://your-domain.com/api/admin/files/purge \
-H "Cookie: your_admin_session_cookie"
Error Codes
All errors return JSON with ok: false and a message field. Plan-related errors also include error and upgradeUrl.
| Status | Meaning |
|---|---|
| 400 | Bad Request — missing or invalid fields |
| 401 | Unauthorized — missing or invalid API key / session |
| 403 | Forbidden — feature requires a higher plan (e.g., API access needs Pro) |
| 429 | Too Many Requests — daily usage limit exceeded |
| 500 | Server Error — unexpected error during processing |
Plan Limit Error Example
{
"ok": false,
"error": "limit_reached",
"message": "Daily limit of 5 images reached. Upgrade your plan for more.",
"upgradeUrl": "/pricing"
}