Commit e45354276ce for woocommerce

commit e45354276cecc405dbb8b455c4955d84d1c1a262
Author: Brian <brian@brianhaas.li>
Date:   Fri Jun 12 13:36:12 2026 +0200

    feat: Add image_size query parameter to products and variations REST API endpoints (#64009)

    * add image size support

    * add image_size support

    * add image size support

    * Add changefile(s) from automation for the following project(s): woocommerce

    * fix coderabbit inputs

    * add coderabbit inputs

    * add Missing PHPDoc for the new $image_size parameter

    * add v4

    * fix lint error

    * add v3 variations rest api

    * update rest api docs

    * add docs

    * add v4 support

    * keep src/srcset/sizes aligned with image_size in products responses

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
    Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>

diff --git a/docs/apis/rest-api/v2/product-variations.mdx b/docs/apis/rest-api/v2/product-variations.mdx
index ef20da411a9..80d336f3bd4 100644
--- a/docs/apis/rest-api/v2/product-variations.mdx
+++ b/docs/apis/rest-api/v2/product-variations.mdx
@@ -680,6 +680,7 @@ woocommerce.get("products/22/variations").parsed_response
 | `on_sale`        | boolean | Limit result set to products on sale.                                                                                                   |
 | `min_price`      | string  | Limit result set to products based on a minimum price.                                                                                  |
 | `max_price`      | string  | Limit result set to products based on a maximum price.                                                                                  |
+| `image_size`     | string  | Use a specific registered image size for the returned variation image `src`. Falls back to the full size if the requested size is not registered. Default is `full`. |

 ## Update a product variation
 This API lets you make changes to a product variation.
@@ -1524,4 +1525,4 @@ woocommerce.post("products/22/variations/batch", data).parsed_response
 ```

   </TabItem>
-</Tabs>
+</Tabs>
\ No newline at end of file
diff --git a/docs/apis/rest-api/v2/products.mdx b/docs/apis/rest-api/v2/products.mdx
index 50082752e17..5a36d74f193 100644
--- a/docs/apis/rest-api/v2/products.mdx
+++ b/docs/apis/rest-api/v2/products.mdx
@@ -1591,6 +1591,7 @@ woocommerce.get("products").parsed_response
 | `on_sale`        | boolean | Limit result set to products on sale.                                                                                                   |
 | `min_price`      | string  | Limit result set to products based on a minimum price.                                                                                  |
 | `max_price`      | string  | Limit result set to products based on a maximum price.                                                                                  |
+| `image_size`     | string  | Use a specific registered image size for the returned image `src` values. Falls back to the full size if the requested size is not registered. Default is `full`. |

 ## Update a product
 This API lets you make changes to a product.
@@ -3219,4 +3220,4 @@ woocommerce.get("products/22/reviews").parsed_response
 ```

   </TabItem>
-</Tabs>
+</Tabs>
\ No newline at end of file
diff --git a/docs/apis/rest-api/v3/api-reference.mdx b/docs/apis/rest-api/v3/api-reference.mdx
index c5ca47474e6..8f16e774cf3 100644
--- a/docs/apis/rest-api/v3/api-reference.mdx
+++ b/docs/apis/rest-api/v3/api-reference.mdx
@@ -5329,6 +5329,12 @@ woocommerce.get("").parsed_response
 							"required": false,
 							"description": "Limit result set to products based on a maximum price.",
 							"type": "string"
+						},
+						"image_size": {
+							"required": false,
+							"default": "full",
+							"description": "Use a specific registered image size for the returned image src values. Falls back to the full size if the requested size is not registered.",
+							"type": "string"
 						}
 					}
 				},
@@ -5818,6 +5824,12 @@ woocommerce.get("").parsed_response
 							"enum": [ "view", "edit" ],
 							"description": "Scope under which the request is made; determines fields present in response.",
 							"type": "string"
+						},
+						"image_size": {
+							"required": false,
+							"default": "full",
+							"description": "Use a specific registered image size for the returned image src values. Falls back to the full size if the requested size is not registered.",
+							"type": "string"
 						}
 					}
 				},
@@ -6947,6 +6959,12 @@ woocommerce.get("").parsed_response
 							"required": false,
 							"description": "Limit result set to products based on a maximum price.",
 							"type": "string"
+						},
+						"image_size": {
+							"required": false,
+							"default": "full",
+							"description": "Use a specific registered image size for the returned variation image src. Falls back to the full size if the requested size is not registered.",
+							"type": "string"
 						}
 					}
 				},
@@ -7202,6 +7220,12 @@ woocommerce.get("").parsed_response
 							"enum": [ "view", "edit" ],
 							"description": "Scope under which the request is made; determines fields present in response.",
 							"type": "string"
+						},
+						"image_size": {
+							"required": false,
+							"default": "full",
+							"description": "Use a specific registered image size for the returned variation image src. Falls back to the full size if the requested size is not registered.",
+							"type": "string"
 						}
 					}
 				},
@@ -8988,4 +9012,4 @@ woocommerce.get("").parsed_response
 ```

   </TabItem>
-</Tabs>
+</Tabs>
\ No newline at end of file
diff --git a/docs/apis/rest-api/v3/product-variations.mdx b/docs/apis/rest-api/v3/product-variations.mdx
index bbb0a6f15e0..6fb7e30b385 100644
--- a/docs/apis/rest-api/v3/product-variations.mdx
+++ b/docs/apis/rest-api/v3/product-variations.mdx
@@ -313,6 +313,8 @@ This API lets you retrieve and view a specific product variation by ID.
 GET /wp-json/wc/v3/products/<product_id>/variations/<id>
 ```

+Optional query parameter: `image_size` (string). Use a specific registered image size for returned variation image `src`. Falls back to the full size when the requested size is not registered. Default is `full`.
+
 <Tabs>
   <TabItem value="curl" label="cURL">

@@ -689,6 +691,7 @@ woocommerce.get("products/22/variations").parsed_response
 | `stock_status`   | string  | Limit result set to products with specified stock status. Options: `instock`, `outofstock` and `onbackorder`.                                                                                                                                                               |
 | `virtual`        | boolean | Limit result set to virtual product variations                                                                                                                                                                                                                              |
 | `downloadable`   | boolean | Limit result set to downloadable product variations.                                                                                                                                                                                                                        |
+| `image_size`     | string  | Use a specific registered image size for the returned variation image `src`. Falls back to the full size if the requested size is not registered. Default is `full`.                                                                                                        |

 ## Update a product variation

@@ -1536,4 +1539,4 @@ woocommerce.post("products/22/variations/batch", data).parsed_response
 ```

   </TabItem>
-</Tabs>
+</Tabs>
\ No newline at end of file
diff --git a/docs/apis/rest-api/v3/products.mdx b/docs/apis/rest-api/v3/products.mdx
index 4344ac24f4b..55bd1f4543f 100644
--- a/docs/apis/rest-api/v3/products.mdx
+++ b/docs/apis/rest-api/v3/products.mdx
@@ -989,6 +989,8 @@ This API lets you retrieve and view a specific product by ID.
 GET /wp-json/wc/v3/products/<id>
 ```

+Optional query parameter: `image_size` (string). Use a specific registered image size for returned image `src` values. Falls back to the full size when the requested size is not registered. Default is `full`.
+
 <Tabs>
   <TabItem value="curl" label="cURL">

@@ -1536,6 +1538,7 @@ woocommerce.get("products").parsed_response
 | `stock_status`    | string  | Limit result set to products with specified stock status. Options: `instock`, `outofstock` and `onbackorder`.                                                                                                                                                                                        |
 | `virtual`         | boolean | Limit result set to virtual products.                                                                                                                                                                                                                                                                |
 | `downloadable`    | boolean | Limit result set to downloadable products.                                                                                                                                                                                                                                                           |
+| `image_size`      | string  | Use a specific registered image size for the returned image `src` values. Falls back to the full size if the requested size is not registered. Default is `full`.                                                                                                                                    |

 ## Duplicate product

@@ -2982,4 +2985,4 @@ woocommerce.post("products/batch", data).parsed_response
 ```

   </TabItem>
-</Tabs>
+</Tabs>
\ No newline at end of file
diff --git a/plugins/woocommerce/changelog/64009-37525-image-size-restapi b/plugins/woocommerce/changelog/64009-37525-image-size-restapi
new file mode 100644
index 00000000000..59c9fd648bb
--- /dev/null
+++ b/plugins/woocommerce/changelog/64009-37525-image-size-restapi
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add image_size query parameter to the products and product variations REST API endpoints, allowing consumers to request a specific registered WordPress image size.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-variations-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-variations-v2-controller.php
index 6fe5f040b3d..51ec47470de 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-variations-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-product-variations-v2-controller.php
@@ -148,11 +148,18 @@ class WC_REST_Product_Variations_V2_Controller extends WC_REST_Products_V2_Contr
 					),
 					'permission_callback' => array( $this, 'get_item_permissions_check' ),
 					'args'                => array(
-						'context' => $this->get_context_param(
+						'context'    => $this->get_context_param(
 							array(
 								'default' => 'view',
 							)
 						),
+						'image_size' => array(
+							'description'       => __( 'Image size to return. Accepts any registered WordPress image size.', 'woocommerce' ),
+							'type'              => 'string',
+							'default'           => 'full',
+							'sanitize_callback' => 'sanitize_text_field',
+							'validate_callback' => 'rest_validate_request_arg',
+						),
 					),
 				),
 				array(
@@ -329,7 +336,7 @@ class WC_REST_Product_Variations_V2_Controller extends WC_REST_Products_V2_Contr
 			),
 			'shipping_class'        => $object->get_shipping_class(),
 			'shipping_class_id'     => $object->get_shipping_class_id(),
-			'image'                 => current( $this->get_images( $object ) ),
+			'image'                 => current( $this->get_images( $object, $request['image_size'] ?? 'full' ) ),
 			'attributes'            => $this->get_attributes( $object ),
 			'menu_order'            => $object->get_menu_order(),
 			'meta_data'             => $object->get_meta_data(),
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
index 5fb0e0ed512..77b6348c31e 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
@@ -176,11 +176,18 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
 					),
 					'permission_callback' => array( $this, 'get_item_permissions_check' ),
 					'args'                => array(
-						'context' => $this->get_context_param(
+						'context'    => $this->get_context_param(
 							array(
 								'default' => 'view',
 							)
 						),
+						'image_size' => array(
+							'description'       => __( 'Image size to return. Accepts any registered WordPress image size.', 'woocommerce' ),
+							'type'              => 'string',
+							'default'           => 'full',
+							'sanitize_callback' => 'sanitize_text_field',
+							'validate_callback' => 'rest_validate_request_arg',
+						),
 					),
 				),
 				array(
@@ -516,10 +523,11 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
 	 * Get the images for a product or product variation.
 	 *
 	 * @param WC_Product|WC_Product_Variation $product Product instance.
+	 * @param string                          $image_size WordPress registered image size. Default 'full'.
 	 *
 	 * @return array
 	 */
-	protected function get_images( $product ) {
+	protected function get_images( $product, $image_size = 'full' ) {
 		$images         = array();
 		$attachment_ids = array();

@@ -543,7 +551,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
 				continue;
 			}

-			$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
+			$attachment = wp_get_attachment_image_src( $attachment_id, $image_size );
 			if ( ! is_array( $attachment ) ) {
 				continue;
 			}
@@ -1004,7 +1012,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
 					$base_data['tags'] = $this->get_taxonomy_terms( $product, 'tag' );
 					break;
 				case 'images':
-					$base_data['images'] = $this->get_images( $product );
+					$base_data['images'] = $this->get_images( $product, $request['image_size'] ?? 'full' );
 					break;
 				case 'attributes':
 					$base_data['attributes'] = $this->get_attributes( $product );
@@ -2575,6 +2583,13 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
 			),
 			'sanitize_callback' => 'wp_parse_list',
 		);
+		$params['image_size']   = array(
+			'description'       => __( 'Image size to return. Accepts any registered WordPress image size.', 'woocommerce' ),
+			'type'              => 'string',
+			'default'           => 'full',
+			'sanitize_callback' => 'sanitize_text_field',
+			'validate_callback' => 'rest_validate_request_arg',
+		);

 		return $params;
 	}
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
index 410c6c40e5f..5013c1a6423 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
@@ -156,7 +156,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
 			),
 			'shipping_class'        => $object->get_shipping_class(),
 			'shipping_class_id'     => $object->get_shipping_class_id(),
-			'image'                 => $this->get_image( $object, $context ),
+			'image'                 => $this->get_image( $object, $context, isset( $request['image_size'] ) ? $request['image_size'] : 'full' ),
 			'gallery_image_ids'     => $object instanceof WC_Product ? array_map( 'intval', $object->get_gallery_image_ids() ) : array(),
 			'attributes'            => $this->get_attributes( $object ),
 			'menu_order'            => $object->get_menu_order(),
@@ -448,11 +448,12 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
 	/**
 	 * Get the image for a product variation.
 	 *
-	 * @param WC_Product_Variation $variation Variation data.
-	 * @param string               $context   Context of the request: 'view' or 'edit'.
+	 * @param WC_Product_Variation $variation  Variation data.
+	 * @param string               $context    Context of the request: 'view' or 'edit'.
+	 * @param string               $image_size Optional. WordPress registered image size to use for the image src. Default 'full'.
 	 * @return array
 	 */
-	protected function get_image( $variation, $context = 'view' ) {
+	protected function get_image( $variation, $context = 'view', $image_size = 'full' ) {
 		if ( ! $variation->get_image_id( $context ) ) {
 			return;
 		}
@@ -463,7 +464,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
 			return;
 		}

-		$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
+		$attachment = wp_get_attachment_image_src( $attachment_id, $image_size );
 		if ( ! is_array( $attachment ) ) {
 			return;
 		}
@@ -1287,6 +1288,14 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
 			'validate_callback' => 'rest_validate_request_arg',
 		);

+		$params['image_size'] = array(
+			'description'       => __( 'Use a specific registered image size for the returned variation image src. Falls back to the full size if the requested size is not registered.', 'woocommerce' ),
+			'type'              => 'string',
+			'default'           => 'full',
+			'sanitize_callback' => 'sanitize_text_field',
+			'validate_callback' => 'rest_validate_request_arg',
+		);
+
 		return $params;
 	}

diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
index d4296b32f87..59353f4afa8 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
@@ -161,10 +161,11 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
 	/**
 	 * Get the images for a product or product variation.
 	 *
-	 * @param WC_Product|WC_Product_Variation $product Product instance.
+	 * @param WC_Product|WC_Product_Variation $product     Product instance.
+	 * @param string                          $image_size  Image size to use. Default 'full'.
 	 * @return array
 	 */
-	protected function get_images( $product ) {
+	protected function get_images( $product, $image_size = 'full' ) {
 		$images         = array();
 		$attachment_ids = array();

@@ -188,7 +189,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
 				continue;
 			}

-			$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
+			$attachment = wp_get_attachment_image_src( $attachment_id, $image_size );

 			if ( ! is_array( $attachment ) ) {
 				continue;
@@ -204,8 +205,8 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
 				'src'               => current( $attachment ),
 				'name'              => get_the_title( $attachment_id ),
 				'alt'               => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
-				'srcset'            => (string) wp_get_attachment_image_srcset( $attachment_id, 'full' ),
-				'sizes'             => (string) wp_get_attachment_image_sizes( $attachment_id, 'full' ),
+				'srcset'            => (string) wp_get_attachment_image_srcset( $attachment_id, $image_size ),
+				'sizes'             => (string) wp_get_attachment_image_sizes( $attachment_id, $image_size ),
 				'thumbnail'         => current( $thumbnail ),
 			);
 		}
diff --git a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Products/Controller.php b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Products/Controller.php
index bf4fb934f72..6c6b8cd1ce4 100644
--- a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Products/Controller.php
+++ b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/Products/Controller.php
@@ -277,9 +277,10 @@ class Controller extends WC_REST_Products_V2_Controller {
 	 * Get the images for a product or product variation.
 	 *
 	 * @param WC_Product|WC_Product_Variation $product Product instance.
+	 * @param string                          $image_size Image size to use for the src. Default 'full'.
 	 * @return array
 	 */
-	protected function get_images( $product ) {
+	protected function get_images( $product, $image_size = 'full' ) {
 		$images         = array();
 		$attachment_ids = array();

@@ -303,7 +304,7 @@ class Controller extends WC_REST_Products_V2_Controller {
 				continue;
 			}

-			$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
+			$attachment = wp_get_attachment_image_src( $attachment_id, $image_size );

 			if ( ! is_array( $attachment ) ) {
 				continue;
@@ -319,8 +320,8 @@ class Controller extends WC_REST_Products_V2_Controller {
 				'src'               => current( $attachment ),
 				'name'              => get_the_title( $attachment_id ),
 				'alt'               => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
-				'srcset'            => (string) wp_get_attachment_image_srcset( $attachment_id, 'full' ),
-				'sizes'             => (string) wp_get_attachment_image_sizes( $attachment_id, 'full' ),
+				'srcset'            => (string) wp_get_attachment_image_srcset( $attachment_id, $image_size ),
+				'sizes'             => (string) wp_get_attachment_image_sizes( $attachment_id, $image_size ),
 				'thumbnail'         => current( $thumbnail ),
 			);
 		}