I'm using createUploadDestinationForResource SP API Uploads API call to get an upload destination for a listing image to be uploaded. It works, but when I'm using returned destination upload URL to upload an image I'm getting an error message:
Conflicting query string parameters: acl, policy
FYI: the destination upload URL returned by createUploadDestinationForResource SP API Uploads API call has the following format (I have put every URL's parameter on a separate line for readability):
https://aplus-media.s3.amazonaws.com/
?x-amz-date=20250928T224752Z
&x-amz-signature={x-amz-signature}
&x-amz-meta-owner={meta-owner}
&acl=private
&key=sc/94cb2445-81fb-4c76-9904-ef258bbc37bc.jpg
&x-amz-algorithm=AWS4-HMAC-SHA256
&policy={policy}
&x-amz-credential={access-key}/20250928/us-east-1/s3/aws4_request
where {policy} is happening to be a base64 encoded JSON:
{"conditions":
[{"bucket":"aplus-media"},
{"key":"sc/94cb2445-81fb-4c76-9904-ef258bbc37bc.jpg"},
{"acl":"private"},
{"x-amz-meta-owner":"{meta-owner}"},
{"x-amz-algorithm":"AWS4-HMAC-SHA256"},
{"x-amz-credential":"{access-key}/20250928/us-east-1/s3/aws4_request"},
{"x-amz-date":"20250928T224752Z"},
["content-length-range",1,3145728
]
],
"expiration":"2025-09-29T01:47:52.285Z"}
with values of {meta-owner} and {access-key} being the same as the destination upload url's parameters.
What could be causing this error message in the above context?
Conflicting query string parameters: acl, policy
[UPDATE]
Source code in Python:
import hashlib
import json
import os
import traceback
from typing import Dict
import requests
# Amazon SP API base URL
BASE_URL = 'https://sellingpartnerapi-na.amazon.com/'
# Your AWS credentials and IDs
CLIENT_ID = 'amzn1.application-...'
CLIENT_SECRET = 'amzn1.oa2-cs.v1....'
REFRESH_TOKEN = 'Atzr|IwEBI...'
SELLER_ID = 'A7...'
MARKETPLACE_ID = 'ATVPD...'
# Path to the image file
IMAGE_FILE_PATH = './Images/SKU-0123456789_MainImage.jpg'
def get_access_token(client_id: str, client_secret: str, refresh_token: str) -> tuple:
"""
Obtain an access token from Amazon SP API.
Args:
client_id (str): Your Amazon SP API client ID.
client_secret (str): Your Amazon SP API client secret.
refresh_token (str): Your Amazon SP API refresh token.
Returns:
tuple: A tuple containing the access token and expiration duration in seconds.
"""
lwa_authorization_url = 'https://api.amazon.com/auth/o2/token'
payload = {
'grant_type': 'refresh_token',
'client_id': client_id,
'client_secret': client_secret,
'refresh_token': refresh_token
}
response = requests.post(lwa_authorization_url, data=payload)
if response.status_code == 200:
result = response.json()
access_token = result.get('access_token')
expires_in_seconds = result.get('expires_in')
return access_token, expires_in_seconds
else:
print(f'Token request error: {response.text}')
return None, None
def normalize_path(path: str) -> str:
"""
Normalize and convert the path to an absolute path.
Args:
path (str): The original path to normalize.
Returns:
str: Normalized absolute path.
"""
return os.path.normpath(os.path.abspath(path))
def md5_from_jpeg(jpeg_file_path: str) -> str:
"""
Compute the MD5 hash of a JPEG file.
Args:
jpeg_file_path (str): Path to the JPEG file.
Returns:
str: Hexadecimal representation of the MD5 hash.
"""
hasher = hashlib.md5()
with open(jpeg_file_path, 'rb') as jpeg_file:
buf = jpeg_file.read()
hasher.update(buf)
return hasher.hexdigest()
def create_upload_destination(seller_id: str, marketplace_ids: list, auth_token: str) -> dict:
"""
Create a temporary URL for uploading an image.
Args:
seller_id (str): Your Amazon seller ID.
marketplace_ids (list): List of marketplace IDs.
auth_token (str): Authenticated access token.
Returns:
dict: Payload returned from the API with the upload URL.
"""
md5_hash = md5_from_jpeg(normalize_path(IMAGE_FILE_PATH))
headers = {
'x-amz-access-token': auth_token,
'content-type': 'application/json'
}
params = {
'marketplaceIds': marketplace_ids,
'contentMD5': md5_hash,
'resource': 'aplus/2020-11-01/contentDocuments',
'contentType': 'image/jpeg',
}
request_url = BASE_URL + 'uploads/2020-11-01/uploadDestinations/aplus/2020-11-01/contentDocuments'
response = requests.post(request_url, headers=headers, params=params)
if response.status_code == 201:
return response.json()['payload']
else:
raise ValueError(f'Error creating temp URL: {response.text}')
def upload_image(upload_url: str, image_file_path: str, auth_token: str) -> None:
"""
Upload an image to the provided URL.
Args:
upload_url (str): Temporary URL for uploading the image.
image_file_path (str): Local path to the image file.
auth_token (str): Authenticated access token.
"""
headers = {
'x-amz-access-token': auth_token,
'content-type': 'application/jpeg'
}
with open(image_file_path, 'rb') as file:
response = requests.put(upload_url, data=file, headers=headers)
#
# Error
# 'Conflicting query string parameters: acl, policy'
# happens here after the above call to requests.put(...)
#
if response.status_code != 200:
raise ValueError(f'Error loading the image: {response.text}')
else:
print('The image successfully loaded.')
if __name__ == '__main__':
try:
task_name = 'Upload Listing Image'
print('=========================================')
print(f'*** `{task_name}` started ***')
print('=========================================')
# Step 1: Compute MD5 hash of the image file
md5_hash = md5_from_jpeg(normalize_path(IMAGE_FILE_PATH))
print(f'File = {normalize_path(IMAGE_FILE_PATH)}')
print(f'MD5 hash = {md5_hash}')
# Step 2: Acquire an access token
access_token, expires_in = get_access_token(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN)
if access_token:
print(f'Obtained x-amz-access-token: {access_token}')
print(f'Token will expire in {expires_in} seconds.')
else:
print('Failed to get token.')
exit()
# Step 3: Create a temporary URL for uploading the image
temp_upload_payload = create_upload_destination(SELLER_ID, [MARKETPLACE_ID], access_token)
upload_url = temp_upload_payload['url']
# Step 4: Upload the image using the temporary URL
upload_image(upload_url, IMAGE_FILE_PATH, access_token)
except Exception as error:
print('---')
print(f'Runtime error:\n{error}')
print('---')
traceback.print_exc()
finally:
print('=========================================')
print(f'*** `{task_name}` completed ***')
print('=========================================')