# Inventory update using shopify api using python

**URL:** <https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619>\
**Category:** Fulfillment Api\
**Tags:** Setting-Up\
**Created:** [February 7, 2024, 12:28pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619 "2024-02-07T12:28:31Z")\
**Posts on this page:** 8\
**Page:** 1

## AI-generated summary

Python examples were requested for two Shopify tasks: automatic inventory updates and fulfilling unfulfilled orders with tracking number, carrier, and shipping location.

Main technical guidance given:
- Use the Shopify GraphQL Admin API from Python, typically via `requests` and an app access token.
- For inventory changes, the suggested mutation was `inventoryAdjustQuantities` / `inventoryAdjustQuantity`.
- For fulfillment creation, `fulfillmentCreateV2` was suggested.

Current problems reported after testing:
- A fulfillment attempt using the Python `shopify` library returned HTTP 400: `expected String to be a id`, indicating the order/ID format was likely incorrect.
- The inventory example returned `404`, suggesting the endpoint, API version, or request path may be wrong.
- A later GraphQL error showed the mutation input was invalid: `inventoryItemId` and `locationId` were not accepted on `InventoryAdjustQuantityInput`, while `inventoryLevelId` was required.
- Another participant reported similar blocking/access issues, mentioning Cloudflare.

Outcome:
- No working final solution was confirmed.
- The discussion remained unresolved, with the last advice being to use Shopify’s dedicated developer forum.

<div class="post-metadata">

**Author:** ![simochouchoudan](https://avatars.discourse-cdn.com/v4/letter/s/76d3ee/32.png) [@simochouchoudan](https://community.shopify.com/u/simochouchoudan)\
**Post date:** [February 7, 2024, 12:28pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/1 "2024-02-07T12:28:31Z")

</div>

hi everyone

I need your help, please.

can you provide me with a Python code that will update the inventory automatically? Also, I need a Python code that allows me to fulfill or update unfulfilled orders with the tracking number and the name of the carrier as well as the location from where this product is shipped.

thank you in advance for your solutions and your suggestions.

Best Regards,

---

<div class="post-metadata">

**Author:** ![Liam](https://yyz1.discourse-cdn.com/shopifyforum/user_avatar/community.shopify.com/liam/32/223867_2.png) [@Liam](https://community.shopify.com/u/Liam)\
**Post date:** [February 8, 2024, 12:53pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/2 "2024-02-08T12:53:48Z")

</div>

Hi Simochouchoudan,

You can make calls to the [GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql) using Python via a library like the requests library and including your API access key, that you’d get once you create an app. For changing inventory, you’d be using [the inventoryAdjustQuantities mutation](https://shopify.dev/docs/api/admin-graphql/2024-01/mutations/inventoryAdjustQuantities) and it would look something like:

```python
import requests

# Your Shopify store URL and access token
shop_url = "yourshop.myshopify.com"
access_token = "your_access_token_here"

# The GraphQL endpoint
graphql_url = f"https://{shop_url}/admin/api/2024-01/graphql.json"

# The GraphQL mutation for updating inventory quantity
mutation = """
mutation inventoryAdjustQuantity($input: InventoryAdjustQuantityInput!) {
  inventoryAdjustQuantity(input: $input) {
    inventoryLevel {
      id
      available
    }
    userErrors {
      field
      message
    }
  }
}
"""

# Variables for the mutation
variables = {
    "input": {
        "inventoryItemId": "the_inventory_item_id",
        "locationId": "the_location_id",
        "availableDelta": 5 # Adjust by this amount, e.g., +5 to increase, -5 to decrease
    }
}

# Headers including the access token and indicating JSON content
headers = {
    "X-Shopify-Access-Token": access_token,
    "Content-Type": "application/json"
}

# Performing the POST request with the mutation and variables
response = requests.post(graphql_url, json={"query": mutation, "variables": variables}, headers=headers)

# Checking the response
if response.status_code == 200:
    # Parsing the JSON response
    json_response = response.json()
    print(json_response)
else:
    print(f"Failed to update inventory: {response.status_code}")

```

For order fulfilment you’d do something similar using [the fulfillmentCreateV2 mutation](https://shopify.dev/docs/api/admin-graphql/2024-01/mutations/fulfillmentCreateV2).

Hope this helps!

---

<div class="post-metadata">

**Author:** ![simochouchoudan](https://avatars.discourse-cdn.com/v4/letter/s/76d3ee/32.png) [@simochouchoudan](https://community.shopify.com/u/simochouchoudan)\
**Post date:** [February 8, 2024, 1:33pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/3 "2024-02-08T13:33:17Z")

</div>

Hi Liam thank you for your assistance, ill try the code and let you know if it is working for me or not. I have this python code :

import shopify

# Initialize the Shopify session

session = shopify.Session(“[xxxxxxxxxxx.myshopify.com](http://xxxxxxxxxxx.myshopify.com)”, “2023-01”, TOKEN)  
shopify.ShopifyResource.activate\_session(session)

# Specify the order ID for the order to be fulfilled

order\_id\_to\_fulfill = 5906428854597 # Replace with the actual order ID

# Create a fulfillment object for the specified order

fulfillment = shopify.Fulfillment()  
fulfillment.order\_id = order\_id\_to\_fulfill  
fulfillment.notify\_customer = False # Set to True if you want to notify the customer  
fulfillment.tracking\_info = {  
“number”: “1Z001985YW99744790”,  
“url”: “[https://www.dhl.com/de-en/home/tracking.html](https://www.dhl.com/de-en/home/tracking.html)”,  
“company”: “DHL”  
}  
fulfillment.save()

but when i run it i receive this error :

```auto
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\connection.py:286, in Connection._open(self, method, path, headers, data)
    285 try:
--> 286 http_response = self._handle_error(self._urlopen(request))
    287 except urllib.error.HTTPError as err:

File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\connection.py:316, in Connection._urlopen(self, request)
    315 if _urllib_has_timeout():
--> 316 return urllib.request.urlopen(request, timeout=self.timeout)
    317 else:

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:216, in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    215 opener = _opener
--> 216 return opener.open(url, data, timeout)

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:525, in OpenerDirector.open(self, fullurl, data, timeout)
    524 meth = getattr(processor, meth_name)
--> 525 response = meth(req, response)
    527 return response

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:634, in HTTPErrorProcessor.http_response(self, request, response)
    633 if not (200 <= code < 300):
--> 634 response = self.parent.error(
    635 'http', request, response, code, msg, hdrs)
    637 return response

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:557, in OpenerDirector.error(self, proto, *args)
    556 args = (dict, proto, meth_name) + args
--> 557 result = self._call_chain(*args)
    558 if result:

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:496, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
    495 func = getattr(handler, meth_name)
--> 496 result = func(*args)
    497 if result is not None:

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:749, in HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
    747 fp.close()
--> 749 return self.parent.open(new, timeout=req.timeout)

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:525, in OpenerDirector.open(self, fullurl, data, timeout)
    524 meth = getattr(processor, meth_name)
--> 525 response = meth(req, response)
    527 return response

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:634, in HTTPErrorProcessor.http_response(self, request, response)
    633 if not (200 <= code < 300):
--> 634 response = self.parent.error(
    635 'http', request, response, code, msg, hdrs)
    637 return response

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:563, in OpenerDirector.error(self, proto, *args)
    562 args = (dict, 'default', 'http_error_default') + orig_args
--> 563 return self._call_chain(*args)

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:496, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
    495 func = getattr(handler, meth_name)
--> 496 result = func(*args)
    497 if result is not None:

File ~\AppData\Local\anaconda3\Lib\urllib\request.py:643, in HTTPDefaultErrorHandler.http_error_default(self, req, fp, code, msg, hdrs)
    642 def http_error_default(self, req, fp, code, msg, hdrs):
--> 643 raise HTTPError(req.full_url, code, msg, hdrs, fp)

HTTPError: HTTP Error 400: Bad Request

During handling of the above exception, another exception occurred:

BadRequest Traceback (most recent call last)
Cell In[67], line 19
     13 fulfillment.notify_customer = False # Set to True if you want to notify the customer
     14 fulfillment.tracking_info = {
     15 "number": "1Z001985YW99744790",
     16 "url": "https://www.dhl.com/de-en/home/tracking.html",
     17 "company": "DHL"
     18 }
---> 19 fulfillment.save()

File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\activeresource.py:836, in ActiveResource.save(self)
    831 response = self.klass.connection.put(
    832 self._element_path(self.id, self._prefix_options),
    833 self.klass.headers,
    834 data=self.encode())
    835 else:
--> 836 response = self.klass.connection.post(
    837 self._collection_path(self._prefix_options),
    838 self.klass.headers,
    839 data=self.encode())
    840 new_id = self._id_from_response(response)
    841 if new_id:

File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\connection.py:375, in Connection.post(self, path, headers, data)
    365 def post(self, path, headers=None, data=None):
    366 """Perform an HTTP post request.
    367 
    368 Args:
   (...)
    373 A Response object.
    374 """
--> 375 return self._open('POST', path, headers=headers, data=data)

File ~\AppData\Local\anaconda3\Lib\site-packages\shopify\base.py:23, in ShopifyConnection._open(self, *args, **kwargs)
     21 self.response = None
     22 try:
---> 23 self.response = super(ShopifyConnection, self)._open(*args, **kwargs)
     24 except pyactiveresource.connection.ConnectionError as err:
     25 self.response = err.response

File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\connection.py:288, in Connection._open(self, method, path, headers, data)
    286 http_response = self._handle_error(self._urlopen(request))
    287 except urllib.error.HTTPError as err:
--> 288 http_response = self._handle_error(err)
    289 except urllib.error.URLError as err:
    290 raise Error(err, url)

File ~\AppData\Local\anaconda3\Lib\site-packages\pyactiveresource\connection.py:413, in Connection._handle_error(self, err)
    411 return err
    412 elif err.code == 400:
--> 413 raise BadRequest(err)
    414 elif err.code == 401:
    415 raise UnauthorizedAccess(err)

BadRequest: Response(code=400, body="b'{"errors":{"id":"expected String to be a id"}}'", headers={'Date': 'Thu, 08 Feb 2024 13:15:57 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'close', 'X-Sorting-Hat-PodId': '324', 'X-Sorting-Hat-ShopId': '80726786373', 'referrer-policy': 'origin-when-cross-origin', 'x-frame-options': 'DENY', 'x-shopid': '80726786373', 'x-shardid': '324', 'x-stats-userid': '', 'x-stats-apiclientid': '86061809665', 'x-stats-apipermissionid': '596308590917', 'x-shopify-api-terms': 'By accessing or using the Shopify API you agree to the Shopify API License and Terms of Use at [https://www.shopify.com/legal/api-terms',](https://www.shopify.com/legal/api-terms',) 'x-shopify-api-version': '2023-04', 'x-shopify-api-version-warning': 'https://shopify.dev/concepts/about-apis/versioning', 'http_x_shopify_shop_api_call_limit': '1/40', 'x-shopify-shop-api-call-limit': '1/40', 'server-timing': 'processing;dur=59', 'x-shopify-stage': 'production', 'content-security-policy': "default-src 'self' data: blob: 'unsafe-inline' 'unsafe-eval' [https://*](https://%2A/) shopify-pos://*; block-all-mixed-content; child-src 'self' [https://*](https://%2A/) shopify-pos://*; connect-src 'self' wss://* [https://*;](https://%2A;/) frame-ancestors 'none'; img-src 'self' data: blob: [https:;](https://;/) script-src [https://cdn.shopify.com](https://cdn.shopify.com/) [https://cdn.shopifycdn.net](https://cdn.shopifycdn.net/) [https://checkout.shopifycs.com](https://checkout.shopifycs.com/) [https://api.stripe.com](https://api.stripe.com/) [https://mpsnare.iesnare.com](https://mpsnare.iesnare.com/) [https://appcenter.intuit.com](https://appcenter.intuit.com/) [https://www.paypal.com](https://www.paypal.com/) [https://js.braintreegateway.com](https://js.braintreegateway.com/) [https://c.paypal.com](https://c.paypal.com/) [https://maps.googleapis.com](https://maps.googleapis.com/) [https://www.google-analytics.com](https://www.google-analytics.com/) [https://v.shopify.com](https://v.shopify.com/) 'self' 'unsafe-inline' 'unsafe-eval'; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=show&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Forders&source%5Bsection%5D=admin_api&source%5Buuid%5D=f8795202-94d8-499c-acc6-b65380905279", 'x-content-type-options': 'nosniff', 'x-download-options': 'noopen', 'x-permitted-cross-domain-policies': 'none', 'x-xss-protection': '1; mode=block; report=/xss-report?source%5Baction%5D=show&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Forders&source%5Bsection%5D=admin_api&source%5Buuid%5D=f8795202-94d8-499c-acc6-b65380905279', 'x-envoy-upstream-service-time': '62', 'X-Dc': 'gcp-europe-west2,gcp-europe-west3', 'X-Request-ID': 'f8795202-94d8-499c-acc6-b65380905279', 'CF-Cache-Status': 'DYNAMIC', 'Report-To': '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=I6%2BKPrbYrz8yrZMw4SWTV5tPtpUtmiS3IaUBVa3aSzWfz1qvZfRAKAf%2B3LUQA94o9NIOHpCk3vkKVvjLTzouMavJy5UW%2BBVx6cw0BFhD8svj8qPS4pLNsTf%2BUZkZn4GD98iUkwAE9Hs%3D"}],"group":"cf-nel","max_age":604800}', 'NEL': '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}', 'Server-Timing': 'processing;dur=59', 'Server': 'cloudflare', 'CF-RAY': '852424f268821b95-DUB', 'alt-svc': 'h3=":443"; ma=86400'}, msg="Bad Request")

any solution please or ideas on how to solve this problem. 

```

---

<div class="post-metadata">

**Author:** ![simochouchoudan](https://avatars.discourse-cdn.com/v4/letter/s/76d3ee/32.png) [@simochouchoudan](https://community.shopify.com/u/simochouchoudan)\
**Post date:** [February 9, 2024, 12:10pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/4 "2024-02-09T12:10:17Z")

</div>

Hi Liam unfortunatelly the code doesnt work, when i run the code, it says

```auto
Failed to update inventory: 404 

```

---

<div class="post-metadata">

**Author:** ![simochouchoudan](https://avatars.discourse-cdn.com/v4/letter/s/76d3ee/32.png) [@simochouchoudan](https://community.shopify.com/u/simochouchoudan)\
**Post date:** [February 12, 2024, 10:01am UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/5 "2024-02-12T10:01:43Z")

</div>

hi everyone any updates please

---

<div class="post-metadata">

**Author:** ![simochouchoudan](https://avatars.discourse-cdn.com/v4/letter/s/76d3ee/32.png) [@simochouchoudan](https://community.shopify.com/u/simochouchoudan)\
**Post date:** [March 12, 2024, 8:38am UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/6 "2024-03-12T08:38:02Z")

</div>

hi Liam,

this is what i get from the code you shared , any ideas please

```auto
{'errors': [{'message': 'Variable $input of type InventoryAdjustQuantityInput! was provided invalid value for inventoryItemId (Field is not defined on InventoryAdjustQuantityInput), locationId (Field is not defined on InventoryAdjustQuantityInput), inventoryLevelId (Expected value to not be null)', 'locations': [{'line': 2, 'column': 34}], 'extensions': {'value': {'inventoryItemId': '37106128519331', 'locationId': '77564969224', 'availableDelta': 1}, 'problems': [{'path': ['inventoryItemId'], 'explanation': 'Field is not defined on InventoryAdjustQuantityInput'}, {'path': ['locationId'], 'explanation': 'Field is not defined on InventoryAdjustQuantityInput'}, {'path': ['inventoryLevelId'], 'explanation': 'Expected value to not be null'}]}}]}

```

---

<div class="post-metadata">

**Author:** ![Vinh](https://yyz1.discourse-cdn.com/shopifyforum/user_avatar/community.shopify.com/vinh/32/401047_2.png) [@Vinh](https://community.shopify.com/u/Vinh)\
**Post date:** [April 19, 2026, 7:43pm UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/7 "2026-04-19T19:43:26Z")

</div>

I came up to the same Python code as @simochouchoudan and had the same cloudflare blocking access. If anyone has solved this problem, I’d appreciate a reply! Thanks

---

<div class="post-metadata">

**Author:** ![PaulNewton](https://yyz1.discourse-cdn.com/shopifyforum/user_avatar/community.shopify.com/paulnewton/32/81148_2.png) [@PaulNewton](https://community.shopify.com/u/PaulNewton)\
**Post date:** [April 20, 2026, 12:16am UTC](https://community.shopify.com/t/inventory-update-using-shopify-api-using-python/293619/8 "2026-04-20T00:16:06Z")

</div>

Use the actual developer forum  
[https://community.shopify.dev/](https://community.shopify.dev/)
