Skip to Content
APIGetting Started with Pichr API

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:

  1. A Pichr account (sign up here )
  2. An API client (cURL, Postman, or your preferred programming language)
  3. 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

# 1. Request a presigned upload URL curl -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 file curl -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 upload curl -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 upload curl -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 up const { data: authData, error: authError } = await supabase.auth.signUp({ email: 'your@email.com', password: 'your-secure-password', }); // 2. Upload image async 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 images async 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 up def 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 image def 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 images def 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 up function 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 image function 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 images function 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:

CodeMeaning
200Success
201Created
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing token
403Forbidden - Insufficient permissions
404Not Found - Resource doesn’t exist
413Payload Too Large - File size exceeds limit
429Too Many Requests - Rate limit exceeded
500Internal Server Error
503Service 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

Need Help?

Last updated: 13 November 2025