Picverce AI Public API v1.0.0

Run Picverce AI from your own server

Edit images with the same tools the website uses, or generate new ones from a prompt with 26 models. Jobs are asynchronous, so a slow model never becomes a timeout on your side.

API jobs draw from the same credit balance as your account. There is nothing separate to buy.

Base URL: https://api.picverce.com. Create a key in your account and start calling it.

Two ways to use it

Both go through the same endpoint. If you already have the picture and want it changed, that is a Tool. If you want a picture that does not exist yet, that is a Model.

Tools API

Run a Picverce AI product tool on an image you supply. You give it an image, it gives back a new one.

2 to 8 credits. Send type: "tool".

Models API

Pick a generation model and a prompt, optionally with a reference image. You give it words, it gives back an image.

3 to 14 credits. Send type: "generate".

Quickstart

  1. 1. Create a key

    Go to Account, API Keys and click Create key. The secret is shown once and cannot be retrieved again, so copy it before closing the dialog.

    Keys look like pk_live_.... A pk_test_ key behaves identically and is for development.

    Public API access comes with a paid plan or a credit pack. An active Basic, Standard or Premium subscription grants it, and so does any credit pack you have bought. See plans

  2. 2. Check your balance

    curl
    curl https://api.picverce.com/v1/me \
      -H "Authorization: Bearer pk_live_..."
  3. 3. Create a job

    curl
    curl -X POST https://api.picverce.com/v1/jobs \
      -H "Authorization: Bearer pk_live_..." \
      -H "Content-Type: application/json" \
      -d '{
        "type": "tool",
        "tool": "background_remover",
        "input": { "image_url": "https://example.com/product.jpg" }
      }'

    Answers in milliseconds with status: "queued" and a job id.

  4. 4. Poll until it finishes

    curl
    curl https://api.picverce.com/v1/jobs/JOB_ID \
      -H "Authorization: Bearer pk_live_..."

    Poll every couple of seconds until status reads succeeded or failed. Results arrive in output.images.

The same thing in JavaScript

Node 18 or newer. Run this on your server, never in a browser.

Node
const KEY = process.env.PICVERCE_API_KEY;
const BASE = 'https://api.picverce.com';

async function api(path, options = {}) {
  const response = await fetch(BASE + path, {
    ...options,
    headers: {
      Authorization: `Bearer ${KEY}`,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(`${body.error.code}: ${body.error.message}`);
  }
  return body;
}

async function runJob(payload) {
  const job = await api('/v1/jobs', { method: 'POST', body: JSON.stringify(payload) });

  while (true) {
    const current = await api(`/v1/jobs/${job.id}`);
    if (current.status === 'succeeded') return current.output.images;
    if (current.status === 'failed') throw new Error(current.error.message);
    await new Promise((r) => setTimeout(r, 2000));
  }
}

const images = await runJob({
  type: 'generate',
  model: 'flux-schnell',
  input: { prompt: 'a red bicycle against a white wall' },
});

Keys belong on your server

This API sends no CORS headers, on purpose. A key pasted into browser JavaScript will not work from a web page.

Treat a key like a password. Keep it out of mobile apps and public repositories. If one leaks, revoke it in your account and it stops working on the next request.

Tools you can run today

All 18 catalog tools accept jobs. Prices and complete input schemas, including the preset lists for the three that take them, come from GET /v1/tools, so a settings UI can be built from the catalog rather than from this page.

ToolCreditsWhat it does
anime_enhance2Enhancement tuned for anime and illustrated artwork
background_remover2Cut the subject out, transparent PNG back
coloring_page3Redraw a photo as a printable black and white coloring page
colorize3Add colour to a black and white photograph
enhance2General cleanup, recovers detail and reduces noise
enhance_pro6Higher quality pass for images that need more
face_restore2Rebuild facial detail in low resolution portraits
hairstyle4Restyle the hair in a portrait, optionally recolouring it
object_remover2Erase an object and fill in what was behind it
outfit4Change the clothing in a portrait using a preset outfit
photo_to_anime3Redraw a photo as anime artwork
photo_to_cartoon3Redraw a photo in a cartoon style
photo_to_sketch3Redraw a photo as a pencil or ink sketch
restore4Repair scratches, fading and grain in old photos
sharpen2Recover focus in soft or slightly blurred images
text_clarity8Make small or smeared text legible
upscale2, 3 or 6Raise resolution 2x, 4x or 8x
watermark_remover3Remove an overlay and rebuild what was under it

All 26 generation models accept jobs. List them with GET /v1/models and read capabilities rather than hardcoding ratios and resolutions, since they differ per model.

Tool options

Every tool takes image_url and nothing else is required. These are the optional fields, and any tool not listed here takes no options at all. An unknown field name is refused by name rather than ignored.

FieldTypeDefaultTools
face_enhancebooleanfalseenhance, enhance_pro, anime_enhance
scale2 | 4 | 82upscale
upscale1 | 2 | 42face_restore
face_upsamplebooleantrueface_restore
background_enhancebooleantrueface_restore
codeformer_fidelitynumber, 0 to 10.5face_restore
stylestring, see the tables belowper toolphoto_to_anime, photo_to_cartoon, photo_to_sketch, coloring_page
mask_urlhttps URLnoneobject_remover
haircutstring, 96 presetsnonehairstyle
hair_colorstring, 31 coloursNo changehairstyle
outfitstring, 38 presetsnoneoutfit
garment_scopefull | top | bottom | dress | outerwearfulloutfit
outfit_colorstring, 17 coloursNo changeoutfit
outfit_color_2string, 17 coloursNo changeoutfit
outfit_color_3string, 17 coloursNo changeoutfit
outfit_color_count1, 2 or 31outfit
aspect_ratioauto and seven ratiosautohairstyle, outfit

scale on upscale is the only option that changes the price: 2 credits at 2x, 3 at 4x, 6 at 8x. Everything else costs what the table above says whatever you pass.

Style keys

The four redraw tools take a style key. These are the complete lists. Keys are lowercase, and a key this API does not know is refused with a 422 naming what is supported, rather than quietly redrawn in the default style.

photo_to_anime9 keys, default ghibli
ghiblishonencyberpunkchibirealisticmangashojoseinenwebtoon
photo_to_cartoon24 keys, default disney-3d
disney-3dghiblichibi-3dkawaii-flatwatercolornekofantasy-gameshonen-comicwestern-comiccyborggothiccandyexpressive-3dimpressionistpop-artpencil-sketchink-mangafairytalefantasy-animeslice-of-lifepixel-artbotanicalgraphic-novelcyberpunk
photo_to_sketch18 keys, default pencil-sketch
pencil-sketchcharcoal-sketchink-sketchcolored-pencil-sketchwatercolor-sketchballpoint-pen-sketchpastel-sketchfine-detail-sketchda-vinci-manuscriptbold-sketchminimalist-line-sketchfigure-quick-sketchcartoon-sketchconcept-sketchmanga-sketchaesthetic-sketchgraffiti-sketchink-wash-sketch
coloring_page30 keys, default classic-outline
toddler-thickkids-simpleclassic-outlineadult-detailedfine-line-intricatehalloweenchristmaseastervalentinesbirthdaymothers-dayfathers-daysummer-holidayback-to-schoolthanksgivingdinosaursunicornspaceunderwaterfarm-animalsvehiclessweets-and-treatsfairy-talemandalabotanicalarchitecturalstained-glasspaper-cutwoodcutdoodle
Minimal, and the same tool with options
{ "type": "tool", "tool": "colorize",
  "input": { "image_url": "https://example.com/photo.jpg" } }

{ "type": "tool", "tool": "photo_to_sketch",
  "input": { "image_url": "https://example.com/photo.jpg",
             "style": "charcoal-sketch" } }

{ "type": "tool", "tool": "face_restore",
  "input": { "image_url": "https://example.com/portrait.jpg",
             "upscale": 4, "codeformer_fidelity": 0.8,
             "face_upsample": true, "background_enhance": false } }

The three that take more than an image

Preset values are exact strings, and the complete lists are enums on GET /v1/tools/{id}. They are names, not slugs: it is Smart casual, never smart-casual. An unknown value is a 422 naming what is supported.

object_remover
mask_urlhttps URLrequired
hairstyle
haircut96 presetsrequired
hair_color31 coloursNo change
aspect_ratioauto, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3auto
outfit
outfit38 presetsrequired
garment_scopefull, top, bottom, dress, outerwearfull
outfit_color17 coloursNo change
outfit_color_217 coloursNo change
outfit_color_317 coloursNo change
outfit_color_count1, 2 or 31
aspect_ratioauto, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3auto

object_remover takes a second image as mask_url, the same size as the source: white marks what to remove, black is kept. Both URLs are downloaded and checked the same way, so a mask on a private address is refused like any other.

hairstyle takes one preset per job. The website can apply several at once; the API does not, and a comma separated value is refused rather than guessed at. outfit takes up to three colours, and outfit_color_count decides how many are used. The top and bottom scopes only have two zones, so a count of 3 behaves as 2 there.

One of each
{ "type": "tool", "tool": "object_remover",
  "input": { "image_url": "https://example.com/room.jpg",
             "mask_url": "https://example.com/room-mask.png" } }

{ "type": "tool", "tool": "hairstyle",
  "input": { "image_url": "https://example.com/portrait.jpg",
             "haircut": "Blunt Bangs", "hair_color": "Auburn" } }

{ "type": "tool", "tool": "outfit",
  "input": { "image_url": "https://example.com/portrait.jpg",
             "outfit": "Smart casual", "garment_scope": "full",
             "outfit_color": "Navy", "outfit_color_2": "Cream",
             "outfit_color_count": 2 } }

Generate settings

A generate job takes a model id and these six fields. Nothing else is accepted, and a field name that is close but wrong is refused rather than ignored: it is ratio, not aspect_ratio, and variations, not n.

FieldTypeDefault
promptstring, up to 2000 charactersrequired
ratiostring1:1
resolutionstringfirst entry in capabilities.resolutions
variationsinteger, 1 or more1
reference_image_urlhttps URLnone
enhance_promptbooleanfalse

What each model supports differs, so read GET /v1/models rather than hardcoding a list. The notes below cover the things you cannot work out from a single model response.

Credits, and why more images do not cost more

A generate job costs the same whatever variations you ask for. The price is set by resolution alone, which is why the catalog gives each model a price per resolution rather than a single number. Four images at 1K cost what one image at 1K costs.

Resolution tokens are per model and are not a size in pixels. Some models use 1K through 4K, some use 1 MP, 2 MP and 4 MP, and a few use Low, Medium, High or Auto. Read capabilities.resolutions and credits.values together.

Ratios

Three sets across the catalog. A ratio a model does not support is a 422 naming the ones it does.

Most models

1:116:99:164:33:4

gpt-image-2, chatgpt-1-5

1:13:22:3

stable-diffusion-3-5

16:91:121:92:33:24:55:49:169:21

There is one more accepted value: match_input_image, which keeps the shape of your reference image. It needs both a model whose capabilities.match_input_ratio is true and a reference_image_url in the same request. 13 models support it today.

Multiple images

There is no batch endpoint. Asking for more than one image is variations on a normal job, and the successful job comes back with that many URLs in output.images. Most models cap at 1. Today only gpt-image-2 and chatgpt-1-5 go higher, both to 4. Over a model's cap is a 422 naming the cap.

Reference images and prompt help

Required by qwen-edit-2511, qwen-layered. A job without one is a 422.

Not accepted by flux-schnell, imagen-4, imagen-4-ultra, recraft-v3. Sending one is a 422 rather than a silently ignored field.

Optional everywhere else. The same URL rules as image_url apply: public https, 5 MB or smaller.

enhance_prompt rewrites a short prompt into a longer one before generating. Supported by seedream-4, flux-2-flex, ideogram-3, ideogram-3-turbo only; asking for it elsewhere is a 422.

qwen-layered is the one model that will run with no prompt at all, given a reference image.

Reading what a model supports
curl -s https://api.picverce.com/v1/models/nano-banana-2 \
  -H "Authorization: Bearer $PICVERCE_API_KEY"

# capabilities.ratios          which ratio strings are accepted
# capabilities.resolutions     which resolution tokens are accepted
# capabilities.max_variations  the ceiling for variations
# capabilities.reference_image unsupported | optional | required
# credits.values               price per resolution token
Minimal, then every setting
{ "type": "generate", "model": "flux-schnell",
  "input": { "prompt": "a lighthouse in fog, 35mm" } }

{ "type": "generate", "model": "gpt-image-2",
  "input": {
    "prompt": "a lighthouse in fog, 35mm",
    "ratio": "3:2",
    "resolution": "High",
    "variations": 4
  } }

{ "type": "generate", "model": "nano-banana-2",
  "input": {
    "prompt": "the same room, repainted deep green",
    "reference_image_url": "https://example.com/room.jpg",
    "ratio": "match_input_image"
  } }

Credits

Credits are reserved when a job is created and charged only when it succeeds. A failed job is refunded in full and reports credits.charged: 0.

Because credits are held up front, your balance drops the moment you create a job. GET /v1/me reports reserved_open so you can see how much is held by work still running.

Errors

Every failure has the same shape, so one handler covers all of them.

Any non-2xx response
{
  "error": {
    "code": "insufficient_credits",
    "message": "This job costs 6 credits and your balance is 2.",
    "request_id": "req_4f2a9c1b7e3d5a8c0b6e1f92"
  }
}
HTTPCodeMeaning
401invalid_api_keyMissing, malformed, unknown or revoked key
402insufficient_creditsValid key, not enough balance
403plan_upgrade_requiredYour account does not include Public API access
404invalid_toolNo such tool
404invalid_modelNo such model
404job_not_foundNo job with that id under your account
409idempotency_conflictThat Idempotency-Key is in use for something else
422validation_errorThe request was understood and refused
429rate_limitedToo many requests this minute
500internal_errorSomething failed on our side

Invalid input is refused, not repaired

Ask for a ratio or a variation count a model does not support and you get a 422 naming the supported values. The website quietly substitutes something valid in the same situation, which is fine when a person is watching. For a machine caller it would mean paying for an image you did not ask for, so the API refuses instead.

Rate limits

60 requests per rolling minute, per key. Every authenticated endpoint counts. Each response carries your standing:

Response headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1787644920

Over the limit you get a 429 with Retry-After in seconds. It is a rolling window, so 60 requests at the top of the minute does not earn you 60 more a second later.

Being straight with you about the ceiling: the counter currently runs per edge instance, so traffic spread across regions can go somewhat above 60 in a real minute. Design for 60 rather than leaning on it. This becomes a hard global limit in a later release.

Retries

Reads are safe to repeat. Creating a job is not, unless you say so: two creates are two jobs and two charges. Send an Idempotency-Key and the retry becomes safe.

The same create, sent twice
curl -sS -X POST https://api.picverce.com/v1/jobs \
  -H "Authorization: Bearer $PICVERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f1c1b8e-2d1a-4f9b-9c3f-0a6d4b2e7c51" \
  -d '{"type":"tool","tool":"enhance",
       "input":{"image_url":"https://example.com/photo.jpg"}}'

The second call returns the first call's job with Idempotent-Replay: true in the headers, and charges nothing more. It comes back in whatever state the job has reached, so a replay a minute later may already say succeeded.

RuleWhat it means
OptionalWithout the header every create makes a new job, as before
Up to 255 charactersPrintable ASCII. A UUID per job is the simplest thing that works
Remembered 24 hoursAfter that the same key starts a new job
Scoped to one API keyTwo keys on one account do not share a namespace
POST /v1/jobs onlyIgnored on every other endpoint

Two requests count as the same job when the validated request matches: the type, the tool or model id, and the validated input. Reordering fields, or leaving out a value that resolves to the same default, does not make them different. Sending a genuinely different body under a used key answers 409 idempotency_conflict, because replaying you a job you did not ask for would be worse than making you pick a new key.

Reusing a key while the first request is still in flight is the same 409, with a message saying so. Wait a few seconds and retry.

Webhooks

Rather than polling, register an endpoint and we will POST a signed event when a job finishes. Add one in Account, API, Webhooks. Two events exist today: job.succeeded and job.failed.

What arrives at your endpoint
POST https://api.yourcompany.com/picverce/webhook
Content-Type: application/json
User-Agent: Picverce-Webhooks/1.0
X-Picverce-Event: job.succeeded
X-Picverce-Delivery: 0f2b1d94-6a3c-4b1e-9d77-2c9e5a1b3d84
X-Picverce-Timestamp: 1756300000
X-Picverce-Signature: v1=8f3c1a...

{
  "id": "evt_11111111222233334444555555555555s",
  "object": "event",
  "type": "job.succeeded",
  "created_at": "2026-08-27T10:00:09.412Z",
  "data": { "object": { ...the same job GET /v1/jobs/{id} returns... } }
}

Verify the signature before you trust the body. The signing string is the timestamp, a full stop, then the raw body exactly as received.

Verify in Node
const crypto = require('crypto');

// rawBody must be the raw bytes, not a re-serialized object.
function verify(rawBody, headers, secret) {
  const timestamp = headers['x-picverce-timestamp'];
  const received = headers['x-picverce-signature'];
  if (!timestamp || !received) return false;

  // The signature itself never expires, so this is what stops a replay.
  if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;

  const expected =
    'v1=' +
    crypto.createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(received);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Parse after you verify

Re-encoding JSON changes key order and whitespace, and the signature stops matching.

Expect duplicates

Delivery is at least once. The event id is stable per job and state, so use it as your idempotency key.

Answer fast

Queue the work and return 200. We time out at 10 seconds and retry twice, about 2 and 8 seconds later, then keep retrying for up to 24 hours.

Endpoints must be https and publicly reachable. Private, loopback and cloud metadata addresses are refused when you register and again before every delivery. Redirects are not followed, so register the final URL. A 404 or 410 from your endpoint stops the retries.

An endpoint that was down for all three quick attempts does not lose the event. It is queued and tried again about 30 minutes later, then at 2, 6 and 12 hour intervals, stopping after 10 attempts or 24 hours. A deploy that takes your endpoint down for an hour therefore costs you nothing. Pausing an endpoint, or unsubscribing from an event, stops its queued retries too.

Claude & MCP

The REST API at api.picverce.com is the platform. MCP at mcp.picverce.com is the connector layer for Claude, Cursor, and other MCP clients. The hosted MCP server calls the same POST /v1/jobs endpoints with your API key, polls jobs when needed, and returns image URLs in tool results. Credits, rate limits, and idempotency behave the same as direct API use.

Manage connector setup from your Account MCP tab. Create a key on the Keys tab first.

Connector URL
https://mcp.picverce.com/mcp

In Claude web, open Settings, Connectors, add a custom connector named Picverce AI, paste the URL above, and set authentication to your pk_live_ API key as a Bearer token. The key never appears in chat; only the connector stores it.

Claude Code
claude mcp add --transport http --scope user picverce-ai https://mcp.picverce.com/mcp

MCP tools (v1)

These are the tools Claude sees. Each maps to Public API jobs or catalog endpoints.

ToolWhat it does
upload_imageStage chat or base64 image bytes on Picverce AI and get a public URL (no credits).
generate_imageText to image with any catalog model, ratio, resolution, and variations.
edit_imageRun a catalog tool on an image URL (enhance, upscale, remove background, and more).
get_jobPoll job status and read output URLs when a job is still running.
list_toolsList runnable tools and credit costs.
list_modelsList generation models and credit ranges.

The MCP server sends an Idempotency-Key on every job it creates so Claude retries do not double-charge. Direct API callers still control their own keys on POST /v1/jobs.

Shopify and other platforms

MCP is for AI assistants in chat. A store or a SaaS product uses the REST API plus webhooks instead: your backend holds the merchant's Picverce AI API key, posts jobs for product images, and listens for job.succeeded to put the results back where they belong.

The Picverce AI app for Shopify is built that way. A merchant installs it, pastes their own API key, picks a tool and some products, and every finished image is added to the product as an extra photo. Originals are never replaced. Jobs are charged to the merchant's own credits, so there is nothing separate to buy.

StepWhat happens
InstallThe merchant installs the app and pastes their own Picverce AI API key
PickA tool and some products, or a collection, or the whole catalog
RunOne POST /v1/jobs per product photo, with an Idempotency-Key so a retry costs nothing
FinishA signed job.succeeded webhook arrives and the image is added to the product
UninstallShopify sends app/uninstalled and every credential stored for that shop is deleted

The app is on development stores while it is being finished, so there is no App Store listing yet. If you want to build the same thing into your own software, everything it uses is on this page: POST /v1/jobs with an Idempotency-Key per image, and a signed webhook when each one finishes.

See the MCP landing page for a short setup guide, or read Anthropic's MCP documentation for connector details.

Full reference

The complete OpenAPI 3.0 description covers every endpoint, field and error. Point a client generator at it rather than writing request types by hand.

Using the API, the MCP connector, or the apps for Shopify and WordPress is covered by the same Terms of Service and Privacy Policy as the website, which describe what a job sends us and how long we keep it.