Listings Tutorial

important

These tutorials are subject to change as endpoints change during our feedback period development. We welcome your feedback! If you find an error or have a suggestion, please post it in the Open API GitHub Repository.

Listings are the pages containing products for sale in an Etsy shop. The Etsy Open API v3 supports managing listings either for an individual shop or across the Etsy marketplace as a whole, depending on your application's Access Level.

Throughout this tutorial, the instructions reference REST resources, endpoints, parameters, and response fields, which we cover in detail in Request Standards and URL Syntax.

Authorization and x-api-key header parameters#

The endpoints in this tutorial require an OAuth token in the header with listings_r and listings_w scope. If your app also deletes listings, then the token requires the listings_d scope as well. See the Authentication topic for instructions on how to generate an OAuth token with these scopes.

In addition, all Open API V3 Requests require the x-api-key: parameter in the header with your shop's Etsy App API Key keystring, which you can find in Your Apps.

Listing lifecycle and state#

After creating a listing, you, Etsy, or your application change the listing to reflect several states that determine how customers interact with the listing and the effective changes available to sellers, which map to API endpoints. The following table summarizes the states and the change operations available from the API.

statedescriptionActions and endpoints
draftinactive listing because its state is not "active" or lacks at least one imagePublish (updateListing), Delete (deleteListing)
publishedactive listing searchable by users with > 0 unsold inventoryDeactivate (updateListing), Delete (deleteListing)
deactivatedpreviously published listing deliberately deactivated, unsearchable, and unsaleablePublish (updateListing), Delete (deleteListing)
sold outpublished listing with 0 unsold inventoryDelete (deleteListing)
expiredpreviously published listing older than it's expiration that was not renewed (not charged)Publish (updateListing), Delete (deleteListing)

Listing a physical product for sale#

To add a new listing to a shop, use the createDraftListing endpoint, which adds a single product for sale to an Etsy Shop. All published listings require at least one listing image, so your application must either:

The following procedure adds a listing using images already uploaded to the shop:

  1. Form a valid URL for createDraftListing, which must include a shop_id for the shop that hosts the listing. For example, if your shop_id is "12345678," the createDraftListing URL is:

    https://api.etsy.com/v3/application/shops/12345678/listings
  1. Build the createDraftListing request body, which must include at a minimum:

    • quantity
    • title
    • description
    • price
    • who_made
    • when_made
    • taxonomy_id
    • image_ids required for active listings
  2. Execute a createDraftListing POST request with your listings_w scoped OAuth token and x-api-key. For example, a createDraftListing request to list 5 yo-yos might look like the following:

var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("quantity", "5");
urlencoded.append("title", "Vintage Duncan Toys Butterfly Yo-Yo, Red");
urlencoded.append("description", "Vintage Duncan Yo-Yo from 1976 with string, steel axle, and plastic body.");
urlencoded.append("price", "1000");
urlencoded.append("who_made", "someone_else");
urlencoded.append("when_made", "1970s");
urlencoded.append("taxonomy_id", "1");
urlencoded.append("image_ids", "378848,238298,030076");
var requestOptions = {
method: 'POST',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/listings", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

To sell variations of the same product in the same listing, such as different colored products with specific quantities for sale in each color, see Listing inventory with different properties, quantities, and prices below.

Listing a digital product for sale#

To list a digital product for sale, use createDraftListing just as you would for a physical product, but your application must set the listing's is_digital parameter to "true" and upload a digital product file for the digital product listing using uploadListingFile. If you already uploaded a digital product file to your shop, for example as part of previous listing, you can associate the file with a listing using uploadListingFile with its file ID as well. Each file in a shop is unique and managed separately, so you cannot assign or upload a file with createDraftListing.

The following procedure uploads a digital product file to a listing and updates the listing's is_digital parameter to true:

  1. Form a valid URL for uploadListingFile, which must include a shop_id and listing_id to assign the digital product file to a listing. For example, if your shop_id is "12345678" and your listing_id is "192837465," then the uploadListingFile URL is:

    https://api.etsy.com/v3/application/shops/12345678/listings/192837465/files
  2. Build the uploadListingFile request body, which must include either a file (binary) parameter for a digital product file to upload or a file_id for a file already uploaded to the shop, but not both.

  3. Execute an uploadListingFile POST request with your listings_w scoped OAuth token and x-api-key. For example, an uploadListingFile request might look like the following:

var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("file", "v8%&^%&$38owf87leshif;hnvygsldjkhnvusidsba',;bf.r;'e,rl;rtkjrj87^*^&_Iuyibdsa*^5765FtIG YtfDbf86af*rfdiidtbsdiGIgdi7vleikvvvkdljke ... d[L>(*BKbaukyfgdg'jsdkbklvh");
var requestOptions = {
method: 'POST',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/listings/192837465/files", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
  1. Set the listing's is_digital field to "true" and type to "download" with an updateListing PATCH request that includes shop_id and listing_id in the URL, a listings_w scoped OAuth token and x-api-key in the header, and the new is_digital setting in the request body. For example, an updateListing request might look like the following:
var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("is_digital", "true");
urlencoded.append("type", "download");
var requestOptions = {
method: 'PATCH',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/listings/192837465", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

Converting a physical product listing to a digital product listing#

In the event that a physical product listing needs to be changed to a digital listing, this can be accomplished via the uploadListing endpoint and passing type as "download". However, note that if the physical product listing has any variations or inventory beyond a single product, updateListing will return a 409 error. Before converting any physical listing to digital, the inventory must be reset to a single product using the uploadListingInventory endpoint.

The following is an example body of the updateListingInventory post (set your price as a float value and your sku):

{
"products": [
{
"sku": "YOUR SKU HERE",
"offerings": [
{
"quantity": 1,
"is_enabled": true,
"price": 1.23
}
],
"property_values": []
}
],
"price_on_property": [],
"quantity_on_property": [],
"sku_on_property": []
}

Adding an image to a listing#

Published listings require at least one listing image, as noted above. To upload a new image and add it to a listing, use the uploadListingImage endpoint with the shop and listing IDs, and add the image binary file in the image parameter. To make a listing active after uploading a required image, use the updateListing endpoint with the state parameter set to "active." As noted above, you can associate images with listings in a createDraftListing request using the image_ids parameter if images are already uploaded to your shop.

The following procedure uploads an image to a listing and updates the listing to active:

  1. Form a valid URL for uploadListingImage, which must include a shop_id and listing_id to assign the image to a listing. For example, if your shop_id is "12345678" and your listing_id is "192837465," then the uploadListingImage URL is:

    https://api.etsy.com/v3/application/shops/12345678/listings/192837465/images
  2. Build the uploadListingImage request body, which must include either an image (binary) parameter with a digital image as its value or a listing_image_id for an image uploaded to the shop, but not both.

  3. Execute an uploadListingImage POST request with your listings_w scoped OAuth token and x-api-key. For example, an uploadListingImage request might look like the following:

var myHeaders = new Headers();
myHeaders.append("Content-Type", "multipart/form-data");
myHeaders.append("x-api-key", "112233445566778899");
myHeaders.append("Authorization", "Bearer abcd1234efgh5678ijkl90mnopqrst");
var formdata = new FormData();
formdata.append("image", fileInput.files[0], "image.jpg");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("https://openapi.etsy.com/v3/application/shops/xxxxxxxx/listings/yyyyyyyy/images", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
  1. Set the Listing's state to "active" with an updateListing PATCH request that includes shop_id and listing_id in the URL, a listings_w scoped OAuth token and x-api-key in the header, and the new state in the request body. For example, an updateListing request might look like the following:
var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("state", "active");
var requestOptions = {
method: 'PATCH',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/listings/192837465", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

Listing inventory with different properties, quantities, and prices#

Inventory is a list of products for sale in a listing. The products are customizable, so understanding the inventory request structure is vital to offering different variations of the same product in one listing. Inventory defines products using the following components:

  • sku: Stock Keeping Unit (SKU) assigned to this product.
  • offerings: a list of prices and quantities associated with a specific product, representing purchase options visible to buyers on the Etsy shop.
    • quantity: the number of products available at this offering price
    • price: a number indicating the price of this product interpreted in the default currency of the listing/shop, which is US pennies by default.
    • is_enabled: when true, the offering is visible to buyers in the listing.
  • property_values: A list of properties differentiating this product from other products in a listing. For example, to sell sets of bed sheets in different color (white, blue, magenta, forest green, etc) and size (twin, full, queen, king) combinations, use property_values for color and size.
    • property_id: a unique number identifying this property.
    • property_name: a string name for a property.
    • scale_id: a number indexing an Etsy-defined scale. There are a lot of these, but for example shoe sizes have three available scales:
Scale IDScale NameValue IDs and Names
17US/Canadavalue_id:1329,"name":"0 (Baby)", value_id:1330,"name":"0.5 (Baby)", value_id:1331,"name":"1 (Baby)", value_id:1332,"name":"1.5 (Baby)", value_id:1333,"name":"2 (Baby)", value_id:1334,"name":"2.5 (Baby)", value_id:1335,"name":"3 (Baby)", value_id:1336,"name":"3.5 (Baby)", value_id:1337,"name":"4 (Baby)", value_id:1338,"name":"4.5 (Walker)", value_id:1339,"name":"5 (Walker)", value_id:1340,"name":"5.5 (Walker)", value_id:1341,"name":"6 (Walker)", value_id:1342,"name":"6.5 (Walker)", value_id:1343,"name":"7 (Walker)", value_id:1344,"name":"7.5 (Toddler)", value_id:1345,"name":"8 (Toddler)", value_id:1346,"name":"8.5 (Toddler)", value_id:1347,"name":"9 (Toddler)", value_id:1348,"name":"9.5 (Toddler)", value_id:1349,"name":"10 (Toddler)", value_id:1350,"name":"10.5 (Toddler)", value_id:1351,"name":"11 (Toddler)", value_id:1352,"name":"11.5 (Toddler)", value_id:1353,"name":"12 (Toddler)", value_id:1354,"name":"12.5 (Youth)", value_id:1355,"name":"13 (Youth)", value_id:1356,"name":"13.5 (Youth)", value_id:1357,"name":"1 (Youth)", value_id:1358,"name":"1.5 (Youth)", value_id:1359,"name":"2 (Youth)", value_id:1360,"name":"2.5 (Youth)", value_id:1361,"name":"3 (Youth)", value_id:1362,"name":"3.5 (Youth)", value_id:1363,"name":"4 (Youth)", value_id:1364,"name":"4.5 (Youth)", value_id:1365,"name":"5 (Youth)", value_id:1366,"name":"5.5 (Youth)", value_id:1367,"name":"6 (Youth)", value_id:1368,"name":"6.5 (Youth)", value_id:1369,"name":"7 (Youth)"
18EU"value_id":1370,"name":"15", "value_id":1371,"name":"16", "value_id":1372,"name":"17", "value_id":1373,"name":"18", "value_id":1374,"name":"19", "value_id":1375,"name":"20", "value_id":1376,"name":"21", "value_id":1377,"name":"22", "value_id":1378,"name":"23", "value_id":1379,"name":"24", "value_id":1380,"name":"25", "value_id":1381,"name":"26", "value_id":1382,"name":"27", "value_id":1383,"name":"28", "value_id":1385,"name":"29", "value_id":1386,"name":"30", "value_id":1387,"name":"31", "value_id":1388,"name":"32", "value_id":1389,"name":"33", "value_id":1390,"name":"34", "value_id":1391,"name":"35", "value_id":1392,"name":"36", "value_id":1393,"name":"37", "value_id":1394,"name":"38", "value_id":1395,"name":"39"
19UKvalue_id:1396,"name":"0 (Baby)", value_id:1397,"name":"0.5 (Baby)", value_id:1399,"name":"1 (Baby)", value_id:1401,"name":"1.5 (Baby)", value_id:1402,"name":"2 (Baby)", value_id:1403,"name":"2.5 (Baby)", value_id:1404,"name":"3 (Baby)", value_id:1405,"name":"3.5 (Walker)", value_id:1406,"name":"4 (Walker)", value_id:1407,"name":"4.5 (Walker)", value_id:1408,"name":"5 (Walker)", value_id:1409,"name":"5.5 (Walker)", value_id:1410,"name":"6 (Walker)", value_id:1411,"name":"6.5 (Toddler)", value_id:1412,"name":"7 (Toddler)", value_id:1413,"name":"7.5 (Toddler)", value_id:1414,"name":"8 (Toddler)", value_id:1415,"name":"8.5 (Toddler)", value_id:1416,"name":"9 (Toddler)", value_id:1417,"name":"9.5 (Toddler)", value_id:1418,"name":"10 (Toddler)", value_id:1419,"name":"10.5 (Toddler)", value_id:1420,"name":"11 (Toddler)", value_id:1421,"name":"11.5 (Youth)", value_id:1422,"name":"12 (Youth)", value_id:1423,"name":"12.5 (Youth)", value_id:1424,"name":"13 (Youth)", value_id:1425,"name":"13.5 (Youth)", value_id:1426,"name":"1 (Youth)", value_id:1428,"name":"2 (Youth)", value_id:1429,"name":"2.5 (Youth)", value_id:1430,"name":"3 (Youth)", value_id:1431,"name":"3.5 (Youth)", value_id:1432,"name":"4 (Youth)", value_id:1433,"name":"4.5 (Youth)", value_id:1434,"name":"5 (Youth)", value_id:1435,"name":"5.5 (Youth)", value_id:1436,"name":"6 (Youth)"
  • value_ids: a list of numbers valid for the scale_id selected indicating the product variations.
  • values: a list of strings matching the value ids selected.

The following endpoints change the listing properties and inventory for an existing listing:

  1. updateListingProperty adds properties to a listing
  2. updateListingInventory assigns skus to offerings for different property combinations

Updating Inventory#

The following procedure adds a product for sale in a listing:

  1. Form a valid URL for updateListingInventory, which must include a listing_id to change the inventory in a listing. For example, if your listing_id is "192837465," then the updateListingInventory URL is:

    https://api.etsy.com/v3/application/listings/192837465/inventory
  2. Build the updateListingInventory request body, which must include at least one product in the products parameter with nested offerings and property_values lists.

  3. Execute an updateListingInventory PUT request with a listings_w scoped OAuth token and x-api-key. For example, an updateListingInventory request to add 10 US/Canada size 4 shoes with a sku of 7836646 might look like the following:

var headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var productsArray = {
"products": [
{
"sku": "7836646",
"property_values": [
{
"property_id": 1,
"property_name": "size 4",
"scale_id": 17,
"value_ids": [
1363
],
"values": [
"4 (Youth)"
],
}
],
"offerings": [
{
"price": 500,
"quantity": 10,
"is_enabled": true
}
]
}
]
};
var requestOptions = {
method: 'PUT',
headers: headers,
body: JSON.stringify(productsArray),
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/listings/192837465/inventory", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

NOTES:

  1. When updating inventory, the entire set of products (based on variations) must be in the products array.

  2. To get the product array, call getListingInventory for the listing. From the getListingInventory response, remove the following fields: product_id, offering_id, scale_name, is_deleted and value_pairs. Also change the price array in offerings to be a decimal value instead of an array.

  3. The *_on_property values should match the property_id values, but only if those properties affect the price, quantity or sku. See the sample below for handling variations.

Handling Variations in Inventory Updates#

The following example updates a listing inventory where there are two variations:

  1. Material - "Pine", "Oak", "Walnut"

  2. Size - "3", "4", "5"

In this example, the material variation affects the price, while the size variation affects the quantity and sku of the product.

In the products array, you will have 9 entries (3 materials x 3 sizes).

Due to the size affecting both quantity and sku, when quantity is updated it must be the same value across all products sharing the same sku. The sku "woodthing3" has 3 entries in the array, but the quantity value for all three of those arrays (inside offerings) must be the same value (33 in this example).

Similarly, because material affects pricing, in each product you will see that the "Walnut" property value indicates the price in offerings is 8.00 while "Oak" is 7.00 and "Pine" is 6.00.

Note that since there is only one variation that affects the price, the price_on_property value is a single value of 507, which is the property_id for "Material". Since size affects both quantity and sku, the quantity_on_property and sku_on_property values are a single value of 100, which is the property_id for "Size".

var headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var productsArray = {
"products": [
{
"sku": "woodthing3",
"offerings": [
{
"quantity": 33,
"is_enabled": true,
"price": 8.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18156809190
],
"values": [
"3"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610620
],
"values": [
"Walnut"
]
}
]
},
{
"sku": "woodthing3",
"offerings": [
{
"quantity": 33,
"is_enabled": true,
"price": 7.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18156809190
],
"values": [
"3"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18114616675
],
"values": [
"Oak"
]
}
]
},
{
"sku": "woodthing3",
"offerings": [
{
"quantity": 33,
"is_enabled": true,
"price": 6.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18156809190
],
"values": [
"3"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610622
],
"values": [
"Pine"
]
}
]
},
{
"sku": "woodthing4",
"offerings": [
{
"quantity": 44,
"is_enabled": true,
"price": 8.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107494140
],
"values": [
"4"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610620
],
"values": [
"Walnut"
]
}
]
},
{
"sku": "woodthing4",
"offerings": [
{
"quantity": 44,
"is_enabled": true,
"price": 7.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107494140
],
"values": [
"4"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18114616675
],
"values": [
"Oak"
]
}
]
},
{
"sku": "woodthing4",
"offerings": [
{
"quantity": 44,
"is_enabled": true,
"price": 6.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107494140
],
"values": [
"4"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610622
],
"values": [
"Pine"
]
}
]
},
{
"sku": "woodthing5",
"offerings": [
{
"quantity": 55,
"is_enabled": true,
"price": 8.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107397735
],
"values": [
"5"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610620
],
"values": [
"Walnut"
]
}
]
},
{
"sku": "woodthing5",
"offerings": [
{
"quantity": 55,
"is_enabled": true,
"price": 7.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107397735
],
"values": [
"5"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18114616675
],
"values": [
"Oak"
]
}
]
},
{
"sku": "woodthing5",
"offerings": [
{
"quantity": 55,
"is_enabled": true,
"price": 6.00
}
],
"property_values": [
{
"property_id": 100,
"property_name": "Size",
"scale_id": 327,
"value_ids": [
18107397735
],
"values": [
"5"
]
},
{
"property_id": 507,
"property_name": "Material",
"scale_id": null,
"value_ids": [
18163610622
],
"values": [
"Pine"
]
}
]
}
],
"price_on_property": [507],
"quantity_on_property": [100],
"sku_on_property": [100]
};
var requestOptions = {
method: 'PUT',
headers: headers,
body: JSON.stringify(productsArray),
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/listings/192837465/inventory", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

How to Fetch Property Values for the Products Array?#

Since variations can not be added at the time of creation of a new listing, the following procedure should help with creating the products array for your updateInventoryListing call.

The property_values is required but may be empty when attempting to post to the endpoint. The properties that may be used for variations on any given listing will depend upon the category the listing is placed in.

Property ids and possible values are available via the following endpoints: getSellerTaxonomyNodes and getPropertiesByTaxonomyId.

  1. If you don't already have a list of property ids for the product properties you wish to use in the new listing, use a GET call to getSellerTaxonomyNodes first to retrieve the full hierarchy tree of seller taxonomy nodes.

  2. In the taxonomy tree, you can look for the category you wish to use and note the id.

  3. Perform a GET call to the getPropertiesByTaxonomyId endpoint with the id of the category. This will give you the possible properties for the category, along with their possible values.

Notes about taxonomy:

  1. Some of the common taxonomy node properties that Etsy uses, such as Color have a list of common values/value ids. When you pass in value_id 4 we convert that to your shop's unique ID for that color. If it's not already in the system for your shop, it will create one. And then you'll use it over and over again any time you provide 'Green' for the color in the values field OR if you pass in 4 or your custom ID in the value_ids field and subsequent queries to get inventory should return that custom ID and not our known common ID.

  2. There are also getBuyerTaxonomyNodes and getPropertiesByBuyerTaxonomyId endpoints for use by more buyer-facing apps. The difference between the two is that the levels of hierachy in the seller taxonomy is often deeper than that of Buyers. For example, a listing for "blue yarn" is in All categoriesCraft Supplies & Tools. The Craft Supplies & Tools is a buyer taxonomy. But in reality, the listing is inside of All categoriesCraft Supplies & ToolsYarn & FiberYarn. The Yarn & Fiber and Yarn category and sub category are the seller taxonomy. For sellers these are very useful categories for sorting and tracking listings. However, from a Buyer perspective, showing the category and subcategory in the category tree would end up cluttering the buyer experience. Since the yarn is really just a craft supply it makes sense to show it under that more top-level category.

Adding a shipping profile to a listing#

Shipping profiles assemble shipping details such as shipping price and processing time for recipients grouped by country or region. Every listing requires a shipping profile, so you add shipping profiles to your shop with createShopShippingProfile, and assign a shipping profile to a listing using a shipping_profile_id when you create or update the listing.

The following procedure creates a new shipping profile and returns the shipping_profile_id in the response:

  1. Form a valid URL for createShopShippingProfile, which must include a shop_id. For example, if your shop_id is "12345678", then the createShopShippingProfile URL is:

    https://api.etsy.com/v3/application/shops/12345678/shipping-profiles

  2. Build the createShopShippingProfile request body, which must include at a minimum:

  • title: Use a title that indicates the country or region.
  • origin_country_iso: The ISO code of the country from which the listing ships.
  • primary_cost: The cost of shipping to this country/region alone, measured in the store's default currency.
  • secondary_cost: The cost of shipping to this country/region with another item, measured in the store's default currency.
  • min_processing_time: The minimum time required to process to ship listings with this shipping profile.
  • max_processing_time: The maximum processing time the listing needs to ship.
  • One of destination_country_iso (see list of Alpha-2 codes here) OR destination_region (possible values are "eu" "non_eu" or "none"), but not both.
  1. Execute an createShopShippingProfile POST request with your shops_w scoped OAuth token and x-api-key, and read the generated shipping_profile_id from the response. For example, a createShopShippingProfile request for shipments from the US to the EU with free shipping might look like the following:
var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("title", "Free shipping to the EU");
urlencoded.append("origin_country_iso", "US");
urlencoded.append("primary_cost", "0");
urlencoded.append("secondary_cost", "0");
urlencoded.append("min_processing_time", "1");
urlencoded.append("max_processing_time", "5");
urlencoded.append("destination_region", "eu");
var requestOptions = {
method: 'POST',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/shipping-profiles", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
  1. Set the listing's shipping_profile_id to the Shipping ID read from the response to setting the shipping ID with an updateListing PATCH request that includes shop_id and listing_id in the URL, a listings_w scoped OAuth token and x-api-key in the header, and the new state in the request body. For example, an updateListing request might look like the following:
var headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
headers.append("x-api-key", "1aa2bb33c44d55eeeeee6fff");
headers.append("Authorization", "Bearer 12345678.jKBPLnOiYt7vpWlsny_lDKqINn4Ny_jwH89hA4IZgggyzqmV_bmQHGJ3HOHH2DmZxOJn5V1qQFnVP9bCn9jnrggCRz");
var urlencoded = new URLSearchParams();
urlencoded.append("shipping_profile_id", "6722757781");
var requestOptions = {
method: 'PATCH',
headers: headers,
body: urlencoded,
redirect: 'follow'
};
fetch("https://api.etsy.com/v3/application/shops/12345678/listings/192837465", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));