Developer API

Our services can be accessed through the powerful and easy-to-use Reducer API that covers all optimizing needs with just one method call.

If you are using WordPress, just install our WordPress plugin and you'll be up and running in seconds, without any implementation effort.

We also offer a PHP client library that features quick configuration and an easy fluent syntax for accessing our services, thus greatly reducing your implementation time.

Reducer API

The Reducer API allows you to shrink an image based on the URL of the image. The image has to be available online in order to be shrunk via this API. You can call it from any programming language that allows you to send an HTTP POST request.

API call template

Calling method Parameters

URL to call:

{"key": "your api key", "plugin_version": "MY123", "lossy": 1, "resize": 1, "resize_width": 1024, "resize_height": 1024, "cmyk2rgb": 1, "keep_exif": 0, "convertto": "+webp", "refresh": 0, "urllist": ["url1", "url2"]}

JSON Encoded parameters, as follows

  • key - Your API key.
  • plugin_version - Your API client identifier (choose it yourself)
  • lossy - 1 if we should compress the images (files) in a lossy way, 2 for glossy compression, 0 for lossless
  • wait - maximum time to wait for the optimization to be ready
  • resize - type of resize
  • resize_width - resize width in pixels
  • resize_height - resize height in pixels
  • cmyk2rgb - convert CMYK to RGB
  • keep_exif - keep the EXIF info of the file
  • convertto - conversion to other image formats
  • refresh - download the image again from source
  • urllist - list of image URLs to optimize.

API Parameters

Parameter Description

Your API key, as provided in your ShortPixel control panel and signup confirmation email.


5 chars max., alphanumeric. Choose a code for your API client (MY123, RR007 for a Ruby on Rails client, etc.).


Controls whether the image compression will use a lossy or lossless algorithm.

  • 1 - Lossy compression. Lossy has a better compression rate than lossless compression. The resulting image is not 100% identical with the original. Works well for photos taken with your camera.
  • 2 - Glossy compression. Creates images that are almost pixel-perfect identical to the originals. Best option for photographers and other professionals that use very high quality images on their sites and want best compression while keeping the quality untouched.
  • 0 - Lossless compression. The shrunk image will be identical with the original and smaller in size. Use this when you do not want to loose any of the original image's details. Works best for technical drawings, clip art and comics.


Set it between 1 and 30 to wait for that number of seconds for the conversion to be done before the API returns, 0 to return immediately. If the optimization is not done in the given amount of wait time, Code 1 (see below) is returned and you can just redo the same post later to find out if the image is ready.


The type of resizing applied to the image.

  • 0 - no resizing, the current size is preserved.
  • 1 - outer resizing (image will contain the given resize_width x resize_height rectangle).
  • 3 means inner resizing (image will be contained in the given resize_width x resize_height rectangle).


The width in pixels for the resize. Needed only if resize > 1.


The height in pixels for the resize. Needed only if resize > 1.


Convert CMYK to RGB, default is set to 1. Images for the web only need RGB format and converting them from CMYK to RGB makes them smaller.


Keep the EXIF tag of the image. Default is set to 0, meaning the EXIF tag is removed.


Convert also to other image types, when optimizing. For converting to WebP use "+webp", for converting to AVIF use "+avif". If you want to convert to both .webp and .avif please use "+webp|+avif". It is also possible to convert images from jpeg, png, gif, bmp and tiff to jpeg, png or gif. Just use "jpg", "png" or "gif" to select to what format you want to convert. Leave it empty if no conversion is needed.


If refresh is set to 1, the image is fetched again by our servers from the source and re-optimized. Otherwise, if the image is already optimized and still available on our servers, the currently optimized image info is returned.


The list of URLs where the original image is located. They need to be valid, maximum 100 URLs are allowed.

API Return

The API call returns a JSON encoded data structure that shows the outcome of the call.

Field Meaning
Status Contains the status message for the call. The status itself is an object with two fields:
  • Code - This is a numeric code identifying the outcome of the call. Can be:
    • 1 - No errors, image scheduled for processing.
    • 2 - No errors, image processed, download URL available.
    • -102 - Invalid URL. Please make sure the URL is properly urlencoded and points to a valid image file.
    • -105 - URL is missing for the call.
    • -106 - URL is inaccessible from our server(s) due to access restrictions.
    • -107 - Too many URLs in a POST, maximum allowed has been exceeded.
    • -108 - Invalid user used for optimizing images from a particular domain.
    • -113 - Too many inaccessible URLs from the same domain, please check accessibility and try again.
    • -201 - Invalid image format.
    • -202 - Invalid image or unsupported image format.
    • -203 - Could not download file.
    • -301 - The file is larger than the remaining quota.
    • -302 - The file is no longer available.
    • -303 - Internal API error: the file was not written on disk.
    • -305 - Internal API error: Unknown, details usually in message.
    • -401 - Invalid API key. Please check that the API key is the one provided to you.
    • -403 - Quota exceeded. You need to subscribe to a larger plan or to buy an additional one time package to increase your quota.
    • -404 - The maximum number of URLs in the optimization queue reached. Please try again in a minute.
    • -500 - API is in maintenance mode. Please come back later.
  • Message - A text message explaining what the code stands for.
OriginalURL This is the original URL provided in the API call.
LossyURL Download URL for the optimized image using lossy/glossy compression.
LosslessURL Download URL for the optimized image using lossless compression.
WebPLossyURL If the convertto=+webp option was provided, it contains the download URL for the image optimized and converted to WebP using lossy/glossy compression. Otherwise will return "NA".
WebPLosslessURL If the convertto=+webp option was provided, it contains the download URL for the image optimized and converted to WebP using lossless compression. Otherwise will return "NA".
AVIFLossyURL If the convertto=+avif option was provided, it contains the download URL for the image optimized and converted to AVIF using lossy/glossy compression. Otherwise will return "NA".
AVIFLosslessURL If the convertto=+avif option was provided, it contains the download URL for the image optimized and converted to AVIF using lossless compression. Otherwise will return "NA".
OriginalSize Size of the original image.
LossySize Size of the lossy/glossy compressed image.
LoselessSize Size of the lossless compressed image.
WebPLossySize Size of the lossy/glossy WebP compressed image.
WebPLoselessSize Size of the lossless WebP compressed image.
AVIFPLossySize Size of the lossy/glossy AVIF compressed image.
AVIFLoselessSize Size of the lossless AVIF compressed image.
Timestamp Timestamp of the file processing.


The syncronized api point uses the following URL: and the parameters are the same except for {url} containing the URL string, instead of the {urllist} array. This API point returns the optimized image directly with image/... mimetype header.

PHP Usage example

$URL = "";
$Lossy = "1";
$Resize = "0";
$ImagesList = array(
    "http://<<FULL URL OF THE IMAGE1>>",
    "http://<<FULL URL OF THE IMAGE2>>"
$ImagesList = array_map('rawurlencode', $ImagesList);
$Data = json_encode(array(
    "plugin_version" => "XY123", //5 chars max - choose a code for your app
    "key" => $APIKey,
    "lossy" => $Lossy,
    "urllist" => $ImagesList
$POSTArray = array(
    'http' => array(
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\n" . "Accept: application/json\r\n" . "Content-Length: " . strlen($Data) ,
        'content' => $Data
$Context = stream_context_create($POSTArray);
$Result = file_get_contents($URL, false, $Context);                  

JavaScript Usage example

JavaScript example using JQuery:

    "key": "<<YOUR_KEY_HERE>>",
    "plugin_version": "JS123",
    "lossy": 1,
    "resize": 0,
    "cmyk2rgb": 1,
    "refresh": 0,
    "urllist": ["https://www.your.domain/first/image.jpg", "https://www.your.domain/second/image.jpg"]
}), function (data) {

Check out this demo HTML/JavaScript page for a working example.

.NET Usage example (C#)

JavaScriptSerializer js = new JavaScriptSerializer();
var data = new Dictionary
               { "key", "<<YOUR_KEY_HERE>>" },
               { "plugin_version", "CS123" },
               { "lossy", "1" },
               { "resize", "0" },
               { "cmyk2rgb", "1" },
               { "urllist", new String[] {"https://www.your.domain/first/image.jpg", "https://www.your.domain/second/image.jpg"} }
string formData = js.Serialize(keyValues);
Console.WriteLine ("formData");

byte[] postData = Encoding.UTF8.GetBytes(formData);

Uri uRI = new Uri("");
HttpWebRequest request = WebRequest.Create(uRI) as HttpWebRequest;

Encoding myEncoding = Encoding.UTF8;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.KeepAlive = true;
request.AllowAutoRedirect = true;
request.ContentLength = postData.Length;

Stream outputStream = request.GetRequestStream();
outputStream.Write(postData, 0, postData.Length);

HttpWebResponse res = request.GetResponse() as HttpWebResponse;
Stream inStream = res.GetResponseStream();
StreamReader reader = new StreamReader(inStream, Encoding.UTF8);
string dataResult = reader.ReadToEnd();


Python example

import requests
import json
from pprint import pprint
image_path ='https://some.image/url.jpg'
sp_key = '<<YOUR_KEY_HERE>>'
r =
    data=json.dumps({'key': sp_key, 'lossy': 1, 'wait': 20, 'urllist':[image_path]})


POST Reducer API

The POST Reducer API allows you to shrink an image that is not accessible online, by uploading it to our servers via a POST HTTP call. You can call it from any programming language that allows you to send an HTTP POST request.

Because you are sending the files along with the POST request, you need to create a multipart POST request (instead of a JSON request as in Reducer API's case) that contains the parameters and the files. The server answer has the same structure as the one from Reducer API, though.

API call template

Calling method Parameters

URL to call:

The parameters are the same as for the Reducer API except the urllist that is replaced by either file_paths and the posted files, or file_urls.

  • file_paths - the local file full paths. These are mainly used to be included in the reporting of the optimized images, for identification (as there is no client URL of the image). If the image is stored in a temporary location, please make up some unique path which is relevant to you, for that image. These paths should be a key:value array where the key is equal to the 'name' parameter of the corresponding uploaded file.
  • file_urls - the corresponding OriginalURLs. These URLs are returned by the first call to post-reducer and they are temporary URLs available while the image is being optimized. If an image is not ready and returns pending after the first call, you should not re-upload it, but pass the OriginalURL inside this array in subsequent calls so the Post-Reducer API just checks the status and returns the results. If the file is re-uploaded, it will be put to optimization as a new file.

The parameters are sent as a multipart POST request, as in the following example (for PHP, see below a code snippet that generates a multipart POST request):

User-Agent: curl/7.21.2 (x86_64-apple-darwin)
Host: localhost:8080
Accept: */*
Content-Length: 1143
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------83ff53821b7c

Content-Disposition: form-data; name="key"

Content-Disposition: form-data; name="lossy"

Content-Disposition: form-data; name="file_paths"

{"file1": "/the/file1/path/a.png"}
Content-Disposition: form-data; name="file1"; filename="a.png"
Content-Type: application/octet-stream


API Additional Parameters

Parameter Description

The local file full paths as a json-encoded key-value array where the key is the same as the name of the file sent (file1 ...). You can provide any string for the value, but it should be unique to the file sent.


The corresponding OriginalURLs sent by the API as answer to a previous call for the same filename. Please provide only either file_paths or file_urls.

API Return

The API call returns a JSON encoded data structure that shows the outcome of the call, similarily with the Reducer API return above. There are some additional fields and error codes, though:

Field Meaning
Status Contains the status message for the call. The status itself is an object with two fields:
  • Code - This is a numeric code identifying the outcome of the call. On top of the codes above, it can be:
    • -109 - The uploaded files are not present. If the urllist is present instead, the message asks to use the Reducer API URL. The error will also be returned in case neither file_paths nor file_urls parameter is present
    • -110 - Upload error
    • -111 - File too big
    • -112 - Generic server error (details in the message field)
    • -115 - Uploaded files are missing.
    • -304 - Internal API Error - could not create user upload space. Please contact support if you encounter this error
  • Message - A text message explaining what the code stands for

PHP Multipart usage example

$URL = "";
$curl = curl_version();
$userAgent = "ShortPixel/1.0 " . " curl/" . $curl["version"];
$request = curl_init();
curl_setopt($request, CURLOPT_URL, $URL);
curl_setopt_array($request, array(
    CURLOPT_HEADER => true,
    CURLOPT_USERAGENT => $userAgent,
$files = array(
    "file1" => "/path/to/first/file.jpg",
    "file2" => "/path/to/second/file.png"
$options = array(
    "lossy" => 1,
    "file_paths" => json_encode($files)
curl_custom_postfields($request, $options, $files);
$response = curl_exec($request);

function curl_custom_postfields($ch, array $assoc = array() , array $files = array() , $header = array()) {
    // invalid characters for "name" and "filename"
    static $disallow = array(
    // build normal parameters
    foreach ($assoc as $k => $v) {
        $k = str_replace($disallow, "_", $k);
        $body[] = implode("\r\n", array(
            "Content-Disposition: form-data; name=\"{$k}\"",
            filter_var($v) ,
    // build file parameters
    foreach ($files as $k => $v) {
        switch (true) {
            case false === $v = realpath(filter_var($v)):
            case !is_file($v):
            case !is_readable($v):
                continue; // or return false, throw new InvalidArgumentException
        $data = file_get_contents($v);
        $val = explode(DIRECTORY_SEPARATOR, $v);
        $v = end( $val);
        $k = str_replace($disallow, "_", $k);
        $v = str_replace($disallow, "_", $v);
        $body[] = implode("\r\n", array(
            "Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"",
            "Content-Type: application/octet-stream",
    // generate safe boundary
    do {
        $boundary = "---------------------" . md5(mt_rand() . microtime());
    } while (preg_grep("/{$boundary}/", $body));
    // add boundary for each parameters
    array_walk($body, function (&$part) use ($boundary) {
        $part = "--{$boundary}\r\n{$part}";
    // add final boundary
    $body[] = "--{$boundary}--";
    $body[] = "";
    // set options
    return @curl_setopt_array($ch, array(
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => implode("\r\n", $body) ,
        CURLOPT_HTTPHEADER => array_merge(array(
            "Expect: 100-continue",
            "Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
        ) , $header) ,

.NET Usage example (VBNet)

Public Class shortPixelObject
    Public Property Status As shortPixelStatus
    Public Property OriginalURL As String = String.Empty
    Public Property LossyURL As String = String.Empty
    Public Property LosslessURL As String = String.Empty
    Public Property OriginalSize As Integer = 0
    Public Property LossySize As Integer = 0
    Public Property LoselessSize As Integer = 0
    Public Property Timestamp As String = String.Empty
    Public Property Key As String = String.Empty
    Public Property localPath As String = String.Empty
End Class

Private Function postShortPixel(ByVal fileStream As Stream, filename As String, Optional intHeight As Integer = 0, Optional intWith As Integer = 0) As System.IO.Stream
    Dim objResponse() As shortPixelObject
        Dim strURL As String = FunGlob.AppsettingsSACS.GetKey("ShortPixel.URL")
        Dim strAPI As String = FunGlob.AppsettingsSACS.GetKey("ShortPixel.API")
        Dim blnLossy As Boolean = (FunGlob.AppsettingsSACS.GetKey("ShortPixel.Lossy") = "1")
        Dim wait As Integer = 15

        Integer.TryParse(FunGlob.AppsettingsSACS.GetKey("ShortPixel.Wait"), wait)
        If wait = 0 Then wait = 15

        Dim imageBytesContent As HttpContent = New ByteArrayContent(imgToByteArray(fileStream))
        imageBytesContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream")

        Using client As HttpClient = New HttpClient()

            Using formData = New MultipartFormDataContent()
                formData.Add(New StringContent(strAPI), "key")
                formData.Add(New StringContent("MY123"), "plugin_version")
                If blnLossy Then
                    formData.Add(New StringContent("1"), "lossy")
                    formData.Add(New StringContent("0"), "lossy")
                End If
                formData.Add(New StringContent(wait), "wait")
                If intHeight > 0 Or intWith > 0 Then
                    formData.Add(New StringContent("1"), "resize")
                    If intHeight > 0 Then
                        formData.Add(New StringContent(intHeight), "resize_height")
                    End If
                    If intWith > 0 Then
                        formData.Add(New StringContent(intWith), "resize_width")
                    End If
                    formData.Add(New StringContent(0), "resize")
                End If
                formData.Add(New StringContent("{""file1"": """ & filename & """}"), "file_paths")
                formData.Add(imageBytesContent, "file1", filename)

                Dim response = client.PostAsync(strURL, formData).Result
                Dim responseContent = response.Content.ReadAsStringAsync().Result

                If Not responseContent Is Nothing Then
                    objResponse = JsonConvert.DeserializeObject(Of shortPixelObject())(responseContent)

                    If Not objResponse Is Nothing AndAlso objResponse.Length > 0 Then
                        If (objResponse(0).Status.Code = 1 Or objResponse(0).Status.Code = 2) Then
                            If objResponse(0).Status.Code = 2 And Len(objResponse(0).LossyURL) > 0 Then
                                Using webClient As WebClient = New WebClient()
                                    Dim imageBytes As Byte() = webClient.DownloadData(objResponse(0).LossyURL)
                                    If imageBytes.Length > 0 Then
                                        Return New MemoryStream(imageBytes)
                                    End If
                                End Using
                                Return Nothing
                            End If
                            Return Nothing
                        End If
                        Return Nothing
                    End If
                    Return Nothing
                End If
            End Using
        End Using
    Catch ex As Exception
        Return Nothing
    End Try

    Return Nothing

End Function

Code example courtesy of Marco Flores


Python example

data = {'key': ''<<YOUR_KEY_HERE>>', 'lossy': 1, 'refresh': 1, 'wait': 20}

r =
url=_'' % $key,
files={image_name: open($physical_path, 'rb')})

# Get the JSON response from the request.

# Check wether the image URL is available, and then download the image and save.
if (('LossyURL' in r.keys() and r['LossyURL']) and int(r['Status']['Code']) == 2):
os.system("wget --method=POST -O %s %s" % ($path_to_save, r['LossyURL']))

Code example courtesy of Rodrigo Nicola

If the call to post-reducer returns Status=1, Message='Image scheduled for processing', you will need to call after a few seconds the reducer end-point, like this:

image_path ='https://the.returned/OriginalURL' // for example:
sp_key = '<<YOUR_KEY_HERE>>'
r =
data=json.dumps({'key': sp_key, 'lossy': 1, 'wait': 20, 'urllist':[image_path]})