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/v1Authentication
All endpoints support authentication via API keys:
Authorization: Bearer pk_your_api_key_hereLearn 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: 1700000000Rate 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