Generate, edit, caption, OCR, and upscale images. Simple HTTP + an API key.
Open the Developer Console →
Base URL & Auth
https://api.wallpapers.com/v1/...
Authorization: Bearer wpk_your_key # or X-API-Key: wpk_your_keyEvery request needs your key. Calls run at high priority. A missing/invalid key → 401;
out of credits → 402.
Credits
Two pools: AI credits (GPU work) and Standard credits (CPU processing). New accounts start with 1 AI + 10 Standard free. See pricing to buy more or subscribe.
| Operation | Endpoint | Cost |
|---|---|---|
| Generate (text→image) | POST /v1/text-to-image | 2 AI |
| Image→image / style | POST /v1/image-to-image | 2 AI |
| Edit (instruct / multi-image / inpaint / outpaint) | POST /v1/edit | 2 AI |
| Face swap (image / video) | POST /v1/face-swap | 2 AI / from 8 AI (by length) |
| Image to 3D (.glb mesh) | POST /v1/image-to-3d | 3 AI |
| Image to video (live wallpaper) | POST /v1/image-to-video | 8 AI |
| Alt-text / caption | POST /v1/image-to-text | 1 AI |
| Document OCR (image or PDF → Markdown) | POST /v1/ocr | 1 AI |
| QA Vision (safety + quality, per-category confidence) | POST /v1/image-qa | 1 AI |
| Photo metadata (EXIF, DPI, color, dimensions, quality) | POST /v1/image-metadata | 1 Standard |
| LLM text | POST /v1/text | 1 AI |
| Upscale | POST /v1/upscale | 1 AI |
| Convert / resize | POST /v1/convert | 1 Standard |
Responses include X-AI-Credits-Remaining and X-Standard-Credits-Remaining headers.
Check anytime: GET /v1/me.
Variations bill per image: requesting num_images on a generate/edit call costs the
op's price × the number of images (e.g. num_images:3 on Generate = 6 AI). If a generation
fails, that image's credits are automatically refunded.
How image generation works (asynchronous)
Image generation — text-to-image, image-to-image, and edit — takes time, so these
calls are asynchronous. You submit a job and get a job_id back
immediately (HTTP 202) — the API never blocks waiting for the image. You then
poll GET /v1/jobs/<job_id> every few seconds until status
is "done", and download the image from the returned output_url (a 7-day
link). Prefer push over polling? Pass a webhook URL and we'll POST the result to it
on completion. Failed jobs are auto-refunded, so you're never billed for an image we
couldn't deliver.
Generate an image
# 1) Submit the job — returns a job_id immediately (HTTP 202, never the bytes)
curl -X POST https://api.wallpapers.com/v1/text-to-image \
-H "Authorization: Bearer wpk_your_key" \
-H "Content-Type: application/json" \
-d '{"prompt":"a misty pine forest at dawn","aspect":"16:9"}'
# → 202 {"job_id":"6f1c…","status":"processing","poll_url":"/v1/jobs/6f1c…"}
# 2) Poll every ~3s until it's done
curl -H "Authorization: Bearer wpk_your_key" \
https://api.wallpapers.com/v1/jobs/6f1c…
# → {"job_id":"6f1c…","status":"processing"} (keep polling)
# → {"job_id":"6f1c…","status":"done","output_url":"https://api.wallpapers.com/v1/file/…"}
# 3) Download the finished image from output_url
curl -L -o wallpaper.png "https://api.wallpapers.com/v1/file/…"Params: prompt (required), aspect (16:9 | 9:16 | 1:1 | 4:3),
optional width/height, negative_prompt, steps,
guidance (CFG strength, default 4.0 — higher = follow the prompt more closely),
seed, webhook (callback URL).
Multiple variations — add "num_images": N (1–4) to fan out N variations of the
same prompt. Each variation is a full generation and is billed individually (cost × N).
You get one job_id per image to poll:
{"num_images":N,"jobs":[{"job_id":"…","status":"processing","poll_url":"/v1/jobs/…"}, …]}.
Image-to-image — POST /v1/image-to-image
curl -X POST https://api.wallpapers.com/v1/image-to-image \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"prompt":"make it snowy","image":"https://you.com/pic.jpg","strength":0.6}'
# → 202 {"job_id":"…","status":"processing"} — then poll GET /v1/jobs/<id> for output_urlRe-render an image guided by a prompt. image is an http(s) URL or base64 (data-URI ok).
Optional: strength (0–1, how far from the original — default 0.7), negative_prompt,
steps, guidance (CFG strength), seed, num_images (1–4, billed per variation),
webhook. Async — returns a job_id (202); poll GET /v1/jobs/<id> for the output_url.
Alt-text / caption — POST /v1/image-to-text
curl -X POST https://api.wallpapers.com/v1/image-to-text \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image":"https://you.com/pic.jpg","task":"both"}'
# → {"description":"...","tags":[...],"alt":"..."}image is an http(s) URL or base64 (data-URI ok). task selects the output:
both (default) → {description, tags, alt};
caption → {description};
tags → {tags} (8–15 search tags);
alt → {alt} (one WCAG alt-text sentence).
Synchronous — unlike image generation, this returns the text directly in the
response body (no job_id / polling).
Document OCR — POST /v1/ocr
Parse a document image or PDF into Markdown with Baidu Unlimited-OCR. This is
separate from /v1/image-to-text: OCR preserves document text and layout,
while image-to-text produces a short caption, tags, or accessibility alt text.
# Submit an image or PDF by public URL (HTTP 202)
curl -X POST https://api.wallpapers.com/v1/ocr \
-H "Authorization: Bearer wpk_your_key" \
-H "Content-Type: application/json" \
-d '{"url":"https://you.com/document.pdf","mode":"auto"}'
# → {"job_id":"…","status":"processing","status_url":"/v1/jobs/…"}
# Poll until done
curl -H "Authorization: Bearer wpk_your_key" \
https://api.wallpapers.com/v1/jobs/<job_id>
# → {"status":"done","result":{"text":"# Parsed document\n…",
# "format":"markdown","pages":3,"mode":"base",
# "model":"baidu/Unlimited-OCR","output_tokens":1842}}Asynchronous — poll GET /v1/jobs/<id>, or pass a
webhook. Supply exactly one of url/file_url or
file_b64/image_b64. Inputs are limited to 25 MB and PDFs to
32 pages. mode may be auto, gundam, or
base; PDFs always use multi-page base mode. Optional:
prompt, dpi (100–300), max_length
(1024–32768), priority, and webhook. Cost:
1 AI; failures are automatically refunded.
Photo metadata — POST /v1/image-metadata
curl -X POST https://api.wallpapers.com/v1/image-metadata \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image":"https://you.com/photo.jpg"}'
# Or upload the original file directly:
curl -X POST https://api.wallpapers.com/v1/image-metadata \
-H "Authorization: Bearer wpk_your_key" \
-F "image=@/path/to/photo.jpg"Fast, synchronous, CPU-worker-backed inspection of a PNG, JPEG, WebP, GIF, TIFF,
BMP or other Pillow-readable raster image. image accepts a public http(s) URL, a data URI,
raw base64, or a multipart file upload (the field may be named image or
file). Maximum encoded input size is 25 MB and maximum raster size is
100 MP. Private/local URLs are rejected.
{
"metadata_version": "image_metadata_v2",
"file": {"bytes": 2841932, "format": "JPEG", "mime_type": "image/jpeg",
"sha256": "…", "sha1": "…", "md5": "…", "crc32": "…",
"estimated_compression_ratio": 12.87},
"image": {"width": 4032, "height": 3024, "display_width": 3024, "display_height": 4032,
"pixel_count": 12192768, "display_aspect_ratio_exact": "3:4",
"mode": "RGB", "bands": ["R","G","B"], "estimated_bits_per_pixel": 24,
"has_alpha": false, "animated": false, "frames": 1},
"dpi": {"reported": true, "x": 300, "y": 300, "unit": "pixels_per_inch",
"print_size_inches": {"width": 13.44, "height": 10.08}},
"color": {"dominant": "#32475a", "palette": ["#32475a", "…"],
"palette_pct": [0.41, 0.22], "average": "#536779", "hues": ["blue"]},
"visual": {"megapixels": 12.19, "aspectRatio": "4:3", "orientation": "portrait",
"brightness": 0.42, "saturation": 0.31, "contrast": 0.55,
"sharpness": 0.72, "entropy": 0.64, "pHash": "…"},
"pixel_analysis": {
"sample": {"width": 384, "height": 512, "downsampled": true},
"channel_statistics_normalized_0_1": {"red": {"min": 0, "max": 1, "mean": 0.42, "…": "…"}},
"luminance_statistics_normalized_0_1": {"min": 0.01, "max": 0.99, "percentiles": {"…": "…"}},
"histograms_8bit_256_bins": {"red": [0,1,"…"], "green": ["…"], "blue": ["…"], "luminance": ["…"]},
"unique_rgb_colors_in_sample": 98214, "colorfulness": 48.2,
"shadow_clipping_pct": 0.2, "highlight_clipping_pct": 0.1,
"edge_density_pct": 13.4,
"hashes": {"average_hash_64": "…", "difference_hash_64": "…"}
},
"exif": {"Make": "Apple", "Model": "iPhone …", "DateTimeOriginal": "…", "…": "…"},
"gps": {"GPSLatitudeRef": "N", "GPSLongitudeRef": "W",
"decimal": {"latitude": 37.3317, "longitude": -122.0301}},
"xmp": {}, "iptc": {}, "container": {},
"format_specific": {"jpeg": {"chroma_subsampling": 2, "segments": ["…"],
"quantization_tables": {"0": ["…"]}}},
"embedded_metadata": {
"exif": {"present": true, "bytes": 2418, "sha256": "…"},
"icc_profile": {"present": true, "bytes": 3144, "description": "sRGB …"}
}
}The endpoint reads metadata from the original encoded bytes, so EXIF orientation,
camera/lens fields, GPS, DPI and embedded profiles are preserved. Large binary maker
notes and profiles are represented by byte count + SHA-256 rather than echoed back.
It returns every readable EXIF/GPS/XMP/IPTC/container field, ICC profile properties,
animation/frame timing, alpha coverage, exact aspect and print calculations, full
256-bin RGB/luminance histograms, channel percentiles, clipping/detail/color metrics,
and format internals (PNG chunk CRCs, JPEG segments/quantization/subsampling, TIFF tags,
GIF headers, WebP RIFF chunks, or BMP headers). Expensive pixel statistics are sampled
at up to 512×512 and the response says when downsampling occurred. The
visual and pixel_analysis blocks are deterministic CPU analysis—no GPU
or vision model is used.
Cost: 1 Standard credit.
QA Vision — POST /v1/image-qa
curl -X POST https://api.wallpapers.com/v1/image-qa \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image":"https://you.com/upload.jpg"}'Moderation and quality assessment for one image, returned as structured JSON with a confidence score per category — so you can set your own thresholds rather than accepting ours. Synchronous, typically 5–15s. 1 AI credit, refunded automatically if the vision service is unavailable.
{
"verdict": "ok", // ok | review | reject (ADVISORY only)
"risk_score": 0, // 0-100, the strongest unsafe/caution signal
"reasons": [],
"description": "A yellow spiral on a solid green background.",
"quality": { "score": 98, "issues": [] },
"detected": [], // categories actually present
"findings": [ // ALWAYS all 16, in a fixed order
{"category":"sexual_explicit","detected":false,"confidence":100,"detail":""},
{"category":"nudity_partial","detected":false,"confidence":100,"detail":""},
...
]
}What each category means, and how we treat it. The tiers below are only
our defaults for the advisory verdict — every category is
reported to you with its own confidence whatever tier it sits in.
| Category | What counts | Our default |
|---|---|---|
Blocking — drives reject at ≥75% confidence, review at ≥50% | ||
sexual_explicit | Visible sexual act or genitalia | reject |
nudity_full | Fully exposed genitalia or female nipples | reject |
gore_graphic | Photorealistic real-world injury, mutilation, corpses. Horror art, skulls, monsters and decorative blood do not count | reject |
hate_symbol | Recognised hate/extremist symbology | reject |
illegal_activity | Depicted criminal activity | reject |
minor_sexualised | An apparent minor shown sexually or undressed. Requires both. Most serious finding — treat any hit as an escalation, not a queue item | reject |
Caution — drives review at ≥50%. Legitimate often enough that a human should decide | ||
sexual_suggestive | Suggestive posing/framing without explicit content | review |
nudity_partial | Lingerie, swimwear, partial exposure, artistic nude | review |
violence_mild | Stylised, fictional or implied violence | review |
minor_present | A person who appears under 18 is visible (on its own, not a violation) | review |
real_person_identifiable | A recognisable celebrity or identifiable private individual — a likeness/rights risk, not a safety one | review |
Informational — reported but never affect verdict or risk_score. Yours to act on | ||
drugs_alcohol | Visible drugs, drug use or alcohol | info |
weapons | Firearms, blades, other weapons | info |
brand_or_logo | Visible third-party brand, logo or trademark | info |
watermark_or_credit | Stock watermark or credit line — usually means you are not looking at a licensed original | info |
ai_generated_look | Appears AI-generated or synthetic | info |
If your policy differs from ours — and it probably does — ignore verdict
and gate on the raw confidences. A dating app and a children's education site want
very different thresholds on the same image.
confidence is how certain the model is of its answer, not how
severe the content is: a confident "not present" is 95–100, genuine uncertainty is
40–60. Every category is returned on every call even when nothing is detected, so
indexing findings is stable.
How do I check for a specific thing (e.g. nudity)? Two ways.
Quick — detected lists the categories present. Look for the ones
your policy cares about:
r = requests.post(".../v1/image-qa", json={"image": url}, headers=H).json()
if {"nudity_full", "sexual_explicit"} & set(r["detected"]):
block() # explicit / full nudity
elif "nudity_partial" in r["detected"] or "sexual_suggestive" in r["detected"]:
send_to_human() # lingerie, swimwear, artistic nudeTunable — findings carries the confidence, so you set the bar.
Every category is present on every call, so this never KeyErrors:
conf = {f["category"]: f["confidence"] for f in r["findings"] if f["detected"]}
if conf.get("nudity_full", 0) >= 90: block()
if conf.get("nudity_partial", 0) >= 70: send_to_human()
if conf.get("weapons", 0) >= 60: log_only() # your call, not oursA real example — a lingerie photograph returns
verdict: "review", risk_score: 95, and
detected: ["sexual_suggestive", "nudity_partial", "real_person_identifiable"],
while nudity_full and sexual_explicit stay false.
That distinction is the point: partial vs full, and suggestive vs explicit, are
separate categories so you can allow one and block the other.
Note detected: false with a high confidence means a
confident "no" — not a weak yes. Only read confidence together
with detected.
We never reject your image. verdict is a suggestion using
sensible defaults (reject ≥75% confidence on explicit/gore/hate/illegal, review ≥50%);
the decision is yours — gate on the raw confidence values that matter to
your policy. Ordinary swimwear, artistic nudity and horror/fantasy art are treated as
normal creative content, not violations.
Edit an image — POST /v1/edit
Edit an existing image. Set mode to choose the kind of edit:
instruct— describe the change inprompt(e.g. "make it night-time")multi— combine your image with one or more others (pass them inimages)inpaint— repaint only a masked area (pass amask)outpaint— extend the image beyond its borders (direction+expand)
image is an http(s) URL or base64 (data-URI ok). Optional on any mode:
prompt, negative_prompt, steps,
guidance (CFG strength, default 4.0 — higher = follow the edit instruction more closely),
seed, num_images (1–4 variations, billed per variation),
webhook. On instruct/multi you may also set
width/height to force the output size (default = the input image's size).
Async — returns a job_id (202); poll GET /v1/jobs/<id> until
"done", then download output_url.
1. Instruct — edit the whole image from a text instruction (no mask):
Restyle a photo — anime, cartoon, oil painting and 20+ more.
Pass mode: "instruct" with a style and the model restyles the
whole image while holding pose, proportions and camera angle. style is
optional; omit it for the base model.
image accepts an http(s) URL (we fetch it) or base64, with or
without the data: prefix. prompt is optional too — leave it out and
the style supplies its own instruction; send one to override.
# the styles, and which are backed by a trained adapter (FREE — no credits)
curl -H "Authorization: Bearer wpk_your_key" https://api.wallpapers.com/v1/styles
# → {"styles":[{"style":"anime","kind":"lora","available":true}, …],"count":26}| Kind | Styles |
|---|---|
| lora — trained adapter, best fidelity | anime cartoon comic polaroid |
| prompt — instruction preset, no adapter | oil-painting watercolor chinese-ink pencil
charcoal renaissance baroque cyberpunk
synthwave steampunk pixel-art voxel
claymation isometric cinematic old-photo
ghibli dark-fantasy psychedelic papercraft
embroidery lego |
curl -X POST https://api.wallpapers.com/v1/edit \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"mode":"instruct","style":"anime","image":"https://you.com/photo.jpg"}'
# → 202 {"job_id":"…","poll_url":"/v1/jobs/…"}
# then: curl -H "Authorization: Bearer wpk_your_key" https://api.wallpapers.com/v1/jobs/<id>
# and download output_url when status == "done"Other mode values on the same endpoint: multi (reference several
images in one prompt), inpaint (needs a mask; white = repaint),
outpaint (needs direction + expand).
curl -X POST https://api.wallpapers.com/v1/edit \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"mode":"instruct","style":"anime",
"image":"https://you.com/photo.jpg",
"prompt":"Turn this photo into an anime style illustration"}'
# → 202 {"job_id":"…","poll_url":"/v1/jobs/…"} then poll for output_url
# `image` accepts an http(s) URL OR base64 (with or without the data: prefix).curl -X POST https://api.wallpapers.com/v1/edit \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"mode":"instruct","image":"https://you.com/pic.jpg",
"prompt":"make it night-time with a full moon"}' \
--output edited.png2. Multi-image — reference several images in one edit (combine subjects, place a
person in a scene, swap a face — pass the other image(s) in images):
-d '{"mode":"multi","image":"https://you.com/scene.jpg",
"images":["https://you.com/face.jpg"],
"prompt":"replace the person's face with the face in the second image"}'3. Inpaint — repaint only a masked region (mask: white = repaint, black = keep):
-d '{"mode":"inpaint","image":"https://you.com/pic.jpg",
"mask":"https://you.com/mask.png","prompt":"a vase of flowers"}'4. Outpaint — extend the canvas; the model fills the new border
(direction: all | up | down | left | right, expand: pixels):
-d '{"mode":"outpaint","image":"https://you.com/pic.jpg",
"direction":"all","expand":256,"prompt":"continue the landscape"}'Face swap — POST /v1/face-swap
Swap a source face onto an image or a video target. Consent-gated:
"consent": true is required (for consent-based use only — not impersonation
or non-consensual content). Asynchronous: returns a job_id immediately;
poll GET /v1/jobs/<id> for the output_url (a 7-day link to the result).
Billed per swap — 2 AI for an image target; a video is billed
by length starting at 8 AI (≤15s), with the per-second
rate tapering down the longer the clip (e.g. ~32 AI for 1 min, ~104 AI for
5 min). The response's billed_credits field reports the exact charge; a
failed swap is fully auto-refunded.
curl -X POST https://api.wallpapers.com/v1/face-swap \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"source_url":"https://you.com/face.jpg",
"target_url":"https://you.com/clip.mp4",
"consent":true}'
# → 202 {"job_id":"…","status":"processing","target_kind":"video","status_url":"/v1/jobs/…"}
# then: curl -H "Authorization: Bearer wpk_your_key" https://api.wallpapers.com/v1/jobs/<id>
# → {"status":"done","kind":"video","output_url":"https://…/swapped.mp4"}Inputs: source_url/target_url (any GET-able URL — pre-signed S3 ok)
or source_b64/target_b64. Optional: target_kind
(image|video, inferred from the target otherwise; an animated GIF counts as
video — every frame is swapped — so it is billed at the video rate), webhook, and an
options object — processors (face_swapper face_enhancer),
face_swapper_model, pixel_boost (128x128|256x256|512x512),
face_enhancer_model (gfpgan_1.4|codeformer|gpen_bfr_512),
face_enhancer_blend (0–100), output_quality (0–100),
output_fps (videos). Video swaps are billed by length — every frame is
swapped, so a longer target costs more, but the per-second rate tapers down the
longer the clip (bulk discount). See Pricing.
Priority. Keys enabled for priority can add "priority":"high" to a
request to have it served ahead of standard requests — send it for your premium users'
swaps and omit it for the rest. It is silently ignored for keys not granted priority (no
error). The same field works on the image, image-to-3D, and image-to-video endpoints.
Contact us to enable it on your key.
Content moderation. Every face-swap input is automatically screened for NSFW /
disallowed content. If the source or target is blocked, the job ends as
{"status":"rejected","reason":"content_policy"} when you poll — you are
not charged (auto-refunded), and retrying the same input will be rejected again.
Handle it as a normal user-facing outcome (ask for different media), not as an error to retry.
Image to 3D — POST /v1/image-to-3d
Turn a single image into a 3D model (a .glb mesh) with TripoSR.
Asynchronous: returns a job_id immediately; poll GET /v1/jobs/<id>
for the output_url (a 7-day link to the .glb). Billed
3 AI per generation.
curl -X POST https://api.wallpapers.com/v1/image-to-3d \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image_url":"https://you.com/object.jpg"}'
# → 202 {"job_id":"…","status":"processing","status_url":"/v1/jobs/…"}
# then: curl -H "Authorization: Bearer wpk_your_key" https://api.wallpapers.com/v1/jobs/<id>
# → {"status":"done","output_url":"https://…/model.glb"}Inputs: image_url (any GET-able URL) or image_b64. Optional:
webhook and an options object — foreground_ratio (0–1, default
0.85), mc_resolution (marching-cubes res, default 256). Works best on a single
centered subject; the background is auto-removed before reconstruction.
Image to video — POST /v1/image-to-video
Animate a still image into a short looping video (a live wallpaper).
Asynchronous: returns a job_id immediately; poll GET /v1/jobs/<id>
for the output_url (a 7-day link to the .mp4). Billed
8 AI per generation (video is GPU-minutes).
curl -X POST https://api.wallpapers.com/v1/image-to-video \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image_url":"https://you.com/scene.jpg","prompt":"gentle drifting clouds"}'
# → 202 {"job_id":"…","status":"processing","status_url":"/v1/jobs/…"}
# then: curl -H "Authorization: Bearer wpk_your_key" https://api.wallpapers.com/v1/jobs/<id>
# → {"status":"done","output_url":"https://…/live.mp4"}Inputs: image_url (any GET-able URL) or image_b64. Optional:
prompt (describe the motion), webhook, and an options object —
size, frames, steps, loop_seconds.
LLM text — POST /v1/text
curl -X POST https://api.wallpapers.com/v1/text \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"prompt":"Write 5 SEO tags for a misty forest wallpaper."}'
# → {"text":"..."}Upscale — POST /v1/upscale
curl -X POST https://api.wallpapers.com/v1/upscale \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image":"https://you.com/pic.jpg","scale":4}' \
--output upscaled.jpgimage is an http(s) URL or base64. scale = 2 | 4 (default 4) —
or pass target_long (px) to set the longest edge directly. Returns image/jpeg.
Convert / resize (Standard) — POST /v1/convert
curl -X POST https://api.wallpapers.com/v1/convert \
-H "Authorization: Bearer wpk_your_key" -H "Content-Type: application/json" \
-d '{"image":"https://you.com/pic.png","format":"webp","width":1920}' \
--output out.webpformat = png | jpg | webp (required). Optional width/height
(px; aspect preserved if you give just one) and quality (1–100, for jpg/webp).
CPU processing — charges Standard credits, not AI.
Check credits — GET /v1/me
curl https://api.wallpapers.com/v1/me -H "Authorization: Bearer wpk_your_key"
# → {"label":"my-app","prefix":"wpk_1234","ai_credits":97,"standard_credits":10}Poll an async job — GET /v1/jobs/<id>
# every image/edit/OCR/face-swap call returns a {"job_id":"abc"} (HTTP 202) — poll it:
curl https://api.wallpapers.com/v1/jobs/abc -H "Authorization: Bearer wpk_your_key"
# → {"status":"processing"} (keep polling)
# → {"status":"done","output_url":"https://api.wallpapers.com/v1/file/…"} (free — no charge)
# → {"status":"failed","error":"…"} (transient error — auto-refunded, safe to retry)
# → {"status":"rejected","reason":"content_policy",
# "error":"input rejected: content not permitted"} (blocked by moderation — auto-refunded, do NOT retry)Terminal statuses. A job ends in exactly one of:
| status | Meaning | How to handle |
|---|---|---|
done | Success | Download output_url, or read result for OCR/text jobs. |
failed | Transient error (upstream/GPU) | Charge auto-refunded. Safe to retry the same request. |
rejected | Input blocked by content moderation (e.g. face swap's NSFW / non-consensual-content filter refused the source or target) | Charge auto-refunded. Do NOT retry the same input — it will be rejected again. Surface error to your user and ask for different media. Check reason=="content_policy" to detect this case. |
Webhooks
Get notified when a job finishes instead of waiting on the response. Set a
default callback in the console, or pass one per request
with "webhook" in the body (as shown in the generate example above).
When the generation completes we POST:
POST your-webhook-url
{
"event": "generation.completed",
"gen_id": "…",
"op": "text-to-image",
"status": "completed",
"prompt": "…",
"output_url": "https://api.wallpapers.com/v1/file/… (link valid 30d)",
"input_url": null
}You are also called back when a job ends without a result, so you never have to
guess whether one is still running. Both of these are terminal and both are
auto-refunded — check status to tell them apart:
POST your-webhook-url
{
"event": "generation.rejected",
"job_id": "…",
"op": "face-swap-video",
"status": "rejected",
"reason": "content_policy",
"error": "input rejected: content not permitted",
"refunded": 8
}POST your-webhook-url
{
"event": "generation.failed",
"job_id": "…",
"op": "face-swap-video",
"status": "failed",
"error": "…",
"refunded": 8
}rejected means our content filter declined that specific input — retrying
the same file will be declined again, so treat it as final and surface it to your user.
failed is a system-side fault and is safe to retry. The same
status and reason values are returned by
GET /v1/jobs/<id>, so polling and webhooks agree.
The per-request webhook takes precedence over your account default.
All URLs are Wallpapers.com links — never third-party storage URLs. Your
images are stored long-term; the link above just expires after 30 days —
fetch a fresh one anytime via GET /v1/generations.
Retrieve your generations
We keep your inputs + outputs in your own private storage long-term — fetch them anytime as Wallpapers.com links (each call returns fresh 30-day links):
curl https://api.wallpapers.com/v1/generations?limit=50 \
-H "Authorization: Bearer wpk_your_key"
# → {"total":N,"generations":[{"op":"text-to-image","prompt":"...",
# "output_url":"https://api.wallpapers.com/v1/file/...","input_url":null,"at":"..."}, ...]}Errors
| Code | Meaning |
|---|---|
401 | Missing / invalid / revoked key |
402 | Insufficient credits — top up in the console |
400 | Bad input (e.g. missing prompt) |
502/503 | Upstream error — safe to retry |
See pricing & plans or manage everything in the Developer Console.