# Retrieve an image

`GET https://stylor.ai/api/v1/image/{key}`

- Authentication: none
- Web page: https://stylor.ai/guides/rest/v1/retrieve-image

Delivers a stored image. Every image key in the API — a product
image's `uploaded.key`, an upload's `key` — resolves through this
endpoint, so `https://stylor.ai/api/v1/image/{key}` is a URL you can
drop straight into an `<img>` tag. (Generated thumbnail `url` values are
already full URLs on this endpoint.)

## Authentication

None is required. Keys are unguessable UUIDs and the endpoint is
public, so it works from browsers, emails and third-party embeds. It is
not rate limited and does not count against your quota.

## How it responds

The endpoint does not stream bytes itself. It
answers `302 Found` with a `Location` pointing at a signed CDN URL that
expires **120 seconds** after it was issued. Browsers and image clients
follow this transparently. If you fetch programmatically, follow the
redirect immediately and cache the *API* URL, never the signed one.

## Caching

The `302` sets no `Cache-Control` header of its own.
Transformed renditions are stored with
`Cache-Control: public, max-age=31536000, immutable`, which the CDN
returns with the image bytes; originals are stored without one. A key
that does not exist still redirects, and the CDN then answers with an
error (typically `403`).

## Transforms

Append segments of the form `/-/<type>/<value>` after
the key to get a derived image:

| Type | Value | Effect |
| --- | --- | --- |
| `resize` | `600x`, `x400`, `800x600` | Fit inside the width, height or box, keeping aspect ratio. Never enlarges. |
| `crop` | `400x400` | Fill exactly that box, cropping around the centre. |
| `format` | `jpeg`, `jpg`, `png`, `webp`, `avif` | Re-encode in that format (case-insensitive). Without it, the source format is kept. |
| `quality` | `1`–`100` | Encoder quality. Default `80`. |

Transforms may be combined and are applied in URL order:
`/api/v1/image/images/3f9c….jpeg/-/resize/600x/-/format/webp`. A value
that does not match the table is ignored rather than rejected. The
first request for a given combination renders and caches it
synchronously, so it is slower; later requests are served from cache.
The cache is keyed on the set of transforms regardless of order, so
list them in a consistent order. If rendering fails, the original
image is served instead of an error.

Do not add a trailing slash: it costs an extra `308` redirect to the
same URL without it. Segments before the first `/-/<known type>/` are
part of the key; after it, segments that do not form a valid
`/-/<type>/<value>` triple are ignored.

## Example request

```bash
curl -X GET "https://stylor.ai/api/v1/image/images%2F3f9c2a1e-8d4b-4c6f-9a2e-1b7d5e0c4f88.jpeg%2F-%2Fresize%2F600x%2F-%2Fformat%2Fwebp"
```

## Path parameters

- `key` (string, required): The object key, including its folder and extension, exactly as returned by the API — for example `images/3f9c2a1e-8d4b-4c6f-9a2e-1b7d5e0c4f88.jpeg` or `uploads/6d1e5c0a-2b3f-4a8e-9c7d-0f1e2a3b4c5d.jpg`. Send the slashes literally; do not URL-encode them. Optional transform segments (`/-/resize/600x`, `/-/crop/400x400`, `/-/format/webp`, `/-/quality/80`) follow the key. [example `"images/3f9c2a1e-8d4b-4c6f-9a2e-1b7d5e0c4f88.jpeg/-/resize/600x/-/format/webp"`]

## Responses

### 302

Redirect to a signed CDN URL for the (optionally transformed) image. The signed URL is valid for 120 seconds. The response has no body and no `Cache-Control` header. Following the redirect returns the image bytes with the matching `image/*` content type.

Headers:

- `Location`: Signed CDN URL for the image. Follow it immediately; do not store it. Transformed images live under `cache/<key without extension>/<type-value>_….<ext>`.

## Errors

Every error is a JSON object with an `error` string. Match on the status code; the message is written for people.

| Status | Message | When |
| --- | --- | --- |
| 422 | Invalid image path | The path consists only of transform segments, so there is no key (for example `/v1/image/-/resize/600x`). |
| 422 | Invalid image key | The key is empty after path-traversal sequences (`..`) and leading slashes are stripped. |
| 500 | Internal server error. | An unexpected failure on the server. The message is always this string. |
