CMS and websites
PrestaShop product images over the Webservice API
Generate the sizes with SocialCutter and upload them to PrestaShop over the Webservice image endpoint, assigned to the product.
- PrestaShop
- Webservice
- images/products
- API key
- PHP
- Python
- product images
Why pre-generate the sizes
A PrestaShop product page shows the same photo in the grid, in the category listing, in the cart and in recommendations. Each slot crops to its own ratio, following the theme’s rules.
The flow is: one master goes in, SocialCutter returns every size, and PrestaShop gets the right one in each slot. The cover crop, which is the default mode, is centred: it scales the image and splits the excess evenly on both sides. Leave some margin in the master so nothing important is trimmed.
| Where it goes in the store | SocialCutter destination | Size |
|---|---|---|
| Main product image | instagram post | 1080x1080 (1:1) |
| Second portrait image | instagram story | 1080x1920 (9:16) |
| Category banner | facebook post | 1200x630 (1.91:1) |
| Store header | twitter header | 1500x500 (3:1) |
| Ad or external listing | facebook story | 1080x1920 (9:16) |
Sizes come from GET /api/v1/platforms, which is public. There is no 4:5 in the catalogue: square is 1:1 and portrait is 9:16.
Versions and permissions
Versions. The Webservice exists on both 1.7 and 8, but it is not identical. Fields change, some resources do too, and authentication especially. Work from the documentation for your version:
- PrestaShop 1.7: https://devdocs.prestashop-project.org/1.7/webservice/
- PrestaShop 8: https://devdocs.prestashop-project.org/8/webservice/
Always probe with a read before writing: GET /api/products/12?output_format=JSON must return the product. The default format is XML; output_format=JSON returns JSON on the versions that support it, and ?schema=blank describes a resource’s schema.
Permissions. The key is created in Advanced Parameters → Webservice. Permissions are per resource and per verb:
| Resource | Verbs | What for |
|---|---|---|
images | GET, POST, DELETE | Upload and list product images |
products | GET, PUT | Associate the image and read the product |
image_types | GET | List the theme’s image types |
If a verb is not ticked, the call fails with an authorisation error even when the key itself is correct.
Authentication. The current docs use the Authorization header with basic authentication: the key as the username and an empty password. Many installs still accept the key inside the URL (https://KEY@store.com/api/...) or the ws_key parameter. A key in the URL ends up in server logs, so prefer the header. curl -u "$PS_KEY:" builds that header for you.
export PS_URL="https://your-store.com"
export PS_KEY="WEBSERVICE_KEY"
export SC_KEY="sc_your_key"
1. Generate the sizes with SocialCutter
curl -s -X POST "https://api.socialcutter.theboomer.dev/api/v1/images/process" \
-H "X-API-Key: $SC_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-12-catalogue" \
-d '{
"source": { "type": "url", "value": "https://your-cdn.com/master.jpg" },
"destinations": [
{ "platform": "instagram", "format": "post" },
{ "platform": "instagram", "format": "story" }
]
}' > sc.json
jq '.image_id, (.outputs[] | {url, platform, format, width, height})' sc.json
Every output is a public URL. Idempotency-Key makes retries safe. For a local master use POST /api/v1/images/process/upload (multipart, file field, 5 MB maximum) and for volume POST /api/v1/images/batch.
2. Upload the image to the product
The images resource takes the file through POST /api/images/products/<id> in a multipart request. Download the SocialCutter output first and upload it with the key in basic authentication:
curl -s -o square.jpg "$(jq -r '.outputs[0].url' sc.json)"
# -u with the key and an empty password builds the Authorization header
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
-u "$PS_KEY:" \
-F "image=@square.jpg;type=image/jpeg" \
"$PS_URL/api/images/products/12"
The same in PHP, with CURLFile to force the file to be sent as a file:
<?php
function ps_upload_image( string $base, string $key, int $product_id, string $path ): int {
$ch = curl_init( $base . '/api/images/products/' . $product_id );
curl_setopt_array( $ch, array(
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => $key . ':',
CURLOPT_POSTFIELDS => array(
'image' => new CURLFile( $path, mime_content_type( $path ), basename( $path ) ),
),
CURLOPT_TIMEOUT => 60,
) );
$body = curl_exec( $ch );
$code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );
if ( $code >= 300 ) {
throw new RuntimeException( 'PrestaShop returned HTTP ' . $code . ': ' . $body );
}
return $code;
}
And in Python, with requests:
import requests
with open("square.jpg", "rb") as handle:
response = requests.post(
f"{BASE}/api/images/products/12",
auth=(KEY, ""),
files={"image": ("square.jpg", handle, "image/jpeg")},
timeout=60,
)
response.raise_for_status()
print(response.status_code)
The file field name and multipart handling can differ between versions: if you get a 400, check the upload example for your version in the official docs before changing your code.
3. Check and associate
List what is attached to the product:
curl -s -u "$PS_KEY:" "$PS_URL/api/images/products/12?output_format=JSON" | jq '.image[]?.id'
If your version does not associate the image on its own, add its id to the associations → images element of the product XML and save the whole product with a PUT to /api/products/<id>. PrestaShop replaces the entire resource on every PUT, so send the full XML the GET returned, not a fragment.
To see which sizes the theme generates, query GET /api/image_types. The classic ones are small_default, medium_default, large_default, home_default and cart_default; on 1.7 and 8 the list depends on your theme configuration. Regenerating those thumbnails is triggered from the admin panel (Design → Image Settings), and there is no documented Webservice endpoint to fire it. That is why, when you need an exact size or a large batch, pre-generating with SocialCutter is cheaper than depending on a full catalogue regeneration.
Cost
- 1 use per destination (platform and format) per request; repeated destinations are not charged twice.
- Failed processings are refunded.
- Plans include API and MCP: Free 3 uses/day, Basic 10, Pro 30, Agency 100.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
401 on every call | Key mistyped, Webservice disabled or IP not allowed | Check Advanced Parameters → Webservice and the key’s IP list |
401 with the key inside the URL | Your version already requires the Authorization header | Use -u "$PS_KEY:" or auth=(KEY, "") |
403 on upload | The images resource has no POST permission | Add the permission and save the key again |
400 on upload | The file field is missing or the MIME type is not an image | Send image as multipart with type=image/jpeg |
| The image uploads but is not visible | It is not associated with the product, or thumbnails were not regenerated | Add the id to associations and regenerate from the panel |
XML rejected on PUT | You sent a fragment instead of the full resource | GET the product and edit that XML |
429 from SocialCutter | Wallet quota exhausted | Check GET /api/v1/wallet or upgrade the plan |
Next steps
Frequently asked questions
Does the API key go in the URL or in a header?
It depends on the version. Older installs accept the key inside the URL (https://KEY@store.com/api/...) or as the ws_key parameter; the current docs recommend the Authorization header with basic authentication, using the key as the username and an empty password. Check which one your version accepts.
Which permissions does the Webservice key need?
Permissions are per resource and per verb. At minimum images with GET and POST (and DELETE if you clean up), products with GET and PUT to associate the image with the product, and image_types with GET to list the available types.
Does it work the same on PrestaShop 1.7 and 8?
The resource scheme is similar but not identical: fields change, some resources do too, and so does authentication. Use the documentation for the version you actually have installed, not another one.
Do I have to regenerate thumbnails afterwards?
Thumbnail regeneration is triggered from the admin panel, in the theme's image settings. If you upload the exact sizes you need with SocialCutter, you stop depending on that regeneration.
What does it cost to process one product?
1 use per destination, meaning per platform and format pair. Two destinations from the same master spend 2 uses; repeating a destination in one request is not charged twice.