Authentication
Pichr uses API keys for programmatic access to the platform. API keys allow you to upload images, manage files, and organize albums from your applications.
Getting Started
1. Create an Account
First, create a Pichr account at pichr.io :
- Sign up with your email
- Choose a subscription plan (Free tier available)
- Verify your email address
2. Generate an API Key
- Log in to your Pichr account
- Navigate to Settings → API Keys
- Click “Create API Key”
- Give your key a descriptive name (e.g., “My Blog”, “Photo Upload App”)
- Select permissions (typically
uploadandread) - Set an expiry date (optional, recommended for better security)
- Click Create
⚠️ Important: Copy your API key immediately! It will only be shown once. Store it securely - treat it like a password.
3. Use Your API Key
Include your API key in the Authorization header of all requests:
curl -X GET https://api.pichr.io/api/v1/files \
-H "Authorization: Bearer pk_your_api_key_here"API Key Format
Pichr API keys follow this format:
pk_abc123defXYZ456...- Prefix: Always starts with
pk_ - Length: 32 characters after the prefix
- Character Set: Alphanumeric (base58)
This format is similar to API keys from other services like Stripe and GitHub.
What You Can Access
API keys are designed for developers integrating Pichr into their applications. They provide access to:
- ✅ Upload images (
/upload/*) - ✅ Manage your files (
/files/*) - ✅ Organize albums (
/albums/*) - ✅ Report content (
/moderation/report)
What API Keys Cannot Access
API keys are restricted from internal endpoints:
- ❌ Account management (signup, login, logout)
- ❌ API key management (you cannot use an API key to create/revoke API keys)
- ❌ Subscription management (billing, upgrades)
- ❌ GDPR data export / account deletion
- ❌ Moderation queue management
- ❌ Admin panel features
These features are available through the web application with your account login.
Using Your API Key
Basic Request
curl -X GET https://api.pichr.io/api/v1/files \
-H "Authorization: Bearer pk_your_api_key_here"Upload Example
# Step 1: Request presigned URL
curl -X POST https://api.pichr.io/api/v1/upload/presign \
-H "Authorization: Bearer pk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"filename": "photo.jpg",
"mime": "image/jpeg",
"bytes": 524288,
"sha256": "abc123...",
"title": "My Photo",
"visibility": "public"
}'
# Step 2: Upload file (response includes uploadUrl)
curl -X PUT [uploadUrl] \
-H "Authorization: Bearer pk_your_api_key_here" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpg
# Step 3: Finalize upload
curl -X POST https://api.pichr.io/api/v1/upload/finalize \
-H "Authorization: Bearer pk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "upl_xyz789",
"fileId": "file_abc123",
"r2Key": "uploads/...",
"r2Etag": "etag-value",
"width": 1920,
"height": 1080
}'Code Examples
JavaScript/TypeScript
const PICHR_API_KEY = 'pk_your_api_key_here';
const API_BASE = 'https://api.pichr.io/api/v1';
async function uploadImage(file: File) {
// Step 1: Request presigned URL
const presignResponse = await fetch(`${API_BASE}/upload/presign`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${PICHR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
filename: file.name,
mime: file.type,
bytes: file.size,
sha256: await calculateSHA256(file),
title: file.name,
visibility: 'public',
}),
});
const { fileId, uploadUrl } = await presignResponse.json();
// Step 2: Upload file
await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${PICHR_API_KEY}`,
'Content-Type': file.type,
},
body: file,
});
// Step 3: Finalize
const finalizeResponse = await fetch(`${API_BASE}/upload/finalize`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${PICHR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileId,
uploadId: presignData.uploadId,
r2Key: presignData.r2Key,
r2Etag: uploadResponse.headers.get('etag'),
width: await getImageWidth(file),
height: await getImageHeight(file),
}),
});
return await finalizeResponse.json();
}
// Get your files
async function getFiles() {
const response = await fetch(`${API_BASE}/files?limit=50`, {
headers: {
'Authorization': `Bearer ${PICHR_API_KEY}`,
},
});
return await response.json();
}Python
import requests
import hashlib
PICHR_API_KEY = 'pk_your_api_key_here'
API_BASE = 'https://api.pichr.io/api/v1'
def upload_image(file_path):
# Calculate SHA256
with open(file_path, 'rb') as f:
file_data = f.read()
sha256 = hashlib.sha256(file_data).hexdigest()
# Step 1: Request presigned URL
presign_response = requests.post(
f'{API_BASE}/upload/presign',
headers={'Authorization': f'Bearer {PICHR_API_KEY}'},
json={
'filename': 'photo.jpg',
'mime': 'image/jpeg',
'bytes': len(file_data),
'sha256': sha256,
'title': 'My Photo',
'visibility': 'public'
}
)
presign_data = presign_response.json()
# Step 2: Upload file
upload_response = requests.put(
presign_data['uploadUrl'],
headers={
'Authorization': f'Bearer {PICHR_API_KEY}',
'Content-Type': 'image/jpeg'
},
data=file_data
)
# Step 3: Finalize
finalize_response = requests.post(
f'{API_BASE}/upload/finalize',
headers={'Authorization': f'Bearer {PICHR_API_KEY}'},
json={
'uploadId': presign_data['uploadId'],
'fileId': presign_data['fileId'],
'r2Key': presign_data['r2Key'],
'r2Etag': upload_response.headers.get('etag'),
'width': 1920,
'height': 1080
}
)
return finalize_response.json()
# Get your files
def get_files():
response = requests.get(
f'{API_BASE}/files',
headers={'Authorization': f'Bearer {PICHR_API_KEY}'},
params={'limit': 50}
)
return response.json()PHP
<?php
$PICHR_API_KEY = 'pk_your_api_key_here';
$API_BASE = 'https://api.pichr.io/api/v1';
function uploadImage($filePath) {
global $PICHR_API_KEY, $API_BASE;
$fileData = file_get_contents($filePath);
$sha256 = hash('sha256', $fileData);
// Step 1: Request presigned URL
$presignResponse = curlRequest('POST', "$API_BASE/upload/presign", [
'filename' => 'photo.jpg',
'mime' => 'image/jpeg',
'bytes' => strlen($fileData),
'sha256' => $sha256,
'title' => 'My Photo',
'visibility' => 'public'
]);
// Step 2: Upload file
curlRequest('PUT', $presignResponse['uploadUrl'], $fileData, [
'Content-Type' => 'image/jpeg'
]);
// Step 3: Finalize
$finalizeResponse = curlRequest('POST', "$API_BASE/upload/finalize", [
'uploadId' => $presignResponse['uploadId'],
'fileId' => $presignResponse['fileId'],
'r2Key' => $presignResponse['r2Key'],
'r2Etag' => 'etag-from-upload',
'width' => 1920,
'height' => 1080
]);
return $finalizeResponse;
}
function getFiles() {
global $PICHR_API_KEY, $API_BASE;
return curlRequest('GET', "$API_BASE/files?limit=50");
}
function curlRequest($method, $url, $data = null, $extraHeaders = []) {
global $PICHR_API_KEY;
$curl = curl_init($url);
$headers = array_merge([
'Authorization: Bearer ' . $PICHR_API_KEY,
'Content-Type: application/json'
], $extraHeaders);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
]);
if ($data && is_array($data)) {
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
} elseif ($data) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($curl);
curl_close($curl);
return json_decode($response, true);
}
?>API Key Management
Creating Keys
- Create multiple API keys for different applications
- Set descriptive names to identify usage
- Choose appropriate permissions
- Set expiry dates for temporary keys
Security Best Practices
-
Store Securely:
- Never commit API keys to version control
- Use environment variables
- Store in secure configuration management
-
Rotate Regularly:
- Rotate keys every 90 days (recommended)
- Create new key before revoking old one
- Update all applications using the key
-
Use Permissions:
- Only grant necessary permissions
- Use
readfor view-only access - Use
upload+readfor full file management
-
Monitor Usage:
- Check “Last Used” date in dashboard
- Revoke unused keys
- Review API key list regularly
-
One Key Per Application:
- Don’t share keys across applications
- Makes it easier to track usage
- Simplifies key rotation
Revoking Keys
If your API key is compromised:
- Log in to pichr.io
- Go to Settings → API Keys
- Click Revoke next to the compromised key
- Create a new API key
- Update your applications with the new key
Note: Revocation is immediate. Any requests with the revoked key will fail with 401 Unauthorized.
Rate Limits
Rate limits vary by subscription plan:
| Plan | Uploads/Hour | API Calls/Hour |
|---|---|---|
| Free | 20 | 1,000 |
| Pro | 100 | 10,000 |
| Enterprise | 1,000 | 100,000 |
Rate limit information is included in response headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1700000000Error Handling
Common authentication errors:
| Error | Status | Meaning |
|---|---|---|
Invalid API key | 401 | API key is malformed or doesn’t exist |
API key has been revoked | 401 | Key was revoked in dashboard |
API key has expired | 401 | Key passed its expiration date |
Forbidden | 403 | API key doesn’t have required permissions |
Forbidden | 403 | Trying to access internal endpoint with API key |
Handling Errors
async function makeRequest(url, options) {
const response = await fetch(url, options);
if (response.status === 401) {
const error = await response.json();
if (error.message.includes('revoked')) {
// Key was revoked - need new key
console.error('API key revoked. Please generate a new key.');
} else if (error.message.includes('expired')) {
// Key expired - need new key
console.error('API key expired. Please generate a new key.');
} else {
// Invalid key
console.error('Invalid API key.');
}
throw new Error('Authentication failed');
}
if (response.status === 403) {
const error = await response.json();
if (error.message.includes('API keys can only access')) {
// Trying to use API key on internal endpoint
console.error('This endpoint requires web application login.');
} else {
// Permission issue
console.error('API key lacks required permissions.');
}
throw new Error('Access denied');
}
return response.json();
}Next Steps
- Getting Started - Build your first integration
- API Endpoints - Complete endpoint reference
- Upload Guide - Upload images
- File Management - Manage your files
Last updated: 13 November 2025