curl --request GET \
--url https://api.zinc.com/products/search \
--header 'Authorization: <api-key>'import requests
url = "https://api.zinc.com/products/search"
headers = {"Authorization": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: '<api-key>'}};
fetch('https://api.zinc.com/products/search', 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://api.zinc.com/products/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.zinc.com/products/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.zinc.com/products/search")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zinc.com/products/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"status": "completed",
"results": [
{
"product_id": "B000NPYY04",
"title": "Nature's Bounty Fish Oil, 1200 mg Omega-3, 200 Rapid Release Softgels",
"image": "https://m.media-amazon.com/images/I/51KGjV22PWL._AC_US218_.jpg",
"brand": "Nature's Bounty",
"price": 1248,
"stars": 4.5,
"num_reviews": 39510,
"num_offers_estimate": 12,
"num_sales": 30000,
"product_details": [
"200 Count (Pack of 1)"
],
"fresh": false,
"prime": true,
"pantry": false,
"addon": false
},
{
"product_id": "B004U3Y9FU",
"title": "Nature Made Burpless Fish Oil 1000 mg Softgels, 150 Count",
"image": "https://m.media-amazon.com/images/I/516F1UWawAL._AC_US218_.jpg",
"brand": "Nature Made",
"price": 1299,
"stars": 4.7,
"num_reviews": 22087,
"num_offers_estimate": 8,
"num_sales": 20000,
"product_details": [
"150 Count (Pack of 1)"
],
"fresh": false,
"prime": true,
"pantry": false,
"addon": false
}
],
"next_page": 2
}{
"detail": "Insufficient wallet balance for data API call"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Search Products
Search retailer catalogs for products, prices, and offer data via the Zinc API.
curl --request GET \
--url https://api.zinc.com/products/search \
--header 'Authorization: <api-key>'import requests
url = "https://api.zinc.com/products/search"
headers = {"Authorization": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: '<api-key>'}};
fetch('https://api.zinc.com/products/search', 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://api.zinc.com/products/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.zinc.com/products/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.zinc.com/products/search")
.header("Authorization", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zinc.com/products/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"status": "completed",
"results": [
{
"product_id": "B000NPYY04",
"title": "Nature's Bounty Fish Oil, 1200 mg Omega-3, 200 Rapid Release Softgels",
"image": "https://m.media-amazon.com/images/I/51KGjV22PWL._AC_US218_.jpg",
"brand": "Nature's Bounty",
"price": 1248,
"stars": 4.5,
"num_reviews": 39510,
"num_offers_estimate": 12,
"num_sales": 30000,
"product_details": [
"200 Count (Pack of 1)"
],
"fresh": false,
"prime": true,
"pantry": false,
"addon": false
},
{
"product_id": "B004U3Y9FU",
"title": "Nature Made Burpless Fish Oil 1000 mg Softgels, 150 Count",
"image": "https://m.media-amazon.com/images/I/516F1UWawAL._AC_US218_.jpg",
"brand": "Nature Made",
"price": 1299,
"stars": 4.7,
"num_reviews": 22087,
"num_offers_estimate": 8,
"num_sales": 20000,
"product_details": [
"150 Count (Pack of 1)"
],
"fresh": false,
"prime": true,
"pantry": false,
"addon": false
}
],
"next_page": 2
}{
"detail": "Insufficient wallet balance for data API call"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}product_id, title, image, brand, price, stars, and num_reviews are common to every retailer, and the remaining fields are retailer-specific and null elsewhere. The Response section below lists each field, and the example shows a real Amazon page of results.
Prices are integers in cents. Etsy results also carry currency_code, and their price is in minor units of that currency.
Pricing
| Price | $0.01 per call, drawn from your wallet |
| Charged | On each successful call, before any order is placed |
| Empty wallet | 402 with {"detail": "Insufficient wallet balance for data API call"} |
| Sandbox | Free — zn_test_ keys are never metered |
GET /wallet/me rather than retrying into a 402.POST /agent/products/search is the same search at the same price, paid per request instead of from a wallet.
Going from a result to a product
Pass a result’sproduct_id, with the same retailer, to Get Product Details or Get Product Offers. Best Buy is the exception: search returns the SKU as product_id, but the details endpoint is addressed by the bsin, the trailing id in the product URL.
Paging
Usenext_page from the response rather than counting results. A short or even empty results array with a non-null next_page is not the end of the results; this happens on free_shipping searches, on Best Buy (which server-renders only a few items per page), and on Etsy (which drops non-USD listings after paginating). next_page is null when the results are exhausted, and always null for Shopify stores, which return a single page.Authorizations
Zinc API key (Bearer zn_...)
Headers
Query Parameters
Search term
1Retailer identifier: amazon, amazon_uk, amazon_ca, amazon_de, amazon_mx, amazon_fr, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)
Page number for pagination
Only return items that ship for free (Walmart and Best Buy: ship price of 0). Currently a no-op for Amazon: the upstream search data under-reports Prime, so filtering on it would drop valid items — Amazon results are returned unfiltered. Filtering happens after pagination, so per-page counts vary; use next_page in the response to keep paging — an empty page with a non-null next_page is not the end of results. Rejected for Shopify stores: their search data carries no shipping information, so the filter cannot be honored.
Response
Normalized search results. Common fields are always present; retailer-specific fields are null or omitted for other retailers.
Response from the product search endpoint.
Show child attributes
Show child attributes
Only set on free_shipping searches: the page to request to keep paging. An empty results with a non-null next_page is NOT the end of results — request next_page to continue. None means results are exhausted (or the search was unfiltered).

