Getting Started with Pichr API
This guide will walk you through making your first API requests to Pichr, from authentication to uploading and managing images.
Prerequisites
Before you begin, you'll need:
- A Pichr account (sign up here)
- An API client (cURL, Postman, or your preferred programming language)
- Basic knowledge of REST APIs and HTTP requests
Base URL
All API requests should be made to:
https://api.pichr.io/api/v1
For local development:
http://localhost:8787/api/v1
Step 1: Create an Account
Sign up for a Pichr account via the API:
curl -X POST https://api.pichr.io/api/v1/auth/signup \ -H "Content-Type: application/json" \ -d '{ "email": "your@email.com", "password": "your-secure-password", "username": "your_username", "firstName": "Your", "lastName": "Name", "ageConfirmed": true }'Response:
{ "user": { "id": "usr_abc123", "email": "your@email.com", "username": "your_username", "role": "user", "plan": "free", "ageConfirmed": true }, "session": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "...", "expires_in": 3600 }}Save the access_token - you'll need it for authenticated requests.
Step 2: Authenticate
For subsequent requests, you can log in with your credentials:
curl -X POST https://api.pichr.io/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "your@email.com", "password": "your-secure-password" }'The response includes an access_token that you'll use for authenticated requests.
Step 3: Upload Your First Image
Option A: Direct Upload (Recommended for small files < 5MB)
# 1. Request a presigned upload URLcurl -X POST https://api.pichr.io/api/v1/upload/presign \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "filename": "cat.jpg", "mime": "image/jpeg", "bytes": 524288, "title": "My Cute Cat", "description": "A photo of my adorable cat", "visibility": "public" }'Response:
{ "uploadId": "upl_xyz789", "uploadUrl": "https://api.pichr.io/api/v1/upload/direct/file_abc123", "fileId": "file_abc123", "r2Key": "uploads/2025-11-13/file_abc123/cat.jpg", "message": "File record created. Use PUT request to upload file, then call /finalize"}# 2. Upload the filecurl -X PUT https://api.pichr.io/api/v1/upload/direct/file_abc123 \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: image/jpeg" \ --data-binary "@/path/to/cat.jpg"Response:
{ "fileId": "file_abc123", "url": "https://i.pichr.io/file_abc123", "mime": "image/jpeg", "bytes": 524288, "status": "ready"}Option B: Multipart Upload (For large files > 5MB)
# 1. Initialize multipart uploadcurl -X POST https://api.pichr.io/api/v1/upload/multipart/init \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "filename": "large-image.jpg", "mime": "image/jpeg", "totalBytes": 52428800, "partSize": 5242880 }'
# 2. Upload parts (repeat for each part)# 3. Complete uploadcurl -X POST https://api.pichr.io/api/v1/upload/multipart/complete \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "uploadId": "upl_xyz789", "fileId": "file_abc123", "r2Key": "uploads/2025-11-13/file_abc123/large-image.jpg", "parts": [ { "partNumber": 1, "etag": "etag1" }, { "partNumber": 2, "etag": "etag2" } ] }'Step 4: Retrieve Your Images
List all your uploaded images:
curl -X GET "https://api.pichr.io/api/v1/files?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"Get a specific image's metadata:
curl -X GET https://api.pichr.io/api/v1/files/file_abc123 \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{ "id": "file_abc123", "url": "https://i.pichr.io/file_abc123", "cdnUrl": "https://cdn.pichr.io/uploads/2025-11-13/file_abc123/cat.jpg", "title": "My Cute Cat", "description": "A photo of my adorable cat", "mime": "image/jpeg", "bytes": 524288, "width": 1920, "height": 1080, "visibility": "public", "viewCount": 42, "downloadCount": 5, "nsfwScore": 0.02, "ageRestricted": false, "tags": ["cat", "pet"], "createdAt": "2025-11-13T10:30:00Z", "expiresAt": null}Step 5: Create an Album
Organize your images into albums:
curl -X POST https://api.pichr.io/api/v1/albums \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Cat Photos Collection", "description": "All my favorite cat pictures", "visibility": "public", "fileIds": ["file_abc123", "file_def456"], "tags": ["cats", "pets", "animals"] }'Response:
{ "id": "alb_xyz789", "title": "Cat Photos Collection", "description": "All my favorite cat pictures", "visibility": "public", "tags": ["cats", "pets", "animals"], "createdAt": "2025-11-13T10:35:00Z", "url": "https://pichr.io/a/alb_xyz789"}Code Examples
JavaScript/TypeScript
// Install dependencies: npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://your-project.supabase.co', 'your-anon-key');
// 1. Sign upconst { data: authData, error: authError } = await supabase.auth.signUp({ email: 'your@email.com', password: 'your-secure-password',});
// 2. Upload imageasync function uploadImage(file: File, token: string) { // Request presign const presignRes = await fetch('https://api.pichr.io/api/v1/upload/presign', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ filename: file.name, mime: file.type, bytes: file.size, visibility: 'public', }), });
const { uploadUrl, fileId } = await presignRes.json();
// Upload file await fetch(uploadUrl, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': file.type, }, body: file, });
return fileId;}
// 3. List imagesasync function listImages(token: string) { const res = await fetch('https://api.pichr.io/api/v1/files', { headers: { 'Authorization': `Bearer ${token}`, }, });
return res.json();}Python
# Install dependencies: pip install requests
import requests
API_BASE = 'https://api.pichr.io/api/v1'
# 1. Sign updef signup(email, password, username): response = requests.post(f'{API_BASE}/auth/signup', json={ 'email': email, 'password': password, 'username': username, 'firstName': 'Your', 'lastName': 'Name', 'ageConfirmed': True }) return response.json()
# 2. Upload imagedef upload_image(file_path, token): # Get file info import os file_size = os.path.getsize(file_path) file_name = os.path.basename(file_path)
# Request presign presign_res = requests.post( f'{API_BASE}/upload/presign', headers={'Authorization': f'Bearer {token}'}, json={ 'filename': file_name, 'mime': 'image/jpeg', 'bytes': file_size, 'visibility': 'public' } )
presign_data = presign_res.json() upload_url = presign_data['uploadUrl'] file_id = presign_data['fileId']
# Upload file with open(file_path, 'rb') as f: upload_res = requests.put( upload_url, headers={'Authorization': f'Bearer {token}'}, data=f )
return file_id
# 3. List imagesdef list_images(token): response = requests.get( f'{API_BASE}/files', headers={'Authorization': f'Bearer {token}'} ) return response.json()PHP
<?php// Install dependencies: composer require guzzlehttp/guzzle
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://api.pichr.io/api/v1/']);
// 1. Sign upfunction signup($email, $password, $username) { global $client; $response = $client->post('auth/signup', [ 'json' => [ 'email' => $email, 'password' => $password, 'username' => $username, 'firstName' => 'Your', 'lastName' => 'Name', 'ageConfirmed' => true ] ]); return json_decode($response->getBody(), true);}
// 2. Upload imagefunction uploadImage($filePath, $token) { global $client;
// Request presign $presignRes = $client->post('upload/presign', [ 'headers' => ['Authorization' => "Bearer $token"], 'json' => [ 'filename' => basename($filePath), 'mime' => mime_content_type($filePath), 'bytes' => filesize($filePath), 'visibility' => 'public' ] ]);
$presignData = json_decode($presignRes->getBody(), true); $uploadUrl = $presignData['uploadUrl']; $fileId = $presignData['fileId'];
// Upload file $client->put($uploadUrl, [ 'headers' => ['Authorization' => "Bearer $token"], 'body' => fopen($filePath, 'r') ]);
return $fileId;}
// 3. List imagesfunction listImages($token) { global $client; $response = $client->get('files', [ 'headers' => ['Authorization' => "Bearer $token"] ]); return json_decode($response->getBody(), true);}?>Error Handling
The API uses conventional HTTP status codes:
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing token |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn't exist |
413 | Payload Too Large - File size exceeds limit |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error |
503 | Service Unavailable |
Error response format:
{ "error": "Rate limit exceeded", "retryAfter": 1700000000000, "remaining": 0}Rate Limit Headers
All responses include rate limit information:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1700000000
Next Steps
- Authentication Guide - Detailed auth flow and token management
- Upload Endpoints - Advanced upload options and multipart uploads
- File Management - Update, delete, and organize images
- Albums - Create and manage image collections
- Moderation - Report content and review moderation queue
Need Help?
- Email: support@pichr.io
- Documentation: docs.pichr.io
- Status Page: status.pichr.io
- GitHub Issues: github.com/perius/pichr/issues
Last updated: 13 November 2025