Image Search: Upload Image
curl --request POST \
--url https://app.uselayers.com/api/storefront/v1/images/upload \
--header 'Accept: <accept>' \
--header 'Content-Type: <content-type>' \
--header 'X-Storefront-Access-Token: <x-storefront-access-token>' \
--data '
{
"crop_x": 123,
"crop_y": 123,
"crop_width": 123,
"crop_height": 123
}
'import requests
url = "https://app.uselayers.com/api/storefront/v1/images/upload"
payload = {
"crop_x": 123,
"crop_y": 123,
"crop_width": 123,
"crop_height": 123
}
headers = {
"X-Storefront-Access-Token": "<x-storefront-access-token>",
"Content-Type": "<content-type>",
"Accept": "<accept>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Storefront-Access-Token': '<x-storefront-access-token>',
'Content-Type': '<content-type>',
Accept: '<accept>'
},
body: JSON.stringify({crop_x: 123, crop_y: 123, crop_width: 123, crop_height: 123})
};
fetch('https://app.uselayers.com/api/storefront/v1/images/upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.uselayers.com/api/storefront/v1/images/upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'crop_x' => 123,
'crop_y' => 123,
'crop_width' => 123,
'crop_height' => 123
]),
CURLOPT_HTTPHEADER => [
"Accept: <accept>",
"Content-Type: <content-type>",
"X-Storefront-Access-Token: <x-storefront-access-token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.uselayers.com/api/storefront/v1/images/upload"
payload := strings.NewReader("{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Storefront-Access-Token", "<x-storefront-access-token>")
req.Header.Add("Content-Type", "<content-type>")
req.Header.Add("Accept", "<accept>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.uselayers.com/api/storefront/v1/images/upload")
.header("X-Storefront-Access-Token", "<x-storefront-access-token>")
.header("Content-Type", "<content-type>")
.header("Accept", "<accept>")
.body("{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.uselayers.com/api/storefront/v1/images/upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Storefront-Access-Token"] = '<x-storefront-access-token>'
request["Content-Type"] = '<content-type>'
request["Accept"] = '<accept>'
request.body = "{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"imageId": "550e8400-e29b-41d4-a716-446655440000"
}
Image Search
Image Search: Upload Image
Image Upload API endpoint that accepts a binary image and returns an image ID you can pass into the Image Search endpoint for visual product lookup.
POST
/
images
/
upload
Image Search: Upload Image
curl --request POST \
--url https://app.uselayers.com/api/storefront/v1/images/upload \
--header 'Accept: <accept>' \
--header 'Content-Type: <content-type>' \
--header 'X-Storefront-Access-Token: <x-storefront-access-token>' \
--data '
{
"crop_x": 123,
"crop_y": 123,
"crop_width": 123,
"crop_height": 123
}
'import requests
url = "https://app.uselayers.com/api/storefront/v1/images/upload"
payload = {
"crop_x": 123,
"crop_y": 123,
"crop_width": 123,
"crop_height": 123
}
headers = {
"X-Storefront-Access-Token": "<x-storefront-access-token>",
"Content-Type": "<content-type>",
"Accept": "<accept>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Storefront-Access-Token': '<x-storefront-access-token>',
'Content-Type': '<content-type>',
Accept: '<accept>'
},
body: JSON.stringify({crop_x: 123, crop_y: 123, crop_width: 123, crop_height: 123})
};
fetch('https://app.uselayers.com/api/storefront/v1/images/upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.uselayers.com/api/storefront/v1/images/upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'crop_x' => 123,
'crop_y' => 123,
'crop_width' => 123,
'crop_height' => 123
]),
CURLOPT_HTTPHEADER => [
"Accept: <accept>",
"Content-Type: <content-type>",
"X-Storefront-Access-Token: <x-storefront-access-token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.uselayers.com/api/storefront/v1/images/upload"
payload := strings.NewReader("{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Storefront-Access-Token", "<x-storefront-access-token>")
req.Header.Add("Content-Type", "<content-type>")
req.Header.Add("Accept", "<accept>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.uselayers.com/api/storefront/v1/images/upload")
.header("X-Storefront-Access-Token", "<x-storefront-access-token>")
.header("Content-Type", "<content-type>")
.header("Accept", "<accept>")
.body("{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.uselayers.com/api/storefront/v1/images/upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Storefront-Access-Token"] = '<x-storefront-access-token>'
request["Content-Type"] = '<content-type>'
request["Accept"] = '<accept>'
request.body = "{\n \"crop_x\": 123,\n \"crop_y\": 123,\n \"crop_width\": 123,\n \"crop_height\": 123\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"imageId": "550e8400-e29b-41d4-a716-446655440000"
}
Authorization
string
required
Token-based authentication header in the form of
<YOUR_LAYERS_TOKEN>.Headers
string
default:"multipart/form-data"
required
string
default:"application/json"
required
Body
file
required
Image file to upload. Supported formats: JPEG, JPG, PNG, WebP, HEIC, and HEIF. Maximum file size: 15MB (15,360 KB). Maximum dimensions: 8,192 pixels per side and 40,000,000 total pixels.
integer
X-coordinate (in pixels) of the top-left corner of the crop region. Must be
0 or greater. When provided, all four crop parameters (crop_x, crop_y, crop_width, crop_height) are required.integer
Y-coordinate (in pixels) of the top-left corner of the crop region. Must be
0 or greater.integer
Width (in pixels) of the crop region. Must be
1 or greater.integer
Height (in pixels) of the crop region. Must be
1 or greater.Response
string
Status of the upload operation. Returns “success” on successful upload.
string
Unique UUID identifier for the uploaded image. Use this ID in the Image Search API to perform visual searches.
Usage
The Image Upload endpoint enables a two-step process for visual search:- Upload the image using this endpoint to receive an
imageId - Search with the image using the
imageIdin the Image Search API
- Better performance: Avoids repeated base64 encoding/decoding
- Caching: Processed image data is cached for faster subsequent searches
- Reduced payload size: Use lightweight UUID instead of large base64 strings
Rate limits
Layers rate limits uploads to protect image processing capacity:- 10 requests per minute per client (IP address) per store
- 120 requests per minute per store across all clients
429 Too Many Requests. Wait the number of seconds given in the Retry-After response header before retrying. To stay under the limit, upload each image once and reuse the returned imageId for subsequent Image Search requests instead of re-uploading.
Error responses
object
Validation errors returned when the request fails validation.
- File required: No image file provided
- Invalid file type: File format not supported (must be JPEG, JPG, PNG, WebP, HEIC, or HEIF)
- File too large: Image exceeds 15MB size limit
- Missing crop parameters: All four crop parameters must be provided together
Image dimension limits
Layers checks the declared dimensions of every upload before accepting it. The endpoint returns422 Unprocessable Content and does not store the image when:
- Either side exceeds 8,192 pixels, or the total pixel count exceeds 40,000,000 (width × height). The response message is
Image dimensions exceed the maximum of 8192px per side or 40000000 total pixels. - The file cannot be decoded as an image. The response message is
Image could not be decoded.
{
"status": "success",
"imageId": "550e8400-e29b-41d4-a716-446655440000"
}
{
"message": "The given data was invalid.",
"errors": {
"image": [
"The image field is required."
]
}
}
Was this page helpful?