using bulkOperationRunMutation for productUpdate

Topic summary

A developer is attempting to implement Shopify’s bulkOperationRunMutation for product updates, which requires uploading data to a Google Cloud Storage bucket that Shopify can then access.

Current Issue:

  • cURL requests work successfully
  • Python POST requests fail with error: InvalidArgument - Cannot create buckets using a POST
  • The developer has tried following both Shopify and Python documentation for multipart/form-data requests without success

Proposed Solution:
A Shopify support representative suggested using the urllib3 library to properly form-encode parameters. The recommended approach includes:

  • Creating staged uploads via Shopify’s Admin GraphQL API
  • Extracting staged target parameters and signed URL
  • Using urllib3.PoolManager() to construct the multipart form data request
  • Properly encoding file content and form parameters

Status: The discussion appears to provide a working code example but remains open for confirmation whether the solution resolved the original issue.

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

You can try formatting your code by indenting it with four spaces. Here’s your updated content:

I am trying to create bulkOperationRunMutation for updating Shopify products https://shopify.dev/docs/api/usage/bulk-operations/imports#generate-the-uploaded-url-and-parameters

For this, I am trying to upload the data to the Google Storage bucket so that Shopify can fetch the data from that bucket and run the bulk operation query. In cURL, it is working fine but when I try to send the POST request using Python, it is giving me an error: InvalidArgumentInvalid argument.

Cannot create buckets using a POST.

url = "https://url_to_bucket/"
payload={
    'Content-Type': 'text/jsonl',
    'success_action_status': '201',
    'acl': 'private',
    'key': 'upload_path_on_the_bucket',
    'x-goog-date': '20230404T073135Z',
    'x-goog-credential': 'merchant_credentials',
    'x-goog-algorithm': 'GOOG4-RSA-SHA256',
    'x-goog-signature': 'signature_token',
    'policy': 'policy_token',
    'file': 'file_contant'
}
headers = {
  'Content-Type': 'multipart/form-data'
}
response = requests.request("POST", url, headers=headers, data=payload)

cURL Code:

curl --location 'https://url_to_bucket/' \
--header 'Content-Type: multipart/form-data' \
--form 'Content-Type="text/jsonl"' \
--form 'success_action_status="201"' \
--form 'acl="private"' \
--form 'key="upload_path_on_the_bucket"' \
--form 'x-goog-date="20230404T073135Z"' \
--form 'x-goog-credential="merchant_credentials"' \
--form 'x-goog-algorithm="GOOG4-RSA-SHA256"' \
--form 'x-goog-signature="signature_token"' \
--form 'policy="policy_token"' \
--form 'file="file_contant"'

I have tried the documentation of Shopify and Python to make a multipart/form-data request but nothing worked for me.

Can someone help me with this issue? Thank you.

1 Like

Hi @navneetkmr :waving_hand:

One method is to use urllib3 to form-encode your parameters. Here is an example implementation:

staged_upload = requests.post(
    url=ADMIN_GQL,
    headers=ADMIN_HEADERS,
    data=json.dumps(payload)
)

data = staged_upload.json().get("data")
staged_target = data["stagedUploadsCreate"]["stagedTargets"][0]
target_params = staged_target["parameters"]
signed_url = staged_target["url"]

multipart_formdata = {p["name"]:p["value"] for p in target_params}
multipart_formdata["file"] = (test_filename, open(test_filename, "rb").read())

http = urllib3.PoolManager()
r = http.request(
    'POST',
    signed_url,
    fields=multipart_formdata
)

r.data.decode("utf-8")

Hope that helps!

@Umiko

1 Like