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/v1For local development:
http://localhost:8787/api/v1Step 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 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:
| 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: 1700000000Next 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