Updating Products with Shopify API - Python

Topic summary

A developer encountered issues updating products via Shopify’s Python API, specifically receiving an error: “expected String to be a Hash” when attempting to add tags to products using requests.put().

Initial Problem:

  • GET and DELETE requests worked fine, but PUT requests for updating product tags failed
  • The error suggested a data format mismatch between string and hash/dictionary objects

Root Cause:

  • The newTagData was being passed as params instead of json in the PUT request
  • Missing proper headers (specifically the X-Shopify-Access-Token header)

Solution (Resolved):

  • Pass data using the json parameter: requests.put(api_url, headers=req_headers, json={"product": {"tags": newProductTags}})
  • Include required headers with access token
  • Merge existing tags with new tags before updating to avoid overwriting
  • Implement retry logic with error handling for API reliability

The working code retrieves the current product data, concatenates existing tags with new ones, then submits the update with proper headers and JSON formatting. Another user inquired if the issue was resolved, confirming community interest in the solution.

Summarized with AI on November 8. AI used: claude-sonnet-4-5-20250929.

Hello, I am having some issues updating products on the shopify API.

I am able to get, delete, and even loop over paginated API calls when pulling large amounts of data. But for some reason, I can’t get the products to update. Below is my code.

Thank you in advance!!!

def add_tag_to_product_by_id(id):
    api_url = f'{url}products/{id}.json'
    #https://(API_KEY):(TOKEN)@(SHOP_URL)/admin/api/2024-01/products/4620786335829.json
    newTagData = {"product": {"id": id, "tags": "new_tagsss, newertags"} }
    #{'product': {'id': 4620786335829, 'tags': 'new_tagsss, newertags'}}
    r = requests.put(api_url, params= newTagData )
    productsOBJ = r.json()
    productsOBJ1 = json.dumps(productsOBJ,indent = 6)
    print(productsOBJ1)
#The Printed response from the code above
{
      "errors": {
            "product": "expected String to be a Hash"
      }
}

I feel like I have tried every variation possible with the put.request call. I look up the “expected String to be a Hash”

This link is to the shopify community answer to that issue above.

Shopify Community 2280911 - Solved

After reading this I then changed my code to this.

def add_tag_to_product_by_id(id):
    api_url = f'{url}products/{id}.json'
    newTagData = {"product": {"id": id, "tags": "new_tagsss, newertags"} }
    print(type(newTagData))
    #

The above code outputs this error which is almost comical.

```python
raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not dict

So the community says make it an object, and shopify says it wants a string. But Hashed!

How do I update a product with a requests.put()?

(PS. Why does the shopify documents not have any python related examples?)

Did you ever figure this out?

I got it to work. And this is the code below. I am no longer able to edit this post.

I have one function being called in the ADD_TAGS function. I also have a one variable that is being called from outside the function. I have add those small details below.

SHOP_URL = os.getenv('SHOP_URL_MYSHOP')
api_version = "2024-01"
url_sm = "https://%s/api/%s/" % (SHOP_URL,api_version)

def get_products(id=None):
    if id is not None:
        endpoint = f'products/{id}.json'
    else:
        endpoint = 'products.json'
    
    gettingDataBool = True
    tryTimes = 0 # at 5 we will stop trying 
    while gettingDataBool:
        try:
            r = requests.get(url+endpoint)
            result = r.json()
            return result
        except:
            if tryTimes < 5:
                tryTimes = tryTimes + 1
                print('WARNING: get_products | Try failed, sleeping for 15 seconds and trying again.')
                time.sleep(15)
            else:
                #this breaks out of the while loop
                print(f'ERROR: get_products | Failed to retrieve data. | ID: {id}')
                exit()

The code below will merge the exsiting tags with the new tags. Then Save with the requests.put() call.

def add_tag_to_product_by_id(id,productTags):
    '''
    Example Usage:
    update_tags = {"tags": "new_tagsssss, nate_dog"}
    add_tag_to_product_by_id(4620786335829,update_tags)

    '''
    #access token added via header
    api_url = f'{url_sm}products/{id}.json'
    req_headers = {
        "X-Shopify-Access-Token": TOKEN,
        "Content-Type": "application/json"
    }
    #print(id)
    productData = get_products(id)
    
    newProductTags = productData['product']['tags'] + ',' +productTags
    #print(api_url)
    gettingDataBool = True
    tryTimes = 0 # at 5 we will stop trying 
    while gettingDataBool:
        try:
            r = requests.put(api_url, headers=req_headers, json={"product": {"tags" : newProductTags} })
            productsOBJ = r.json()
            print(str(id) + ' | ' +productData['product']['title'] + '  |  ' + productTags)
            #if it worked move on. 
            gettingDataBool = False
        except:
            if tryTimes < 5:
                tryTimes = tryTimes + 1
                print('WARNING: add_tag_to_product_by_id | Try failed, sleeping for 10 seconds and trying again.')
                time.sleep(10)
            else:
                print('ERROR: add_tag_to_product_by_id   || ' + str(id) + ' | ' + productTags)
                exit()

I hope this helps. Let me know if you have any issues. I might be able to help.