API Endpoints Reference
Complete reference for all public Pichr API endpoints accessible with API keys. All requests should be made to https://api.pichr.io/api/v1.
Base URL
https://api.pichr.io/api/v1
Authentication
All endpoints support authentication via API keys:
Authorization: Bearer pk_your_api_key_here
Learn more in the Authentication Guide.
Upload Endpoints
Base path: /upload
POST /upload/presign
Request presigned URL for file upload.
Headers: Authorization: Bearer {api_key} (optional for anonymous uploads)
Request Body:
{ "filename": "image.jpg", "mime": "image/jpeg", "bytes": 524288, "sha256": "abc123...", "title": "My Image", "description": "Image description", "visibility": "public", "expiresIn": 24}Response:
{ "uploadId": "upl_xyz789", "uploadUrl": "https://api.pichr.io/api/v1/upload/direct/{fileId}", "fileId": "file_abc123", "r2Key": "uploads/2025-11-13/file_abc123/image.jpg", "message": "File record created..."}Error Responses:
413 Payload Too Large: File exceeds size limit for plan402 Payment Required: Storage quota exceeded429 Too Many Requests: Rate limit exceeded
PUT /upload/direct/:fileId
Upload file data directly to R2.
Headers:
Authorization: Bearer {api_key}Content-Type: {mime-type}
Body: Binary file data
Response:
{ "fileId": "file_abc123", "url": "https://i.pichr.io/file_abc123", "mime": "image/jpeg", "bytes": 524288, "status": "ready"}POST /upload/finalize
Finalize upload and trigger NSFW detection.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "uploadId": "upl_xyz789", "fileId": "file_abc123", "r2Key": "uploads/2025-11-13/file_abc123/image.jpg", "r2Etag": "etag-value", "width": 1920, "height": 1080, "phash": "abc123", "nsfwScore": 0.05}Response:
{ "fileId": "file_abc123", "url": "https://i.pichr.io/file_abc123", "cdnUrl": "https://cdn.pichr.io/...", "mime": "image/jpeg", "bytes": 524288, "width": 1920, "height": 1080, "nsfwScore": 0.05, "ageRestricted": false, "visibility": "public", "expiresAt": null, "phash": "abc123", "duplicates": []}POST /upload/multipart/init
Initialize multipart upload for large files.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "filename": "large-file.jpg", "mime": "image/jpeg", "totalBytes": 52428800, "partSize": 5242880}Response: Upload ID and part upload URLs
POST /upload/multipart/complete
Complete multipart upload.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "uploadId": "upl_xyz789", "fileId": "file_abc123", "r2Key": "uploads/...", "parts": [ { "partNumber": 1, "etag": "etag1" }, { "partNumber": 2, "etag": "etag2" } ]}Response: File ID and ETag
File Management Endpoints
Base path: /files
GET /files
List your uploaded files.
Headers: Authorization: Bearer {api_key}
Query Parameters:
limit(int): Max results (1-100, default: 50)offset(int): Pagination offset (default: 0)sort(enum): Sort field -created,views,size(default:created)order(enum): Sort order -asc,desc(default:desc)visibility(enum): Filter by visibility -public,unlisted,privateexcludeNsfw(boolean): Exclude NSFW content (default:true)
Response:
{ "files": [ { "id": "file_abc123", "url": "https://i.pichr.io/file_abc123", "thumbnailUrl": "https://cdn.pichr.io/t/file_abc123", "title": "My Image", "mime": "image/jpeg", "bytes": 524288, "width": 1920, "height": 1080, "viewCount": 42, "createdAt": "2025-11-13T10:00:00Z" } ], "pagination": { "total": 25, "limit": 50, "offset": 0, "hasMore": false }}GET /files/:id
Get file metadata and details.
Headers: Authorization: Bearer {api_key} (required for private files)
Response:
{ "id": "file_abc123", "url": "https://i.pichr.io/file_abc123", "cdnUrl": "https://cdn.pichr.io/...", "title": "My Image", "description": "Image description", "mime": "image/jpeg", "bytes": 524288, "width": 1920, "height": 1080, "visibility": "public", "viewCount": 42, "downloadCount": 5, "nsfwScore": 0.05, "ageRestricted": false, "tags": ["nature", "landscape"], "createdAt": "2025-11-13T10:00:00Z", "expiresAt": null}Error Responses:
403 Forbidden: Content restricted by safe mode404 Not Found: File doesn't exist410 Gone: File was banned or deleted
PATCH /files/:id
Update file metadata.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "title": "Updated Title", "description": "Updated description", "visibility": "unlisted", "hotlinkPolicy": "whitelist", "tags": ["updated", "tags"], "expiresIn": 168}Response: Success message with updated fields
DELETE /files/:id
Delete a file (soft delete).
Headers: Authorization: Bearer {api_key}
Response: Success message
Note: Queues file for cleanup from R2 storage.
GET /files/:id/download
Download file with Content-Disposition header.
Headers: Authorization: Bearer {api_key} (required for private files)
Response: Binary file data with download headers
Album Endpoints
Base path: /albums
POST /albums
Create a new album.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "title": "My Album", "description": "Album description", "fileIds": ["file_abc123", "file_def456"], "visibility": "public", "password": "optional-password", "tags": ["vacation", "2025"]}Response:
{ "id": "alb_xyz789", "title": "My Album", "description": "Album description", "visibility": "public", "tags": ["vacation", "2025"], "createdAt": "2025-11-13T10:00:00Z", "url": "https://pichr.io/a/alb_xyz789"}GET /albums
List your albums.
Headers: Authorization: Bearer {api_key}
Query Parameters:
limit(int): Max results (1-100, default: 50)offset(int): Pagination offset (default: 0)
Response: List of albums with file counts
GET /albums/:id
Get album details and files.
Headers:
Authorization: Bearer {api_key}(required for private albums)X-Album-Password: {password}(if password-protected)
Response:
{ "id": "alb_xyz789", "title": "My Album", "description": "Album description", "visibility": "public", "tags": ["vacation", "2025"], "viewCount": 150, "fileCount": 25, "createdAt": "2025-11-13T10:00:00Z", "updatedAt": "2025-11-13T12:00:00Z", "files": [...]}PATCH /albums/:id
Update album metadata.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "title": "Updated Album", "description": "New description", "visibility": "unlisted", "password": "new-password", "tags": ["updated"], "coverImageId": "file_abc123"}Response: Success message
DELETE /albums/:id
Delete an album (doesn't delete files).
Headers: Authorization: Bearer {api_key}
Response: Success message
POST /albums/:id/files
Add files to an album.
Headers: Authorization: Bearer {api_key}
Request Body:
{ "fileIds": ["file_abc123", "file_def456", "file_ghi789"]}Response:
{ "message": "Files added successfully", "added": 3, "requested": 3}DELETE /albums/:id/files/:fileId
Remove a file from an album.
Headers: Authorization: Bearer {api_key}
Response: Success message
Moderation Endpoint
Base path: /moderation
POST /moderation/report
Report content for moderation review.
Request Body:
{ "fileId": "file_abc123", "reason": "adult_content", "details": "This image contains inappropriate content", "email": "reporter@example.com"}Reason Options: csam, terrorism, adult_content, violence, harassment, hate_speech, self_harm, spam, copyright, other
Response:
{ "message": "Report submitted successfully", "reportId": "rep_xyz789", "complianceMessage": "Thank you for reporting...", "status": "pending"}Note: High-priority reports (CSAM, terrorism) trigger automatic actions and are escalated immediately. This endpoint does not require authentication.
Error Responses
All endpoints follow consistent error response format:
{ "error": "Error type", "message": "Human-readable error message", "details": {...}}HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized (Invalid or missing API key) |
| 403 | Forbidden |
| 404 | Not Found |
| 413 | Payload Too Large |
| 429 | Rate Limit Exceeded |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
Rate Limit Headers
All responses include rate limit headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1700000000
Rate Limits by Plan
| Plan | Uploads/Hour | API Calls/Hour |
|---|---|---|
| Anonymous | 5 | 100 |
| Free | 20 | 1,000 |
| Pro | 100 | 10,000 |
| Enterprise | 1,000 | 100,000 |
What's Not Included?
This documentation covers public endpoints accessible with API keys. The following features require web application login and are NOT accessible via API keys:
- Account management (signup, login, logout)
- API key management (create, list, revoke keys)
- Subscription management (billing, upgrades)
- GDPR data export and account deletion
- Moderation queue management
- Admin panel features
These features are available through the web application at pichr.io.
Next Steps
- Getting Started - Build your first integration
- Authentication - Get your API key and learn about authentication
- API Overview - API introduction and features
Last updated: 13 November 2025