Skip to Content
APIAuthentication

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

  1. Log in to your Pichr account
  2. Navigate to Settings → API Keys
  3. Click “Create API Key”
  4. Give your key a descriptive name (e.g., “My Blog”, “Photo Upload App”)
  5. Select permissions (typically upload and read)
  6. Set an expiry date (optional, recommended for better security)
  7. 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

  1. Store Securely:

    • Never commit API keys to version control
    • Use environment variables
    • Store in secure configuration management
  2. Rotate Regularly:

    • Rotate keys every 90 days (recommended)
    • Create new key before revoking old one
    • Update all applications using the key
  3. Use Permissions:

    • Only grant necessary permissions
    • Use read for view-only access
    • Use upload + read for full file management
  4. Monitor Usage:

    • Check “Last Used” date in dashboard
    • Revoke unused keys
    • Review API key list regularly
  5. 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:

  1. Log in to pichr.io 
  2. Go to Settings → API Keys
  3. Click Revoke next to the compromised key
  4. Create a new API key
  5. 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:

PlanUploads/HourAPI Calls/Hour
Free201,000
Pro10010,000
Enterprise1,000100,000

Rate limit information is included in response headers:

X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 995 X-RateLimit-Reset: 1700000000

Error Handling

Common authentication errors:

ErrorStatusMeaning
Invalid API key401API key is malformed or doesn’t exist
API key has been revoked401Key was revoked in dashboard
API key has expired401Key passed its expiration date
Forbidden403API key doesn’t have required permissions
Forbidden403Trying 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

Last updated: 13 November 2025