Inspekt
Get a key

Render API

GET /api/still returns a single rendered image/webp of a skin, produced by the same compositor as the live embed. Use it where a static image is the right tool: a product-card thumbnail, an Open Graph or social unfurl image, anywhere the live 3D embed doesn't fit.

Read this before you integrate it. A cache miss does not render synchronously by default. It returns a placeholder image immediately and starts the real render in the background. See Caching and the placeholder below; it is the single most common source of "why is my image wrong" reports for this endpoint.

Request

Two forms of the same request: use whichever input you already have.

by inspect link
GET /api/still?inspect=steam%3A%2F%2Frungame%2F730%2F76561202255233023%2F%2Bcsgo_econ_action_preview%2000180720...&w=1200&h=630&key=cs2pk_your_key_here
by catalog id
GET /api/still?id=ak47_cu_ak47_cobra_gen&wear=0.1662&seed=55&w=1200&h=630&key=cs2pk_your_key_here

With inspect, the link is resolved server-side to the same normalized config the id form uses, so both hash and cache identically. If the link can't be resolved, you get a 400, 404 or 503 (see the response table below) with an error, and, when the item decoded but isn't in the catalog yet, the raw defindex / paintindex it decoded to.

Parameters

ParamType / boundsDefaultOn failure
id/^[a-z0-9][a-z0-9_.-]*$/i, must exist in the catalog required (or inspect) 400 { error: "bad or missing id" } or 400 { error: "no such skin: <id>" }
inspectSteam inspect linkrequired (or id) 400, 404 or 503, message from the resolver
wearnumber, 0-1kit default 400 { error: "wear must be 0..1" }
seedinteger, 0-100000kit default 400 { error: "seed must be an integer 0..100000" }
stslot:stickerId:wear[,…], up to 5none 400 { error: "st must be slot:stickerId:wear[,…] (≤5 stickers)" }
name≤20 printable characters, no control charactersnone 400 { error: "name must be at most 20 printable characters" }
w, hintegers, 160-16001200 × 630 400 { error: "w/h must be integers 160..1600" }
wait1unset (non-blocking) see below
key/^[a-z0-9_-]{4,64}$/inone (anonymous) see Error codes

Caching and the placeholder

The render identity is a hash of the normalized config (id, wear, seed, st, name, w, h). Two requests with the same effective params always hit the same cached file.

Why the default doesn't wait: this endpoint doubles as the image behind Open Graph and social-card unfurls, and most crawlers give up in a couple of seconds and cache whatever they got, for hours. Blocking by default would mean a first share of any not-yet-rendered skin gets permanently stuck with a blank card. Returning the thumbnail instead means the worst case is "looks like the flat catalog icon for a few seconds," not "broken forever."

What this means for you: if you're generating an image for a user to see live on page load (a product card, say), either poll the same URL a couple of times a few seconds apart until the response stops looking like the flat catalog thumbnail, or pre-warm the config ahead of time (fetch it once, off the critical path, before a customer ever requests the page) so it's already cached by the time it matters. Reach for wait=1 only from a backend job or build step, where a several-second blocking call is fine and you control the timeout. Don't reach for it from a page load.

Response

StatusBodyMeaning
200image/webp A real render (cached or freshly finished) or the placeholder thumbnail.
400{ error } A parameter failed validation: see the table above.
503{ error } With wait=1, when the request can't be queued at all: the render worker is off, its queue is full (8 distinct configs in flight), or no browser is available on the host. Carries Retry-After: 15.
503{ error } Without wait, on the rare miss where even the catalog thumbnail used as the placeholder is missing from disk. Carries Retry-After: 10.
504{ error } Only with wait=1: the render didn't finish inside 45 seconds. It may still finish and land in the cache. Retry the same request without wait and you may get it immediately.

Access-control refusals (bad key, wrong origin, over quota) use the same JSON error shape at 401/403/429. See Error codes.

Quota and billing

Every served render counts toward your monthly quota: a cache hit, a placeholder, and a fresh render all count the same. Overage is a separate question. Past quota, only a render that actually costs compute is recorded as overage. A cache hit costs nothing, and so does a request that joins a render already in flight. The request that starts a new render is recorded like a real render, even though what it gets back right away is the placeholder. Nothing is charged automatically today, so overage shows up as an estimate rather than a bill. See Limits & SLA for the exact rule.