Product Images API
Upload, list, describe, and delete images for a product. Image uploads use
multipart/form-data.
Endpoints
- List Product Images -
GET /api/v1/products/{product_id}/images/ - Upload Product Images -
POST /api/v1/products/{product_id}/images/ - Update Product Image -
PUT /api/v1/products/{product_id}/images/{image_id}/ - Delete Product Image -
DELETE /api/v1/products/{product_id}/images/{image_id}/
List Product Images
Retrieve all images for a product. This endpoint is not paginated.
Endpoint: GET /api/v1/products/{product_id}/images/
Scope Required: products:read
Request
curl -X GET "https://studio.easel.engineering/api/v1/products/pro_d0b6fmv28q6vn14peun0/images/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
{
"data": [
{
"id": "img_d0b6fmv28q6vn14peun0",
"url": "/images/img_d0b6fmv28q6vn14peun0-large.webp",
"alt_text": "Front of the t-shirt",
"width": 1200,
"height": 1200,
"format": "webp",
"created_at": "2023-01-01T10:00:00Z",
"external_source": null,
"external_id": null,
"taken_down_at": null
}
]
}
The url is relative to the storefront origin.
Upload Product Images
Upload one or more images for a product. Send the images as files in a
multipart/form-data body. Every file part is stored, so you can include
several images in a single request.
Endpoint: POST /api/v1/products/{product_id}/images/
Scope Required: products:write
Limits
- The total request body must be 50 MiB or smaller.
- Images must be no larger than 8192 pixels in either dimension. Larger
images are rejected. - Supported formats depend on the image decoder. Common web formats such as
JPEG, PNG, GIF, and WebP are accepted.
Uploaded images are downscaled and re-encoded as WebP, which keeps
transparency, and report a format of webp. The derived files are named
<image_id>-<size>.webp, for example img_d0b6fmv28q6vn14peun0-large.webp.
Each image also has a JPEG copy, <image_id>-share.jpeg, up to 1200 pixels on
its longest side, for email clients and link previews that can't show WebP.
Images uploaded before WebP report jpeg or png until Easel converts them.
Photos are turned upright using their EXIF orientation, so a phone photo taken
sideways is stored the right way up.
Form Fields
| Field | Required | Description |
|---|---|---|
| (any name) | Yes | An image file; every file part is stored |
external_source |
No | System the image was imported from; requires external_id and exactly one file (see External References) |
external_id |
No | Image's ID in that system; requires external_source |
Request
curl -X POST "https://studio.easel.engineering/api/v1/products/pro_d0b6fmv28q6vn14peun0/images/" \
-H "Authorization: Bearer your_api_key_here" \
-H "Idempotency-Key: unique-request-id-12345" \
-F "file=@front.jpg" \
-F "file=@back.jpg"
Response
Status: 201 Created
{
"data": [
{
"id": "img_d0b6fmv28q6vn14peun0",
"url": "/images/img_d0b6fmv28q6vn14peun0-large.webp",
"alt_text": "",
"width": 1200,
"height": 1200,
"format": "webp",
"created_at": "2023-01-01T10:00:00Z",
"external_source": null,
"external_id": null,
"taken_down_at": null
}
]
}
Error Responses
Invalid multipart form
Status: 400 Bad Request
{
"error": "Invalid input",
"code": "INVALID_FIELD"
}
Request too large
Status: 413 Request Entity Too Large
{
"error": "Request body is too large",
"code": "REQUEST_TOO_LARGE"
}
Image refused
A file Easel removed as illegal can't be uploaded to any store.
Status: 422 Unprocessable Entity
{
"error": "The image can't be uploaded",
"code": "INVALID_FIELD"
}
Update Product Image
Set an image's alt text, which describes it for screen readers and search
engines. The storefront's product page uses it, and falls back to the
product's name for an image without any. Translate it into the site's other
languages with the Translations API.
Endpoint: PUT /api/v1/products/{product_id}/images/{image_id}/
Scope Required: products:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
alt_text |
string | Yes | Up to 512 characters, trimmed; an empty string removes it |
Request
curl -X PUT "https://studio.easel.engineering/api/v1/products/pro_d0b6fmv28q6vn14peun0/images/img_d0b6fmv28q6vn14peun0/" \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"alt_text": "Front of the t-shirt"}'
Response
Status: 200 OK
The updated image.
{
"id": "img_d0b6fmv28q6vn14peun0",
"url": "/images/img_d0b6fmv28q6vn14peun0-large.webp",
"alt_text": "Front of the t-shirt",
"width": 1200,
"height": 1200,
"format": "webp",
"created_at": "2023-01-01T10:00:00Z",
"external_source": null,
"external_id": null,
"taken_down_at": null
}
Error Responses
Alt text too long
Status: 400 Bad Request
{
"error": "Invalid input",
"code": "INVALID_FIELD"
}
Image not found, or not on this product
Status: 404 Not Found
{
"error": "Resource not found",
"code": "NOT_FOUND"
}
Delete Product Image
Delete an image from a product.
Endpoint: DELETE /api/v1/products/{product_id}/images/{image_id}/
Scope Required: products:write
Request
curl -X DELETE "https://studio.easel.engineering/api/v1/products/pro_d0b6fmv28q6vn14peun0/images/img_d0b6fmv28q6vn14peun0/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 204 No Content
Error Responses
Image not found
Status: 404 Not Found
{
"error": "Resource not found",
"code": "NOT_FOUND"
}
Image Object
Fields
| Field | Type | Description |
|---|---|---|
id |
string | Image public ID |
url |
string | Relative URL to the large image |
alt_text |
string | Alt text; empty when the image has none |
width |
integer | Width in pixels of the image at url |
height |
integer | Height in pixels of the image at url |
format |
string | Image format: webp, or jpeg or png for an image uploaded before WebP that is not yet converted |
created_at |
string | ISO 8601 timestamp |
external_source |
string|null | System the record was imported from (see External References) |
external_id |
string|null | Record's ID in that system |
taken_down_at |
string|null | ISO 8601 timestamp of when Easel took the image down, or null while it hasn't. Read-only. See Takedowns |
Takedowns
Easel hosts your store's content, so under the EU Digital Services Act its
staff may take an image down that's illegal or against Easel's terms (see
Moderation). A taken-down image stays on
its product, with taken_down_at set, but isn't served at any size, so
its url stops loading, on the storefront too. Delete it, or upload
another in its place. The statement of reasons was emailed to the
store's owners, and the dashboard keeps it under Settings › Activity ›
Easel decisions; the activity log's taken_down event, on the store with
part image, names the category in reason.