Skip to content
Developer API

Build AI image enhancement into your product

A REST API for AI enhancement (smooth-skin, upscale, auto-enhance, background removal) with first-class outbound webhooks. Authenticate with an API key and receive signed events when jobs finish.

Authentication

Every request is authenticated with an API key passed in the x-api-key header. Create and manage keys from your dashboard. Keys are shown in full only once — store them securely. Do not send Bearer tokens on the public API.

curl https://pickimg.com/api/ai/enhance \
  -H "x-api-key: YOUR_API_KEY"

Scopes

Each key carries scopes. Missing a required scope returns 403.

image:readRead image jobs and sources
image:writeUpload and process images
ai:enhanceUpload sources, create, and poll AI jobs
webhook:manageCreate and inspect outbound webhooks

New keys default to image:read, image:write, and ai:enhance. webhook:manage is opt-in.

HTTP errors

Use these status codes when integrating retries and upgrades.

401Missing or invalid API key.
403Key lacks the required scope. Monthly enhancement or storage quota exceeded — jobs are blocked, not billed as overage.
429Per-key request-per-minute limit exceeded. Wait and retry.

429 responses include Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining.

AI enhancement

POST/api/ai/uploadUpload a source image
POST/api/ai/enhanceCreate an enhancement job
GET/api/ai/enhance/:idGet job status & result
GET/api/ai/enhanceList your jobs

POST /api/ai/upload is multipart form field file (JPEG, PNG, WebP, or AVIF, max 20MB) and returns { url } for sourceUrl.

Operations: smooth_skin, upscale, auto_enhance, bg_remove. level: low | medium | high.

Poll GET until status is queued, processing, completed, failed.

Upload, enhance, poll

# 1. Upload a source image (multipart field: file, max 20MB)
curl -X POST https://pickimg.com/api/ai/upload \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@./photo.jpg"
# { "url": "https://cdn.example.com/uploads/..." }

# 2. Queue enhancement
curl -X POST https://pickimg.com/api/ai/enhance \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceUrl": "https://cdn.example.com/uploads/photo.jpg",
    "operation": "smooth_skin",
    "level": "medium"
  }'
# { "id": "job_123", "status": "queued", "operation": "smooth_skin" }

# 3. Poll until completed or failed
curl https://pickimg.com/api/ai/enhance/job_123 \
  -H "x-api-key: YOUR_API_KEY"
# { "id": "job_123", "status": "completed", "resultUrl": "https://cdn.example.com/out.jpg" }
// Node.js
const key = process.env.PICKIMG_KEY;

const form = new FormData();
form.append("file", blob, "photo.jpg");
const uploaded = await fetch("https://pickimg.com/api/ai/upload", {
  method: "POST",
  headers: { "x-api-key": key },
  body: form,
}).then((r) => r.json());

const job = await fetch("https://pickimg.com/api/ai/enhance", {
  method: "POST",
  headers: {
    "x-api-key": key,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sourceUrl: uploaded.url,
    operation: "upscale",
  }),
}).then((r) => r.json());

const poll = await fetch(`https://pickimg.com/api/ai/enhance/${job.id}`, {
  headers: { "x-api-key": key },
}).then((r) => r.json());

Official SDKs (v0.1.0)

JavaScript (@pickimg/sdk) and Python (pickimg) wrap upload → enhance → poll. Install from a clone of the repo until npm/PyPI publish. Do not put tokens in source.

# From a clone of the pickimg repo (v0.1.0)
pnpm add ./sdk/js
pip install ./sdk/python

# After npm / PyPI publish:
# npm install @pickimg/sdk
# pip install pickimg
import { PickimgClient } from "@pickimg/sdk";

const client = new PickimgClient({ apiKey: process.env.PICKIMG_API_KEY! });

// 1. Upload
const { url } = await client.uploadImage(file);
// 2. Enhance
const job = await client.enhance({ sourceUrl: url, operation: "smooth_skin" });
// 3. Poll
const done = await client.waitForEnhancement(job.id);
console.log(done.resultUrl);
from pickimg import PickimgClient

client = PickimgClient(api_key="YOUR_KEY")

# 1. Upload
with open("photo.jpg", "rb") as fh:
    upload = client.upload_image(fh)
# 2. Enhance
job = client.enhance(source_url=upload["url"], operation="smooth_skin")
# 3. Poll
done = client.wait_for_enhancement(job.id)
print(done.result_url)

Webhooks

Register endpoints to receive events when jobs complete. Each delivery is signed so you can verify authenticity.

GET/api/webhooks/eventsList available event types
POST/api/webhooks/endpointsCreate an endpoint
GET/api/webhooks/deliveriesInspect delivery attempts

Events: enhancement.queued, enhancement.completed, enhancement.failed, image.uploaded, quota.exceeded, ping, job.completed, upload.finished.

job.completed is also sent with image.completed and enhancement.completed. upload.finished is also sent with image.uploaded. Prefer the alias if you want one handler for both job types.

Verifying the signature

Deliveries include an X-PickIMG-Signature header formatted as t=<timestamp>,v1=<hmac>, where the HMAC-SHA256 is computed over <timestamp>.<raw-body>.

import crypto from "crypto";

function verify(rawBody, header, secret) {
  const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Rate limits & quotas

RPM is per API key (plan ceiling). Monthly AI enhancements and storage are shared by all keys of the same account.

PlanRPMAI / monthStorageAPI keys
Free30501 GB1
Personal1202,00050 GB3
Business60020,000500 GB15
Enterprisecustomcustomcustomcustom

Hard quota returns 403. RPM returns 429 with Retry-After. Enterprise limits are custom — contact sales; do not assume a self-serve number.

When a quota is exceeded the API returns 403 with an upgrade hint. See pricing.