Hydra is now in beta|Get started free|Follow our journey on X.com

Images API
On this page

Images

Images exist at two levels in Hydra: the store-level media library and product-level attachments. You can upload images directly to a product, or upload to the library first and attach them to products later. Images are stored on Cloudflare R2 and served via CDN.

Base URL: https://api.hydrajs.dev

Endpoints

Method Path Auth Description
GET /v1/images Publishable List images in store library
POST /v1/images Secret Upload image to library
GET /v1/products/{id}/images Publishable List images for a product
POST /v1/products/{id}/images Secret Upload image to product
POST /v1/products/{id}/images/attach Secret Attach library images to product
PATCH /v1/images/{id} Secret Update image metadata
DELETE /v1/images/{id} Secret Delete image
POST /v1/images/{id}/detach Secret Detach image from product
POST /v1/images/batch-delete Secret Delete multiple images
POST /v1/images/batch-detach Secret Detach multiple images
PATCH /v1/images/reorder Secret Reorder images by position

List library images

GET /v1/images

Returns a paginated list of images in the store’s media library. Supports filtering by category, attachment status, and search.

Query parameters

Parameter Type Default Description
limit integer 25 Results per page (1–100)
cursor string - Pagination cursor from a previous response
search string - Search by filename or alt text (max 200 chars)
category string - Filter by category: product, collection, blog, logo, general, page, marketing
unattached string - Filter by attachment status: "true" or "false"
fields string - Comma-separated fields to return

Request

curl "https://api.hydrajs.dev/v1/images?category=product&unattached=true&limit=20" \
  -H "Authorization: Bearer pk_live_YOUR_KEY"

Response 200

{
	"data": [
		{
			"id": "img_abc123def456ghij",
			"product_id": null,
			"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_abc123def456ghij.webp",
			"alt": null,
			"position": 0,
			"width": 2400,
			"height": 1600,
			"file_size": 184320,
			"lqip": "AAAQGH...",
			"filename": "hero-banner.webp",
			"mime_type": "image/webp",
			"category": "product",
			"slug": "hero-banner",
			"slug_locked": false,
			"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/hero-banner",
			"duplicate": false,
			"created_at": "2026-07-20T09:00:00Z",
			"updated_at": "2026-07-20T09:00:00Z"
		}
	],
	"pagination": {
		"cursor": "eyJ0IjoiMjAyNi...",
		"has_more": false,
		"total": 1
	}
}

Pagination

All list endpoints use cursor-based pagination. Pass the cursor value from the response to fetch the next page.


Upload image to library

POST /v1/images

Uploads an image file to the store’s media library without attaching it to a product. Send as multipart/form-data.

Form fields

Field Type Required Description
file File Yes Image file. Max 10 MB
lqip string No Base64 ThumbHash for low-quality image placeholder (generated client-side)

Supported formats

JPEG, PNG, WebP, GIF, AVIF, HEIC, and HEIF. Maximum file size is 10 MB. Image dimensions are extracted automatically via Cloudflare Images.

Request

curl -X POST https://api.hydrajs.dev/v1/images \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -F "file=@product-photo.jpg"

Response 201

{
	"data": {
		"id": "img_klm789nop012qrst",
		"product_id": null,
		"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_klm789nop012qrst.jpg",
		"alt": null,
		"position": 0,
		"width": 1200,
		"height": 1600,
		"file_size": 245760,
		"lqip": null,
		"filename": "product-photo.jpg",
		"mime_type": "image/jpeg",
		"category": null,
		"slug": "product-photo",
		"slug_locked": false,
		"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/product-photo",
		"duplicate": false,
		"created_at": "2026-08-17T10:30:00Z",
		"updated_at": "2026-08-17T10:30:00Z"
	}
}

Content-hash deduplication

Images are content-hashed. If you upload a file that is identical to one already in the library, the existing image is returned with "duplicate": true instead of creating a new entry.


List product images

GET /v1/products/{id}/images

Returns all images attached to a product, ordered by position.

Request

curl https://api.hydrajs.dev/v1/products/prod_abc123/images \
  -H "Authorization: Bearer pk_live_YOUR_KEY"

Response 200

{
	"data": [
		{
			"id": "img_ghi789jkl012mnop",
			"product_id": "prod_abc123",
			"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789jkl012mnop.webp",
			"alt": "Classic T-Shirt front view",
			"position": 0,
			"width": 1200,
			"height": 1600,
			"file_size": 184320,
			"lqip": "AAAQGH...",
			"filename": "tshirt-front.webp",
			"mime_type": "image/webp",
			"category": "product",
			"slug": "tshirt-front",
			"slug_locked": false,
			"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/tshirt-front",
			"duplicate": false,
			"created_at": "2026-05-01T14:00:00Z",
			"updated_at": "2026-05-01T14:00:00Z"
		},
		{
			"id": "img_qrs345tuv678wxyz",
			"product_id": "prod_abc123",
			"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_qrs345tuv678wxyz.webp",
			"alt": "Classic T-Shirt back view",
			"position": 1,
			"width": 1200,
			"height": 1600,
			"file_size": 156800,
			"lqip": "AAAQFk...",
			"filename": "tshirt-back.webp",
			"mime_type": "image/webp",
			"category": "product",
			"slug": "tshirt-back",
			"slug_locked": false,
			"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/tshirt-back",
			"duplicate": false,
			"created_at": "2026-05-01T14:01:00Z",
			"updated_at": "2026-05-01T14:01:00Z"
		}
	]
}

Upload image to product

POST /v1/products/{id}/images

Uploads an image file and attaches it to a specific product. Send as multipart/form-data.

Form fields

Field Type Required Description
file File Yes Image file (JPEG, PNG, WebP, GIF, AVIF, HEIC, HEIF). Max 10 MB
lqip string No Base64 ThumbHash for low-quality image placeholder

Request

curl -X POST https://api.hydrajs.dev/v1/products/prod_abc123/images \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -F "file=@photo.jpg"

Response 201

Returns the created image object with product_id set to the target product.


Attach library images to product

POST /v1/products/{id}/images/attach

Attaches one or more images from the media library to a product without re-uploading. Images that are already attached to another product are duplicated.

Request body

Field Type Required Description
image_ids string[] Yes Array of image IDs to attach (1–50)

Request

curl -X POST https://api.hydrajs.dev/v1/products/prod_abc123/images/attach \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'

Response 201

{
	"data": [
		{
			"id": "img_abc123def456ghij",
			"product_id": "prod_abc123",
			"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_abc123def456ghij.webp",
			"alt": null,
			"position": 2,
			"width": 2400,
			"height": 1600,
			"file_size": 184320,
			"lqip": "AAAQGH...",
			"filename": "hero-banner.webp",
			"mime_type": "image/webp",
			"category": "product",
			"slug": "hero-banner",
			"slug_locked": false,
			"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/hero-banner",
			"duplicate": false,
			"created_at": "2026-07-20T09:00:00Z",
			"updated_at": "2026-08-17T11:00:00Z"
		}
	]
}

Update image metadata

PATCH /v1/images/{id}

Updates an image’s metadata. Only the fields you send are changed.

Request body

Field Type Required Description
alt string No Alt text for accessibility (max 500 chars)
position integer No Display order (min 0)
slug string No URL-safe slug for friendly CDN URLs. Must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ (max 200 chars)
category string No Image category: product, collection, blog, logo, general, page, marketing

Request

curl -X PATCH https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "alt": "Classic T-Shirt front view on white background",
    "slug": "classic-tshirt-front",
    "category": "product"
  }'

Response 200

{
	"data": {
		"id": "img_ghi789jkl012mnop",
		"product_id": "prod_abc123",
		"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789jkl012mnop.webp",
		"alt": "Classic T-Shirt front view on white background",
		"position": 0,
		"width": 1200,
		"height": 1600,
		"file_size": 184320,
		"lqip": "AAAQGH...",
		"filename": "tshirt-front.webp",
		"mime_type": "image/webp",
		"category": "product",
		"slug": "classic-tshirt-front",
		"slug_locked": false,
		"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/classic-tshirt-front",
		"duplicate": false,
		"created_at": "2026-05-01T14:00:00Z",
		"updated_at": "2026-08-17T11:15:00Z"
	}
}

Delete image

DELETE /v1/images/{id}

Permanently deletes an image from storage and the database. This action cannot be undone.

Request

curl -X DELETE https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 204

Empty body.


Detach image from product

POST /v1/images/{id}/detach

Removes an image’s association with a product without deleting it. The image remains in the store’s media library for reuse.

Request

curl -X POST https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop/detach \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 204

Empty body.


Batch delete images

POST /v1/images/batch-delete

Deletes multiple images in one request. Permanently removes them from storage and the database.

Request body

Field Type Required Description
image_ids string[] Yes Array of image IDs to delete (1–50)

Request

curl -X POST https://api.hydrajs.dev/v1/images/batch-delete \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'

Response 204

Empty body.


Batch detach images

POST /v1/images/batch-detach

Detaches multiple images from their products without deleting them. The images remain in the media library.

Request body

Field Type Required Description
image_ids string[] Yes Array of image IDs to detach (1–50)

Request

curl -X POST https://api.hydrajs.dev/v1/images/batch-detach \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'

Response 204

Empty body.


Reorder images

PATCH /v1/images/reorder

Sets the display position for multiple images. Useful for drag-and-drop reordering in the admin panel.

Request body

Field Type Required Description
images object[] Yes Array of {id, position} pairs (1–100)
images[].id string Yes Image ID
images[].position integer Yes New position (min 0)

Request

curl -X PATCH https://api.hydrajs.dev/v1/images/reorder \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
      {"id": "img_ghi789jkl012mnop", "position": 0},
      {"id": "img_qrs345tuv678wxyz", "position": 1},
      {"id": "img_abc123def456ghij", "position": 2}
    ]
  }'

Response 204

Empty body.


The image object

Field Type Description
id string Unique ID (prefix: img_)
product_id string | null Product this image is attached to, or null for library-only images
src string Full CDN URL for the image
alt string | null Alt text for accessibility
position integer Display order (0-indexed)
width integer | null Image width in pixels
height integer | null Image height in pixels
file_size integer | null File size in bytes
lqip string | null Base64-encoded ThumbHash for low-quality image placeholder
filename string | null Original filename
mime_type string | null MIME type (e.g. image/webp)
category string | null Image category: product, collection, blog, logo, general, page, marketing
slug string | null URL-safe slug for friendly CDN paths
slug_locked boolean Whether the slug is locked from auto-generation
slug_url string | null Full friendly CDN URL using the slug (e.g. https://cdn.hydrajs.dev/store_xxx/i/product-photo)
duplicate boolean true if the upload was deduplicated against an existing image
created_at string ISO 8601 timestamp
updated_at string ISO 8601 timestamp