New to the API?

Start with our Quickstart Guide — convert your first image in 30 seconds

API Documentation

Convert images programmatically. Free, fast, no signup.

Quick Start

1

Create an account at /account/signup/ (free, email only)

2

Include it as X-API-Key header or ?api_key= parameter

3

POST your image to /api/v1/convert

Authentication

Two methods (use either):

Header: X-API-Key: kp_your_key_here

Query: ?api_key=kp_your_key_here

Anonymous requests work but have lower rate limits.

See API Key terms for usage policies and responsibilities.

Browser Integration

When calling the API from browser JavaScript without an API key, you must include a CSRF token to prevent cross-site request forgery.

When is CSRF required?

Scenario CSRF Required?
Browser POST/PUT/PATCH/DELETE without API key Yes
Any request with X-API-Key header No (bypassed)
GET requests No (exempt)
Server-to-server requests (curl, Python, etc.) No (use API key)

3-step flow

1

Fetch GET /api/v1/csrf-token to set the csrftoken cookie

2

Read the cookie value: document.cookiecsrftoken=...

3

Include X-CSRFToken: <value> in your POST/PUT/PATCH/DELETE headers

JavaScript example

// Step 1: Get the CSRF cookie (one-time)
await fetch('/api/v1/csrf-token');

// Step 2: Read the cookie
function getCsrfToken() {
    const c = document.cookie.split('; ')
        .find(r => r.startsWith('csrftoken='));
    return c ? c.split('=')[1] : '';
}

// Step 3: Include in your requests
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('output_format', 'jpg');

const response = await fetch('/api/v1/convert', {
    method: 'POST',
    headers: { 'X-CSRFToken': getCsrfToken() },
    body: formData,
});

Tip: If you're building a server-side integration, skip CSRF entirely and use an API key instead. CSRF is only needed for browser-based JavaScript without authentication.

CORS Policy

The API uses a split CORS policy based on authentication method. CORS headers are only added to /api/* paths.

API Key Requests (Any Origin)

Requests with an X-API-Key header or ?api_key= parameter are allowed from any origin. No CORS restrictions apply. This lets third-party websites and applications call the KoalaPic API directly from browser JavaScript.

Anonymous Browser Requests (Restricted)

Requests without an API key are restricted to allowed origins (koalapic.com). These requests include Access-Control-Allow-Credentials: true for CSRF cookie support. See Browser Integration above.

Exposed Headers

The following custom headers are accessible to JavaScript on cross-origin requests:

  • X-RateLimit-Limit-Minute, X-RateLimit-Limit-Day, X-RateLimit-Tier
  • X-Download-Limit-Remaining-IP, X-Download-Limit-Remaining-Token
  • X-API-Version, Deprecation, Sunset, Retry-After

Example: Third-Party Integration

// Call KoalaPic API from your own website using an API key
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('output_format', 'webp');

const response = await fetch('https://koalapic.com/api/v1/convert', {
    method: 'POST',
    headers: { 'X-API-Key': 'kp_your_api_key_here' },
    body: formData,
});
// No CORS restrictions — works from any domain!

Preflight Caching

OPTIONS preflight responses are cached for 1 hour (Access-Control-Max-Age: 3600), minimizing round trips for browsers.

Rate Limits

Per Minute Per Day
With API Key 30 500
Anonymous 10 50

Response headers: X-RateLimit-Limit-Minute, X-RateLimit-Limit-Day, Retry-After (on 429)

See Rate Limit terms for fair use policies and escalation details.

Endpoints

POST /api/v1/convert

Convert an image to a different format.

Parameters (multipart/form-data)

file required — Image file (max 50 MB)
output_format required — jpg, png, webp, avif, heic, tiff, gif, bmp, svg, pdf, ico
quality optional — 1-100, default 85 (applies to JPG, WEBP, AVIF, HEIC)
tiff_compression optional — tiff_lzw (default), raw, tiff_deflate. Only for TIFF output.
gif_colors optional — 2-256, default 256. Only for GIF output.
gif_dither optional — true (default) or false. Floyd-Steinberg dithering for GIF.
svg_trace_mode optional — polygon (default) or spline. Only for SVG output.
pdf_dpi optional — 72–600 (default 150). Rendering resolution for PDF input.
pdf_page_range optional — e.g. "1-3, 5, 8-10". Empty = all pages. Only for PDF input.
crop_x optional — Crop X offset in pixels. All four crop params required together.
crop_y optional — Crop Y offset in pixels.
crop_width optional — Crop width in pixels.
crop_height optional — Crop height in pixels.
preview_id optional — UUID from POST /preview. Use instead of file for two-step crop flow.
edit_rotate optional — Clockwise rotation 0-359°. 90° increments are lossless.
edit_filter optional — Named filter preset (e.g. grayscale, sepia, vintage). See GET /filters.
edit_brightness optional — Brightness 0.0-3.0 (1.0=unchanged). Also: edit_contrast, edit_saturation, edit_sharpness, edit_blur, edit_flip_horizontal, edit_flip_vertical, edit_auto_enhance.

Success: Binary file (converted image)

Error: { "error": "message" }

curl -X POST https://koalapic.com/api/v1/convert \
  -H "X-API-Key: kp_your_key_here" \
  -F "file=@photo.heic" \
  -F "output_format=jpg" \
  -F "quality=90" \
  --output photo.jpg
import httpx

response = httpx.post(
    "https://koalapic.com/api/v1/convert",
    headers={"X-API-Key": "kp_your_key_here"},
    files={"file": open("photo.heic", "rb")},
    data={"output_format": "jpg", "quality": "90"},
)

if response.status_code == 200:
    with open("photo.jpg", "wb") as f:
        f.write(response.content)
    print(f"Converted! Size: {len(response.content)} bytes")
else:
    print(f"Error: {response.json()['error']}")
import fs from "fs";

const form = new FormData();
form.append("file", fs.createReadStream("photo.heic"));
form.append("output_format", "jpg");
form.append("quality", "90");

const res = await fetch("https://koalapic.com/api/v1/convert", {
  method: "POST",
  headers: { "X-API-Key": "kp_your_key_here" },
  body: form,
});

if (res.ok) {
  const buf = await res.arrayBuffer();
  fs.writeFileSync("photo.jpg", Buffer.from(buf));
  console.log("Converted!");
} else {
  const err = await res.json();
  console.error(err.error);
}
POST /api/v1/convert/url/batch

Convert multiple images from URLs in a single batch request.

Parameters (JSON body)

urls required — Array of image URLs (1–50, must start with http:// or https://)
output_format default: jpg — Target format (jpg, png, webp, avif, etc.)
quality default: 85 — Output quality 1–100 (lossy formats only)
webhook_url optional — Webhook URL for completion events (requires API key auth)

Success: { "job_id": "...", "conversions": [...] }

Workflow: Poll with GET /v1/jobs/{'{'}job_id{'}'} or stream via SSE. Download all results as ZIP.

curl -X POST https://koalapic.com/api/v1/convert/url/batch \
  -H "X-API-Key: kp_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://example.com/photo1.jpg",
      "https://example.com/photo2.png"
    ],
    "output_format": "webp",
    "quality": 80
  }'
const res = await fetch("https://koalapic.com/api/v1/convert/url/batch", {
  method: "POST",
  headers: {
    "X-API-Key": "kp_your_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    urls: [
      "https://example.com/photo1.jpg",
      "https://example.com/photo2.png",
    ],
    output_format: "webp",
    quality: 80,
  }),
});

const { job_id, conversions } = await res.json();
console.log(`Job ${job_id}: ${conversions.length} conversions started`);

// Poll for completion
const status = await fetch(
  `https://koalapic.com/api/v1/jobs/${job_id}`,
  { headers: { "X-API-Key": "kp_your_key_here" } }
).then(r => r.json());

// Download ZIP when complete
if (status.status === "completed") {
  window.location = `https://koalapic.com/api/v1/download/batch/${job_id}`;
}
GET /api/v1/formats

List all supported format conversion pairs. No authentication required.

Response

{
  "input_formats": ["heic", "webp", "avif", "tiff", "bmp", "svg", "gif", "png", "jpg", "cr2", "cr3", "nef", "arw", "dng"],
  "output_formats": ["jpg", "png", "webp", "avif", "heic", "tiff", "gif", "bmp", "svg", "pdf", "ico"],
  "pairs": [
    { "input": "heic", "output": "jpg" },
    ...
  ]
}
curl https://koalapic.com/api/v1/formats
GET /api/v1/status

Service health status. No authentication required.

Response

{
  "status": "ok",
  "services": {
    "database": "ok",
    "redis": "ok",
    "huey": "ok"
  }
}
curl https://koalapic.com/api/v1/status
GET /api/v1/rate-limit

Check your current rate limit status and usage.

Response (authenticated)

{
  "authenticated": true,
  "limits": {
    "per_minute": 30,
    "per_day": 500
  },
  "usage": {
    "today_requests": 42,
    "today_conversions": 38
  }
}
curl -H "X-API-Key: kp_..." https://koalapic.com/api/v1/rate-limit
GET /api/v1/filters

List available image filter presets for the edit_filter parameter.

Returns a sorted array of filter objects with id and name fields.

curl https://koalapic.com/api/v1/filters
POST /api/v1/preview

Upload an image to get a browser-viewable preview and dimensions. Used for building crop UIs with non-browser formats (HEIC, RAW, TIFF).

Parameters (multipart/form-data)

file required — Image file (max 50 MB)

Response

{
  "preview_id": "a1b2c3d4-...",
  "preview_url": "/api/v1/preview/a1b2c3d4-.../image",
  "width": 4032,
  "height": 3024,
  "format": "heic",
  "file_size": 3200000,
  "is_browser_renderable": false
}

Two-step crop flow: Upload via POST /preview, then convert via POST /convert with preview_id + crop parameters. Previews expire after 1 hour.

# Upload for preview
curl -X POST https://koalapic.com/api/v1/preview \
  -H "X-CSRFToken: ..." \
  -F "file=@photo.heic"

# Crop using preview_id (no re-upload)
curl -X POST https://koalapic.com/api/v1/convert \
  -H "X-API-Key: kp_your_key_here" \
  -F "preview_id=a1b2c3d4-..." \
  -F "output_format=jpg" \
  -F "crop_x=100" \
  -F "crop_y=200" \
  -F "crop_width=800" \
  -F "crop_height=600"

Converting PDFs

Upload a PDF to POST /api/v1/convert and KoalaPic renders each page as a separate image. The response returns a job ID for polling progress.

# Convert PDF pages 1-3 to PNG at 300 DPI
curl -X POST https://koalapic.com/api/v1/convert \
  -H "X-API-Key: kp_your_key_here" \
  -F "file=@document.pdf" \
  -F "output_format=png" \
  -F "pdf_dpi=300" \
  -F "pdf_page_range=1-3"

# Response (202):
# { "job_id": "...", "total_pages": 10, "requested_pages": 3, "conversions": [...] }

# Poll job status
curl -H "X-API-Key: kp_..." \
  https://koalapic.com/api/v1/jobs/JOB_ID

# Download all pages as ZIP
curl -H "X-API-Key: kp_..." \
  https://koalapic.com/api/v1/download/batch/JOB_ID --output pages.zip
  • PDF-to-PDF conversion is not supported
  • PDF files cannot be included in batch uploads
  • Maximum 200 pages per PDF (20 for bots)
  • DPI range: 72–600 (default: 150)

Error Handling

All errors return JSON: { "error": "Human-readable message" }

Code Meaning
400 Bad request (invalid file or format)
401 Invalid or missing API key
413 File too large (max 50 MB)
415 Unsupported file type
429 Rate limit exceeded (check Retry-After header)
500 Server error (our team is notified)

Versioning

The API uses URL path versioning. The current version is v1, accessed at /api/v1/.

Version Lifecycle

Status Meaning Response Headers
active Fully supported. Recommended for new integrations. X-API-Version: v1
deprecated Still fully functional, but a newer version is available. Migrate when convenient. Deprecation: true
Sunset: <date>
Link: ...rel="successor-version"
retired No longer available. Returns 410 Gone. N/A (410 response with migration guide link)

Retirement Policy

We support API versions indefinitely. When a new version launches, older versions are marked deprecated but continue working. Retirement only happens after explicit announcement — there are no automatic shutoffs.

Detecting Deprecation

Monitor the Deprecation response header. If present, your version has a newer successor. The Link header points to the migration guide.

Deprecation: true
Sunset: Wed, 01 Jan 2027 00:00:00 GMT
Link: </api/v2/docs/>; rel="successor-version",
      </api/v1/migration/v2>; rel="migration-guide"

Version Endpoints

Endpoint Description
GET /api/versions List all API versions and their status
GET /api/v1/changelog Structured changelog for this version
GET /api/v1/migration/{'{'}target{'}'} Migration guide to a target version (when available)

Supported Formats

Input

HEIC, HEIF, WEBP, AVIF, TIFF, BMP, SVG, GIF, PNG, JPG, CR2, CR3, NEF, ARW, DNG

Output

JPG, PNG, WEBP, AVIF, HEIC, TIFF, GIF, BMP, SVG, PDF, ICO

Data Retention

API-created data follows these retention periods:

  • Conversion files: 1 hour (automatically deleted)
  • Conversion metadata (authenticated): 1 year
  • Conversion metadata (anonymous): 30 days
  • API usage logs: 90 days
  • Webhook delivery logs: 7 days

See our Privacy Policy for the complete retention schedule.

Data Processing Agreement (DPA)

If your organization processes personal data through the KoalaPic API, GDPR Article 28 requires a Data Processing Agreement between you (the data controller) and KoalaPic (the data processor).

Our DPA covers:

  • Scope and purpose of data processing
  • Technical and organizational security measures
  • Sub-processor disclosures
  • Data breach notification procedures
  • Data subject rights assistance
  • International data transfer safeguards

View the full DPA · Download PDF

For a countersigned copy, contact support@koalapic.com.

SDKs & Integrations

No official SDK yet — the API is a simple multipart POST. Works with any HTTP client: curl, httpx, requests, fetch, axios.

Bot integrations: Telegram · Discord · Slack

Need Help?

Send Feedback

Thank you! We'll get back to you soon.

Install KoalaPic

Add to your home screen for quick access

Cookie & Storage Preferences

We use cookies and local storage to improve your experience. Essential storage is always active for core functionality. Learn more

Essential

CSRF protection, dark mode, error tracking. Always active.

Functional

Conversion presets, UI preferences, PWA install state.

Analytics

Anonymous usage statistics to improve the service.