Image Processing API

Upload an image, run up to five ordered operations, and download the result through one asynchronous workflow.

Base URL
https://api.pixmender.com
Interactive docs
Swagger UI
1Create key
2Request upload
3Upload image
4Create job
5Check status
6Download
01

Create an API key

Open API keys, create a key with images:write and images:read. Add webhooks:manage only if you will use webhooks, then copy the key immediately. The full key is shown only once.

Keep it server-sideNever expose an img_live_… key in browser code or a public repository.
Terminal
export PIXMENDER_API_KEY="img_live_your_key"
02

Request a secure upload URL

Create an asset record first. Use the exact MIME type of the file: JPEG, PNG, WebP, AVIF, GIF, HEIC, or HEIF. Files can be up to 20 MB.

Request
curl --request POST https://api.pixmender.com/v1/uploads \
  --header "Authorization: Bearer $PIXMENDER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"content_type":"image/jpeg","filename":"photo.jpg"}'
Response
{
  "id": "ast_01...",
  "status": "pending_upload",
  "upload_url": "https://storage.pixmender.com/...",
  "upload_method": "POST",
  "upload_fields": {
    "bucket": "pixmender-assets",
    "key": "originals/usr_.../ast_01....jpg",
    "Content-Type": "image/jpeg",
    "Policy": "...",
    "X-Amz-Signature": "..."
  },
  "upload_url_expires_at": "2026-08-25T10:30:00.000Z",
  "asset_expires_at": "2026-08-26T10:15:00.000Z"
}

upload_url_expires_at is the short upload-policy deadline. asset_expires_at is the separate retention deadline for the uploaded image.

03

Upload the image bytes

POST a multipart/form-data body directly to upload_url. Add every value from upload_fields first and the image as the final file field. The signed policy accepts 1 byte through 20 MB. Do not send your PixMender API key to the storage URL.

Request
export UPLOAD_URL="https://storage.pixmender.com/..."

# Add every upload_fields entry returned above before the file field.
curl --request POST "$UPLOAD_URL" \
  --form-string "bucket=$UPLOAD_BUCKET" \
  --form-string "key=$UPLOAD_KEY" \
  --form-string "Content-Type=image/jpeg" \
  --form-string "Policy=$UPLOAD_POLICY" \
  --form-string "X-Amz-Algorithm=$UPLOAD_ALGORITHM" \
  --form-string "X-Amz-Credential=$UPLOAD_CREDENTIAL" \
  --form-string "X-Amz-Date=$UPLOAD_DATE" \
  --form-string "X-Amz-Signature=$UPLOAD_SIGNATURE" \
  --form "[email protected];type=image/jpeg"
04

Create a processing job

Pass the uploaded asset ID and an ordered operations array. One credit is reserved for each operation and automatically returned if processing fails. The API supports PNG, JPEG, WebP, and AVIF as final output formats. The Idempotency-Key header is required: reuse the same stable key only for an identical request so transport retries cannot create a second charged job.

Request
curl --request POST https://api.pixmender.com/v1/images/process \
  --header "Authorization: Bearer $PIXMENDER_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: request-001" \
  --data '{
    "input": { "asset_id": "ast_01..." },
    "operations": [
      { "type": "denoise" },
      {
        "type": "upscale",
        "scale": 4,
        "model": "realesrgan-x4plus"
      }
    ],
    "output_format": "webp"
  }'
202 Accepted
{
  "id": "job_01...",
  "status": "queued",
  "credits_reserved": 2,
  "input_asset_id": "ast_01...",
  "result_asset_id": null
}
05

Poll the job status

Poll every 1–3 seconds until the status is succeeded,failed, canceled, or expired. On success, use result_asset_id for the download request.

queuedvalidatingprocessingsucceededfailedcancel_requestedcanceledexpired
Request
curl https://api.pixmender.com/v1/jobs/job_01... \
  --header "Authorization: Bearer $PIXMENDER_API_KEY"
Succeeded response
{
  "id": "job_01...",
  "status": "succeeded",
  "credits_charged": 2,
  "credits_refunded": 0,
  "result_asset_id": "ast_result_01...",
  "result_filename": "photo.webp",
  "result_byte_size": 1843200,
  "error": null
}
06

Download the result

The API returns a temporary signed URL valid for 10 minutes. The processed file itself remains available for 24 hours.

Request
curl 'https://api.pixmender.com/v1/assets/ast_result_01.../download?disposition=attachment' \
  --header "Authorization: Bearer $PIXMENDER_API_KEY"

# Response: { "url": "https://storage.pixmender.com/...", "expires_in": 600 }
curl --location "https://storage.pixmender.com/..." --output photo.webp
07

Operations reference

A pipeline contains 1–5 operations. If used, upscalemust be the final operation.

TypePurposeExtra fields
remove_watermarkRemove visible watermarksJob: rights_attested: true
remove_textRemove captions and lettering
remove_logoRemove brand marks
remove_objectRemove a described objectprompt is required
restoreRepair damage and degradation
denoiseReduce noise and compression artifacts
deblurImprove blurred details
face_enhanceRefine portrait details
upscaleIncrease resolutionscale: 2 | 4; must be last
Output formatspng · jpeg · webp · avif
Upscale modelsrealesrgan-x4plus · realesrgan-x4plus-anime · realesr-animevideov3
08

Receive signed webhooks

Create an endpoint with the webhooks:manage scope or manage it from Webhooks. PixMender sends job.succeeded, job.failed, and job.canceled. Return any 2xx response within 10 seconds; failed deliveries retry with exponential backoff.

Create endpoint
curl --request POST https://api.pixmender.com/v1/webhooks \
  --header "Authorization: Bearer $PIXMENDER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"url":"https://example.com/pixmender","events":["job.succeeded","job.failed"]}'
Verify the raw request bodySignatures use HMAC-SHA256 over webhook-id.webhook-timestamp.raw-body. Store the whsec_… secret shown at creation; it is displayed once.
Node.js signature verification
import { createHmac, timingSafeEqual } from "node:crypto";

const eventId = request.headers.get('webhook-id');
const timestamp = request.headers.get('webhook-timestamp');
const signatureHeader = request.headers.get('webhook-signature');
if (!eventId || !timestamp || !signatureHeader?.startsWith('v1=')) {
  throw new Error('Missing webhook signature headers');
}
const received = signatureHeader.slice(3);
if (!/^[a-f0-9]{64}$/i.test(received)) {
  throw new Error('Invalid webhook signature format');
}
const rawBody = await request.text();

const timestampNumber = Number(timestamp);
const age = Math.abs(Date.now() / 1000 - timestampNumber);
if (!Number.isFinite(timestampNumber) || age > 300) {
  throw new Error('Stale webhook');
}

const secret = process.env.PIXMENDER_WEBHOOK_SECRET;
if (!secret) throw new Error('Webhook secret is not configured');
const expected = createHmac('sha256', secret)
  .update(`${eventId}.${timestamp}.${rawBody}`)
  .digest('hex');
const receivedBytes = Buffer.from(received, 'hex');
const expectedBytes = Buffer.from(expected, 'hex');
if (receivedBytes.length !== expectedBytes.length ||
    !timingSafeEqual(receivedBytes, expectedBytes)) {
  throw new Error('Invalid webhook signature');
}

Delivery headers are webhook-id, webhook-timestamp, and webhook-signature: v1=…. Deduplicate by webhook ID.

09

Use a server-side SDK

The server-side SDKs combine signed upload, job creation, polling, and result download into one method. Keep the API key out of browser code.

TypeScript · @pixmender/sdk
import { PixMender } from "@pixmender/sdk";

const client = new PixMender({
  apiKey: process.env.PIXMENDER_API_KEY!,
});
const result = await client.processImage({
  file: "./photo.jpg",
  operations: [{ type: "denoise" }, { type: "upscale", scale: 4 }],
  idempotencyKey: "workflow-run-123",
  outputFormat: "webp",
});
await result.downloadTo("./photo-enhanced.webp");
Python · pixmender
import os
from pixmender import PixMender

client = PixMender(api_key=os.environ["PIXMENDER_API_KEY"])
result = client.process_image(
    file="photo.jpg",
    operations=[{"type": "restore"}],
    idempotency_key="workflow-run-123",
    output_format="webp",
)
result.download_to("photo-enhanced.webp")
PHP · pixmender/pixmender-php
use PixMender\PixMender;

$client = new PixMender($_ENV['PIXMENDER_API_KEY']);
$result = $client->processImage(
    __DIR__ . '/photo.jpg',
    [['type' => 'restore']],
    'workflow-run-123',
    outputFormat: 'webp',
);
$result->downloadTo(__DIR__ . '/photo-enhanced.webp');
Direct downloadsVersion 0.1.0 · hosted by PixMender
SHA-256 checksums
TypeScriptNode.js 18+
@pixmender/sdk
PythonPython 3.10+
pixmender
PHPPHP 8.1+
pixmender/pixmender-php
Install directly from pixmender.com
# TypeScript
npm install https://pixmender.com/downloads/pixmender-sdk-0.1.0.tgz

# Python
pip install https://pixmender.com/downloads/pixmender-0.1.0-py3-none-any.whl

# PHP / Composer
composer config repositories.pixmender composer https://pixmender.com/downloads/composer
composer require pixmender/pixmender-php:^0.1
10

Connect automation tools

The n8n and Make integrations accept a binary image, execute the full asynchronous workflow, and return the processed image as a binary output for the next step in the scenario.

Direct downloadsVersion 0.1.0 · direct source packages
n8nSelf-hosted installation
n8n-nodes-pixmender
MakeDeveloper app components
PixMender Custom App
Install on self-hosted n8n
# Run in the custom-nodes directory of a self-hosted n8n instance
npm install https://pixmender.com/downloads/n8n-nodes-pixmender-0.1.0.tgz