diff --git a/etsy_python/v3/resources/Listing.py b/etsy_python/v3/resources/Listing.py
index c395700..b79bb50 100644
--- a/etsy_python/v3/resources/Listing.py
+++ b/etsy_python/v3/resources/Listing.py
@@ -168,6 +168,24 @@ def get_listings_by_listings_ids(
)
return self.get_listings_by_listing_ids(listing_ids, includes, legacy)
+ def get_listings_inventory_by_listing_ids(
+ self, listing_ids: List[int]
+ ) -> Union[Response, RequestException]:
+ endpoint = "/listings/batch/inventory"
+ query_params: Dict[str, Any] = {
+ "listing_ids": ",".join(list(map(str, listing_ids))),
+ }
+ return self.session.make_request(endpoint, query_params=query_params)
+
+ def get_listings_shipping_by_listing_ids(
+ self, listing_ids: List[int]
+ ) -> Union[Response, RequestException]:
+ endpoint = "/listings/batch/shipping"
+ query_params: Dict[str, Any] = {
+ "listing_ids": ",".join(list(map(str, listing_ids))),
+ }
+ return self.session.make_request(endpoint, query_params=query_params)
+
def get_featured_listings_by_shop(
self, shop_id: int, limit: int = 25, offset: int = 0,
legacy: Optional[bool] = None,
diff --git a/specs/baseline.json b/specs/baseline.json
index a3de734..4c63119 100644
--- a/specs/baseline.json
+++ b/specs/baseline.json
@@ -2673,6 +2673,16 @@
}
}
},
+ "409": {
+ "description": "There was a request conflict with the current state of the target resource. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ },
"500": {
"description": "The server encountered an internal error. See the error message for details.",
"content": {
@@ -2694,6 +2704,82 @@
]
}
},
+ "/v3/application/listings/batch/inventory": {
+ "get": {
+ "operationId": "getListingsInventoryByListingIds",
+ "description": "
This endpoint is ready for production use.
\n\nRetrieves the inventory record for each listing referenced by listing ID. Requires the listings_r OAuth scope. Limit 100 listing IDs per request.",
+ "tags": [
+ "ShopListing Inventory"
+ ],
+ "parameters": [
+ {
+ "name": "listing_ids",
+ "in": "query",
+ "description": "The list of numeric IDS for the listings in a specific Etsy shop.",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "description": "The list of numeric IDS for the listings in a specific Etsy shop.",
+ "items": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 1
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of listings with their inventory records.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ShopListingsWithAssociations"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "There was a problem with the request data. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A resource could not be found. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "The server encountered an internal error. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "api_key": [],
+ "oauth2": [
+ "listings_r"
+ ]
+ }
+ ]
+ }
+ },
"/v3/application/listings/{listing_id}/inventory/products/{product_id}": {
"get": {
"operationId": "getListingProduct",
@@ -3937,6 +4023,82 @@
}
}
},
+ "/v3/application/listings/batch/shipping": {
+ "get": {
+ "operationId": "getListingsShippingByListingIds",
+ "description": "This endpoint is ready for production use.
\n\nRetrieves the shipping profile for each listing referenced by listing ID. Requires the shops_r OAuth scope. Limit 100 listing IDs per request.",
+ "tags": [
+ "ShopListing"
+ ],
+ "parameters": [
+ {
+ "name": "listing_ids",
+ "in": "query",
+ "description": "The list of numeric IDS for the listings in a specific Etsy shop.",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "description": "The list of numeric IDS for the listings in a specific Etsy shop.",
+ "items": {
+ "type": "integer",
+ "format": "int64",
+ "minimum": 1
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of listings with their shipping profiles.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ShopListingsWithAssociations"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "There was a problem with the request data. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A resource could not be found. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "The server encountered an internal error. See the error message for details.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorSchema"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "api_key": [],
+ "oauth2": [
+ "shops_r"
+ ]
+ }
+ ]
+ }
+ },
"/v3/application/shops/{shop_id}/listings/{listing_id}/transactions": {
"get": {
"operationId": "getShopReceiptTransactionsByListing",
@@ -12915,6 +13077,11 @@
"type": "string",
"description": "A description string of the product for sale in the listing."
},
+ "rich_description": {
+ "type": "string",
+ "description": "The seller-authored HTML rich-text description of the product when the listing uses rich text; null for plain-text listings. The plain-text `description` field is always populated. This value is HTML and consumers MUST sanitize it before rendering it in any HTML context.",
+ "nullable": true
+ },
"state": {
"type": "string",
"description": "When _updating_ a listing, this value can be either `active` or `inactive`. Note: Setting a `draft` listing to `active` will also publish the listing on etsy.com and requires that the listing have an image set. Setting a `sold_out` listing to active will update the quantity to 1 and renew the listing on etsy.com.",
@@ -13378,6 +13545,11 @@
"type": "string",
"description": "A description string of the product for sale in the listing."
},
+ "rich_description": {
+ "type": "string",
+ "description": "The seller-authored HTML rich-text description of the product when the listing uses rich text; null for plain-text listings. The plain-text `description` field is always populated. This value is HTML and consumers MUST sanitize it before rendering it in any HTML context.",
+ "nullable": true
+ },
"state": {
"type": "string",
"description": "When _updating_ a listing, this value can be either `active` or `inactive`. Note: Setting a `draft` listing to `active` will also publish the listing on etsy.com and requires that the listing have an image set. Setting a `sold_out` listing to active will update the quantity to 1 and renew the listing on etsy.com.",
diff --git a/tests/test_listing_resource.py b/tests/test_listing_resource.py
index 527520d..9918f06 100644
--- a/tests/test_listing_resource.py
+++ b/tests/test_listing_resource.py
@@ -310,6 +310,58 @@ def test_legacy_forwarded_through_deprecated_alias(self, mock_session):
assert qp["legacy"] is True
+class TestGetListingsInventoryByListingIds:
+ def test_listing_ids_joined(self, mock_session):
+ mock_session.make_request.return_value = Response(
+ 200, make_shop_listing_collection()
+ )
+ resource = ListingResource(session=mock_session)
+
+ resource.get_listings_inventory_by_listing_ids([111, 222, 333])
+
+ endpoint = mock_session.make_request.call_args[0][0]
+ qp = mock_session.make_request.call_args[1]["query_params"]
+ assert endpoint == "/listings/batch/inventory"
+ assert qp["listing_ids"] == "111,222,333"
+
+ def test_single_listing_id(self, mock_session):
+ mock_session.make_request.return_value = Response(
+ 200, make_shop_listing_collection()
+ )
+ resource = ListingResource(session=mock_session)
+
+ resource.get_listings_inventory_by_listing_ids([111])
+
+ qp = mock_session.make_request.call_args[1]["query_params"]
+ assert qp["listing_ids"] == "111"
+
+
+class TestGetListingsShippingByListingIds:
+ def test_listing_ids_joined(self, mock_session):
+ mock_session.make_request.return_value = Response(
+ 200, make_shop_listing_collection()
+ )
+ resource = ListingResource(session=mock_session)
+
+ resource.get_listings_shipping_by_listing_ids([111, 222, 333])
+
+ endpoint = mock_session.make_request.call_args[0][0]
+ qp = mock_session.make_request.call_args[1]["query_params"]
+ assert endpoint == "/listings/batch/shipping"
+ assert qp["listing_ids"] == "111,222,333"
+
+ def test_single_listing_id(self, mock_session):
+ mock_session.make_request.return_value = Response(
+ 200, make_shop_listing_collection()
+ )
+ resource = ListingResource(session=mock_session)
+
+ resource.get_listings_shipping_by_listing_ids([111])
+
+ qp = mock_session.make_request.call_args[1]["query_params"]
+ assert qp["listing_ids"] == "111"
+
+
class TestGetFeaturedListingsByShop:
def test_basic_call(self, mock_session):
mock_session.make_request.return_value = Response(