Introduction
The Squirrel API gives your application the product data and prices that power Squirrel: send a product code, a retailer's product page or a product name, and get back the product and the offers of the retailers you work with, with your own affiliate tracking already in each link.
With the API you can:
- find products and prices: by barcode (EAN, UPC), ASIN, SKU, product page URL or name, in the countries Squirrel covers;
- keep up to date: receive a webhook when a product you asked for changes, or set price-drop and back-in-stock alerts for your readers;
- report: read your impressions and clicks, your Insights transactions and your CPC campaigns;
- connect AI assistants to Squirrel's product search with the MCP servers.
Everything runs from your Squirrel account: the retailers and affiliate networks you select there are the ones the API answers with.
Quickstart
- Get your API key. Sign in to your Squirrel account and open API Integration (in the Offers menu). Copy the key: it is the bearer token every call sends.
- Search for a product. Send its barcode to
POST /products/search, as in the samples on the right. Replace[YOUR_ACCESS_TOKEN]with your key. - Read the answer.
dataholds one list per code you sent, with the product in each country and its offers. The first time you ask for a product, Squirrel answers "We are refreshing our cache" and fetches it in the background: ask again a minute later, or sendfirst_response_liveto get a quick first answer straight away.
curl --request POST 'https://api.getsquirrel.co/api/v2/products/search' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'code=4548736132580' \
--form 'country=GB'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/products/search');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'code' => '4548736132580',
'country' => 'GB',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/products/search', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
code: '4548736132580',
country: 'GB',
}),
});
const result = await response.json();A product in the cache returns JSON structured like this (shortened):
{
"execution_time_ms": 41,
"monthly_quota": 100000,
"current_usage": 1523,
"error": false,
"data": {
"4548736132580": [
{
"name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"squirrel_id": 1234567,
"price": 228.99,
"rrp": 379,
"offer_url": "https://www.awin1.com/cread.php?awinmid=0000&awinaffid=[YOUR_AWIN_ID]&clickref=[CLICK_REF]&ued=https%3A%2F%2Fwww.currys.co.uk%2Fproducts%2Fsony-wh-1000xm5",
"retailer": {
"id": 10322,
"name": "Currys PC World",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/currys.png",
"searched_retailer": false,
"retailer_domain": "www.currys.co.uk"
},
"image": "https://m.media-amazon.com/images/I/31SNSWUACeL._SL160_.jpg",
"country": "GB"
}
]
},
"request_ids": {
"4548736132580": "0123456789abcdef0123456789abcdef"
},
"message": ""
}That's it. The rest of this reference explains every endpoint, its parameters and its answers. Each endpoint has a sample in curl, PHP and JavaScript: pick the language with the tabs above the code.
Authentication
The API uses a bearer token: send your API key in the Authorization header of every call.
Authorization: Bearer [YOUR_ACCESS_TOKEN]
Your key is on the API Integration page of your Squirrel account. A call without a key, or with a key that is wrong or no longer valid, gets a 401 answer.
Also send Accept: application/json, so that every answer, errors included, comes back as JSON.
Keys limited to statistics. Some accounts have a key for reporting only. Such a key can call Statistics, Insights, CPC campaigns and Countries; every product, webhook, alert, retailer and CSV Master call answers 403 with "Your API key is limited to the stats and insights endpoints. Contact support to enable product search."
Base URL. Every path in this reference is relative to:
https://api.getsquirrel.co/api/v2
Request parameters can be sent as form fields (multipart/form-data or application/x-www-form-urlencoded) or as a JSON body with Content-Type: application/json.
Limits and quotas
| Limit | Value |
|---|---|
| Rate limit | A number of calls per minute, shared by all the keys of your account group. 60 a minute unless your plan says otherwise. Above it the API answers 429: wait until the next minute before you retry. |
| Monthly quota | The number of calls your plan allows in a calendar month, shared by all the keys of your account group. Each answer shows it in monthly_quota, and what the whole group has used in current_usage. When it is used up the API answers 429 until the next month. |
| Codes or URLs per call | At most 10 in one product search, separated by |. |
| Statistics | A range of at most 31 days, ending yesterday at the latest; at most 1,000 rows per page. |
| Insights | At most 100 transactions per page. |
| CPC campaigns | At most 200 campaigns in a list; at most 366 rows in one partner-clicks push. |
A product search whose answer has no product yet (the "We are refreshing our cache" message, or a quick first response) is not counted in current_usage.
Responses and errors
Every answer is JSON. A successful answer has HTTP status 200 and this wrapper:
{
"execution_time_ms": 41,
"monthly_quota": 100000,
"current_usage": 1523,
"error": false,
"data": {},
"request_ids": {},
"message": ""
}| Field | Description |
|---|---|
| error | false on success, true on an error. |
| data | The answer itself: an object or a list, described with each endpoint. |
| message | Empty on success; on an error, what went wrong. |
| monthly_quota | The calls your plan allows this calendar month, for your whole account group. |
| current_usage | The calls all the keys of your account group have made this calendar month, this one included. |
| execution_time_ms | How long the API took, in milliseconds. Not in every answer. |
| request_ids | On a product search, the id of each code or URL you sent; it is the id of its webhook. Not in every answer. |
Some endpoints add their own fields next to these (for example returned_results and total_results on Insights); some answer with error, data and message only, plus the quota fields.
Response headers worth knowing (product search only):
| Header | Description |
|---|---|
| X-Response-Version | Where the answer came from: cached 1 from Squirrel's cache, cached 0 not from the cache (a new or refreshed request), first-live 1 a quick first response. With several codes in one call it describes the last one. |
| X-Response-TTL | The cache_ttl used for the request, in minutes. |
| X-Response-Age | On an answer from the cache, how old it is, in seconds. |
Errors
An error answer looks like this:
{
"error": true,
"data": "",
"message": "Please send no more than 10 products per call",
"monthly_quota": 100000,
"current_usage": 1523
}An error answer has error: true, says what went wrong in message and comes with an HTTP status other than 200. Some errors give the detail in data as a one-item list, for example ["Missing parameter alert_value"]; others only say "There was an error, the Squirrel team has been notified."
Treat any status other than 200 as an error, and read message (and data) for the reason.
| Status | Meaning |
|---|---|
| 400 | Bad request. A parameter is missing or not valid, for example "Please send at least one product to fetch", "Please use only one type of search per request", "One of the provided country ISO is incorrect, please double check your parameters". When the API has no specific message, the answer is "There was an error, the Squirrel team has been notified.", sometimes with the reason in data. |
| 401 | Unauthorized. The API key is missing, wrong or no longer valid: message is "session.expired.unauthenticated." |
| 403 | Forbidden. Your key is limited to the stats and Insights endpoints (see Authentication); or the resource belongs to another account (a webhook, a CSV file, an API-links call without the feature); or the address does not exist: "You are not allowed to do this." |
| 404 | Not found. The resource does not exist or is not yours (a campaign, a CSV file): "Resource not found..." |
| 399 | Invalid input on Insights and CPC campaigns: a date missing or not valid, a start_date after the end_date, an empty push. The reason is in message. 399 is not a standard HTTP status: check for it explicitly if your HTTP client only treats 4xx and 5xx as errors. |
| 429 | Too many requests. Either the rate limit, "Too many requests - you have been rate limited. Please slow down your requests." (wait until the next minute), or the monthly quota, "Monthly quota exceeded. Please upgrade your plan or wait until the next billing period." |
| 500 | Server error. Something failed on Squirrel's side; the team is notified. Try again later. |
| 503 | Service unavailable. The API is down for maintenance or overloaded. Try again in a few minutes, waiting longer between each try. |
Products
Search by code or URL
/products/searchcurl --request POST 'https://api.getsquirrel.co/api/v2/products/search' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'code=4548736132580' \
--form 'country=GB'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/products/search');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'code' => '4548736132580',
'country' => 'GB',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/products/search', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
code: '4548736132580',
country: 'GB',
}),
});
const result = await response.json();The above command returns JSON structured like this (two offers shown):
{
"execution_time_ms": 41,
"monthly_quota": 100000,
"current_usage": 1523,
"error": false,
"data": {
"4548736132580": [
{
"name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"squirrel_id": 1234567,
"price": 228.99,
"rrp": 379,
"offer_url": "https://www.awin1.com/cread.php?awinmid=0000&awinaffid=[YOUR_AWIN_ID]&clickref=[CLICK_REF]&ued=https%3A%2F%2Fwww.currys.co.uk%2Fproducts%2Fsony-wh-1000xm5",
"retailer": {
"id": 10322,
"name": "Currys PC World",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/currys.png",
"searched_retailer": false,
"retailer_domain": "www.currys.co.uk"
},
"image": "https://m.media-amazon.com/images/I/31SNSWUACeL._SL160_.jpg",
"make": "Sony",
"model": "WH-1000XM5",
"last_updated": "2026-10-26 09:12:44",
"last_updated_timestamp": 1793005964,
"all_offers": [
{
"id": 100000001,
"price": 228.99,
"offer_url": "https://www.awin1.com/cread.php?awinmid=0000&awinaffid=[YOUR_AWIN_ID]&clickref=[CLICK_REF]&ued=https%3A%2F%2Fwww.currys.co.uk%2Fproducts%2Fsony-wh-1000xm5",
"offer_clean_url": "https://www.currys.co.uk/products/sony-wh-1000xm5",
"retailer": {
"id": 10322,
"name": "Currys PC World",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/currys.png",
"searched_retailer": false,
"retailer_domain": "www.currys.co.uk"
},
"retailer_id": 10322,
"affiliate_network_id": 117,
"api_id": 9,
"rrp": 0,
"max_price": 279,
"is_promoted": 0,
"image_url": "",
"category": "Headphones",
"min_price": 228.99,
"in_stock_status": true,
"affiliate_network_name": "[CSV] Awin UK"
},
{
"id": 100000002,
"price": 229.99,
"offer_url": "https://www.anrdoezrs.net/links/[YOUR_CJ_ID]/type/dlg/sid/[CLICK_REF]/https://www.argos.co.uk/product/0000000",
"offer_clean_url": "https://www.argos.co.uk/product/0000000",
"retailer": {
"id": 28418,
"name": "Argos",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/argos.png",
"searched_retailer": false,
"retailer_domain": "www.argos.co.uk"
},
"retailer_id": 28418,
"affiliate_network_id": 152,
"api_id": 12,
"rrp": 0,
"max_price": 299.99,
"is_promoted": 0,
"image_url": "",
"category": "Headphones",
"min_price": 229.99,
"in_stock_status": true,
"affiliate_network_name": "[CSV] Commission Junction UK"
}
],
"used_offers": [],
"refurbished_offers": [],
"no_affiliate_offers": [
{
"price": 279,
"retailer": "Very.co.uk - [CSV] Awin UK"
}
],
"top_offer": {
"id": 100000001,
"price": 228.99,
"offer_url": "https://www.awin1.com/cread.php?awinmid=0000&awinaffid=[YOUR_AWIN_ID]&clickref=[CLICK_REF]&ued=https%3A%2F%2Fwww.currys.co.uk%2Fproducts%2Fsony-wh-1000xm5",
"retailer_id": 10322,
"in_stock_status": true
},
"description": "Industry-leading noise cancellation, up to 30 hours of battery life and crystal-clear hands-free calling.",
"product_codes": {
"EAN": ["4548736132580"],
"SKU": [],
"UPC": ["4548736132580"],
"MPN": ["WH1000XM5B.CE7"]
},
"category": "Headphones",
"cached": 2,
"country": "GB"
}
]
},
"request_ids": {
"4548736132580": "0123456789abcdef0123456789abcdef"
},
"message": ""
}Finds products by barcode, ASIN, SKU, product page URL or Squirrel id, and returns each product with the offers of the retailers active on your account. Send one of code, urls or squirrel_id.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| code | string | one of three | A product code: EAN, UPC, SKU or ASIN (ASINs only for accounts with Amazon). Up to 10 codes separated by |, for example 4548736132580|5060409600602. | |
| urls | string | one of three | A retailer's product page URL. Up to 10 URLs separated by |. | |
| squirrel_id | integer | one of three | The Squirrel id of a product, from an earlier answer or from your Squirrel dashboard. | |
| country | string | optional | every country | The ISO code of one country, for example GB (UK is read as GB). Without it the answer has the product in every country Squirrel covers. A list of countries, or a country Squirrel does not cover, answers 400. |
| show_with_offers_only | boolean | optional | false | true to leave out products that have no offers. |
| cache_ttl | integer | optional | 120 | How long, in minutes, an answer may come from Squirrel's cache. An older answer is still returned, and refreshed in the background for your next call. 0 skips the cache: you get the "refreshing" message and the fresh answer on a later call. |
| generate_webhook | boolean | optional | true | Whether a new request creates a webhook (when webhooks are active on your account). false does not switch off a webhook that already exists. |
| first_response_live | boolean | optional | false | true to get a quick first response instead of the "refreshing" message, on a product Squirrel has not answered for you yet. |
How the answer is built
Squirrel always answers in milliseconds, so your application never waits on a retailer.
- A product Squirrel already answered for you comes from its cache. When the cache is older than
cache_ttl, you still get it, and Squirrel refreshes it in the background for the next call. - A product that is new for you comes back as the message "We are refreshing our cache with the latest response for you.": Squirrel fetches it in the background. Ask again a minute later, use webhooks to be told when it is ready, or send
first_response_live. - An empty list
[]for a code means Squirrel has no match for it.
data has one entry per code, URL or Squirrel id you sent, under that value (a URL with https:// added when you left it out). Each entry is a list: one product per country, or the message, or [].
{
"execution_time_ms": 12,
"monthly_quota": 100000,
"current_usage": 1523,
"error": false,
"data": {
"4548736132580": [
"We are refreshing our cache with the latest response for you."
]
},
"request_ids": {
"4548736132580": "0123456789abcdef0123456789abcdef"
},
"message": ""
}First response live
On a product Squirrel has not answered for you yet, first_response_live=true returns a quick preliminary answer instead of the "refreshing" message:
- it has the product and its current offers, found in Squirrel's product search in under a second;
- its
squirrel_idis empty (null) and its offer links are Squirrel Links links; - it comes with the header
X-Response-Version: first-live 1and is not counted incurrent_usage.
Squirrel still processes the request as usual: the full answer, with the squirrel_id and your own tracking links, follows from the cache on a later call or through the webhook. Without a quick match you get the usual "refreshing" message. It answers barcodes, ASINs and product page URLs, not squirrel_id searches.
The Squirrel team can also switch it on for every call of your account: ask your account manager.
Product object
| Field | Description |
|---|---|
| name | The product name in that country. |
| squirrel_id | Squirrel's id for the product on your account: the id the Squirrel widget and plugins use. |
| price | The price of the best offer, in the country's currency (prices carry no currency: they belong to the country). |
| rrp | The recommended retail price, when known. |
| offer_url | The link of the best offer, with your affiliate tracking. |
| retailer | The retailer of the best offer. |
| image | The product image URL. |
| make | The brand. |
| model | The model. |
| last_updated | When the product's offers were last updated (YYYY-MM-DD HH:MM:SS), or null. |
| last_updated_timestamp | The same as a Unix timestamp, or null. |
| all_offers | The new-condition offers for the product in that country, cheapest first, one per retailer, 20 at most. Empty when there is none. |
| used_offers | Offers for the product used. |
| refurbished_offers | Offers for the product refurbished. |
| no_affiliate_offers | Offers of retailers you have not activated on your account (price and retailer only). Activate them in your Squirrel account to get their links. |
| top_offer | The best offer, or empty when there is none. |
| description | The product description, as a retailer gives it. |
| product_codes | The product's codes in that country: EAN, SKU, UPC and MPN lists, and ASIN for accounts with Amazon. |
| category | The Squirrel category of the product. |
| country | The ISO code of the country this product information is for. |
Offer object
| Field | Description |
|---|---|
| id | Squirrel's id for the offer. |
| price | The price, in the country's currency. |
| offer_url | The link to the offer, with your affiliate tracking. |
| offer_clean_url | The direct link to the retailer's product page, when known. |
| retailer | The retailer of the offer. |
| retailer_id | Squirrel's id for the retailer. |
| affiliate_network_id | Squirrel's id for the affiliate network. |
| affiliate_network_name | The name of the affiliate network. |
| api_id | Squirrel's id for the feed the offer came from. |
| category | The product category the retailer gives. |
| rrp | The recommended retail price the retailer gives, or 0. |
| image_url | The retailer's own image for the product, when it gives one. |
| max_price | The highest price Squirrel saw for this offer over the last 90 days. |
| min_price | The lowest price Squirrel saw for this offer over the last 90 days. |
| is_promoted | 1 when the offer is promoted on your account. |
| in_stock_status | true when the product is in stock. By default Squirrel leaves out-of-stock offers out; you can change that in your account. |
Retailer object
| Field | Description |
|---|---|
| id | Squirrel's id for the retailer. |
| name | The retailer's name. |
| logo_url | Squirrel's image of the retailer's logo. |
| searched_retailer | true when the product was searched by a URL of this retailer. |
| retailer_domain | The retailer's website. |
| lowest_commission_rate, highest_commission_rate, commission_rates | The retailer's commission rates, for accounts with retailer commissions. |
Search by name
/products/findcurl --request POST 'https://api.getsquirrel.co/api/v2/products/find' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'term=sony wh-1000xm5' \
--form 'country=GB' \
--form 'size=2'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/products/find');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'term' => 'sony wh-1000xm5',
'country' => 'GB',
'size' => '2',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/products/find', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
term: 'sony wh-1000xm5',
country: 'GB',
size: '2',
}),
});
const result = await response.json();The above command returns JSON structured like this (one product shown):
{
"execution_time_ms": 0,
"monthly_quota": 100000,
"current_usage": 1524,
"error": false,
"data": {
"term": "sony wh-1000xm5",
"country": "GB",
"search_mpn_sku": false,
"took_ms": 212,
"cached": false,
"offers_partial": false,
"notice": "",
"products": [
{
"name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"squirrel_id": null,
"price": 228.99,
"rrp": null,
"offer_url": "https://sqrlinks.ai/[YOUR_CLIENT_ID]?url=aHR0cHM6Ly93d3cuY3VycnlzLmNvLnVrL3Byb2R1Y3RzL3Nvbnktd2gtMTAwMHhtNQ%3D%3D",
"retailer": {
"id": 10322,
"name": "Currys PC World",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/currys.png",
"searched_retailer": false,
"retailer_domain": "www.currys.co.uk"
},
"image": "https://m.media-amazon.com/images/I/31SNSWUACeL._SL160_.jpg",
"make": "Sony",
"model": null,
"last_updated": null,
"last_updated_timestamp": null,
"all_offers": [
{
"id": "currys-0000000",
"price": 228.99,
"offer_url": "https://sqrlinks.ai/[YOUR_CLIENT_ID]?url=aHR0cHM6Ly93d3cuY3VycnlzLmNvLnVrL3Byb2R1Y3RzL3Nvbnktd2gtMTAwMHhtNQ%3D%3D",
"offer_clean_url": "https://www.currys.co.uk/products/sony-wh-1000xm5",
"retailer_id": 10322,
"affiliate_network_id": 117,
"affiliate_network_name": "[CSV] Awin UK",
"in_stock_status": true
}
],
"used_offers": [],
"refurbished_offers": [],
"no_affiliate_offers": [],
"description": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"product_codes": {
"EAN": ["4548736132580"],
"SKU": [],
"UPC": ["4548736132580"],
"MPN": []
},
"category": null,
"cached": 2,
"country": "GB",
"key": "4548736132580",
"score": 31.4,
"gtins": ["4548736132580"],
"from_offer": false
}
],
"timings": {
"boot_ms": 18,
"pre_search_ms": 2,
"search_ms": 96,
"offers_ms": 74,
"context_ms": 9,
"build_ms": 6,
"total_ms": 212
}
},
"request_ids": [],
"message": ""
}Searches Squirrel's product catalogue by product name and answers straight away with the products found and their current offers, best match first. A barcode, an ASIN or a product page URL in term is looked up exactly.
Unlike search by code, the answer is never deferred: it comes from Squirrel's product search, with offer links through Squirrel Links and no squirrel_id. Answers are cached for 10 minutes.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| term | string | required | What to search for: a product name such as sony wh-1000xm5, a barcode, an ASIN or a product page URL. At most 1,000 characters (the first 200 are searched). | |
| country | string | optional | GB | The ISO code of one country (UK is read as GB). |
| size | integer | optional | 10 | How many products to return, 1 to 20. |
| search_mpn_sku | boolean | optional | false | true to first look for offers whose MPN or SKU is exactly term (for a term of 64 characters or less), before the name search. |
Answer
| Field | Description |
|---|---|
| term | The term as searched (lower case, single spaces). |
| country | The country searched. |
| products | The products found: product objects plus key, score (how well it matches), gtins (its barcodes) and from_offer. Offers have the fields of the offer object; their id is a string. |
| notice | Empty, or why the answer is incomplete: "The search took too long, please try again", "We do not have this product page", "No product search index for this country yet", or how the MPN/SKU lookup went. |
| offers_partial | true when some offers could not be loaded in time. |
| cached | true when the answer came from the 10-minute cache. |
| took_ms, timings | How long the search took, in milliseconds. |
Search by text
/terms/searchcurl --request POST 'https://api.getsquirrel.co/api/v2/terms/search' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'term=Apple iPhone 16' \
--form 'country=GB'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/terms/search');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'term' => 'Apple iPhone 16',
'country' => 'GB',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/terms/search', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
term: 'Apple iPhone 16',
country: 'GB',
}),
});
const result = await response.json();The above command returns JSON structured like this (two products shown):
{
"execution_time_ms": 64,
"monthly_quota": 100000,
"current_usage": 1525,
"error": false,
"data": [
{
"name": "Apple iPhone 16 128GB - Ultramarine",
"squirrel_id": null,
"rrp": 799,
"image": "https://m.media-amazon.com/images/I/313JYjxMQ-L._SL160_.jpg",
"make": "Apple",
"model": "iPhone 16",
"used_offers": [],
"refurbished_offers": [],
"no_affiliate_offers": [],
"description": "",
"product_codes": {
"ASIN": null,
"EAN": ["0195949822445"],
"SKU": [],
"UPC": ["195949822445"],
"MPN": []
},
"category": "Mobile phones",
"country": "GB"
},
{
"name": "Apple iPhone 16 128 GB - Black",
"squirrel_id": null,
"rrp": 799,
"image": "https://m.media-amazon.com/images/I/31O+YeW1FxL._SL160_.jpg",
"make": "Apple",
"model": "iPhone 16",
"used_offers": [],
"refurbished_offers": [],
"no_affiliate_offers": [],
"description": "",
"product_codes": {
"ASIN": null,
"EAN": ["0195949821905"],
"SKU": [],
"UPC": ["195949821905"],
"MPN": []
},
"category": "Mobile phones",
"country": "GB"
}
],
"request_ids": [],
"message": ""
}Searches products by free text and returns the matching products without offers: use it to find a product's codes, then search by code for its prices. To get products and offers in one call, use search by name.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| term | string | required | The text to search for. | |
| country | string | optional | GB | The ISO code of one country. A list of countries answers 400. |
Answer
data is a list of products, with these fields of the product object: name, squirrel_id, rrp, image, make, model, description, product_codes, category and country. The offer lists are always empty here. No product found gives an empty list.
Webhooks
/webhooks/{request_id}Enabled on requestcurl 'https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef?with_data=1' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$url = 'https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef?' . http_build_query([
'with_data' => '1',
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const url = 'https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef?' + new URLSearchParams({
with_data: '1',
});
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 9,
"monthly_quota": 100000,
"current_usage": 1526,
"error": false,
"data": {
"squirrel_id": 1234567,
"request_hash": "0123456789abcdef0123456789abcdef",
"created_at": "2026-10-20 14:03:11",
"active": 1,
"last_webhook_at": "2026-10-26 08:00:05",
"content": {
"4548736132580": [
{
"name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"squirrel_id": 1234567,
"price": 228.99,
"country": "GB"
}
]
}
},
"request_ids": [],
"message": ""
}With webhooks, Squirrel sends your server the new answer of a product search whenever its product or offers change, so you don't have to ask again. They are part of the Squirrel Expert plan: ask a Squirrel team member to set them up on your account (the address they are sent to included).
Every product search creates a webhook for each code or URL, unless you send generate_webhook=false. Its id is the request_ids value of that code or URL in the search's answer. These endpoints check a webhook, and pause or restart it.
Check a webhook
GET /webhooks/{request_id}
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| request_id | string (path) | required | The webhook's id: the request_ids value from the product search. | |
| with_data | boolean | optional | Send it (any value) to get the webhook's last content in content, while Squirrel still has it cached. |
| Field | Description |
|---|---|
| squirrel_id | The Squirrel id of the product the webhook is about. |
| request_hash | The webhook's id. |
| created_at | When the webhook was created. |
| active | 1 when the webhook is active and sent on every update, 0 when it is paused. |
| last_webhook_at | When the webhook was last sent. |
| content | With with_data: the webhook's last content, the same as the product search's data. |
Pause a webhook
/webhooks/{request_id}curl --request DELETE 'https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef', {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();Pauses the webhook: data is "The selected webhook was disabled."
Restart a webhook
/webhooks/{request_id}curl --request PUT 'https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/webhooks/0123456789abcdef0123456789abcdef', {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();Makes a paused webhook active again: data is "The selected webhook was activated."
An id that does not exist answers 400, and an id of another account answers 403, both with the message "There was an error, the Squirrel team has been notified."
Alerts
/alertsEnabled on requestcurl --request POST 'https://api.getsquirrel.co/api/v2/alerts' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'squirrel_id=1234567' \
--form 'country=GB' \
--form 'alert_type_id=2' \
--form 'alert_method_id=1' \
--form 'value_type=percentage' \
--form 'alert_value=10' \
--form 'user_name=Alex Example' \
--form 'user_email=reader@example.com' \
--form 'first_email=1'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/alerts');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'squirrel_id' => '1234567',
'country' => 'GB',
'alert_type_id' => '2',
'alert_method_id' => '1',
'value_type' => 'percentage',
'alert_value' => '10',
'user_name' => 'Alex Example',
'user_email' => 'reader@example.com',
'first_email' => '1',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/alerts', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
squirrel_id: '1234567',
country: 'GB',
alert_type_id: '2',
alert_method_id: '1',
value_type: 'percentage',
alert_value: '10',
user_name: 'Alex Example',
user_email: 'reader@example.com',
first_email: '1',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 57,
"monthly_quota": 100000,
"current_usage": 1527,
"error": false,
"data": {
"id": 98765,
"name": "Alex Example",
"squirrel_id": 1234567,
"squirrel_page_url": "https://www.example.com/reviews/sony-wh-1000xm5",
"alert_type_id": 2,
"alert_method_id": 1,
"alert_method_value": "reader@example.com",
"created_at": "2026-10-26 10:15:00",
"updated_at": "2026-10-26 10:15:00",
"active": 1,
"country": "GB"
},
"request_ids": [],
"message": ""
}With Squirrel Notify, your readers can ask to be told by email when a product's price drops or when it is back in stock. The API creates, checks and stops these alerts.
Create an alert
POST /alerts
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| squirrel_id | integer | required | The Squirrel id of the product, from a product search or your Squirrel dashboard. It must be a product of your account. | |
| country | string | required | The ISO code of the country. | |
| alert_type_id | integer | required | 1 back in stock, 2 price drop. | |
| alert_value | string | required | The price drop that triggers the alert: a percentage or a price, as value_type says (for example 10). | |
| value_type | string | required | percentage: the alert fires when the price falls by alert_value percent from today's best price. price: when it falls to alert_value or below. | |
| retailer_id | integer | optional | any retailer | Follow one retailer's offer only. It must sell the product in that country. |
| alert_method_id | integer | optional | How the alert is sent: 1 email, 2 webhook. | |
| user_name | string | optional | The name of the person the alert is for. | |
| user_email | string | optional | The email address the alert is sent to. | |
| first_email | integer | optional | 0 | 1 to send the person an email confirming the alert. It is always sent when alert_method_id is given. |
A price-drop alert needs at least one active offer for the product in that country.
Alert object
| Field | Description |
|---|---|
| id | The alert's id: use it to check or stop the alert. |
| name | The name of the person the alert is for. |
| squirrel_id | The product the alert follows. |
| squirrel_page_url | The page the alert was asked from (the Referer of the call), when known. |
| country | The ISO code of the alert's country (on creation). |
| alert_type_id | 1 back in stock, 2 price drop. |
| alert_method_id | 1 email, 2 webhook. |
| alert_method_value | The email address the alert is sent to. |
| created_at | When the alert was created. |
| updated_at | When the alert last changed. |
| active | 1 active, 0 stopped. |
| retailer | The retailer the alert follows, when it follows one (id, name, logo_url). |
Check an alert
/alerts/{alert_id}curl 'https://api.getsquirrel.co/api/v2/alerts/98765' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/alerts/98765');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/alerts/98765', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();Returns the alert object in data.
Stop an alert
/alerts/{alert_id}curl --request DELETE 'https://api.getsquirrel.co/api/v2/alerts/98765' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/alerts/98765');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/alerts/98765', {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();Stops the alert: data is "The selected alert was disabled."
Errors. A missing parameter, a product that is not yours, a country Squirrel does not cover, a retailer that does not sell the product or a product with no active offers answers 400, with the reason in data, for example ["Missing parameter alert_value"] or ["Invalid retailer for this squirrel"].
Retailers
/retailers/listcurl 'https://api.getsquirrel.co/api/v2/retailers/list' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/retailers/list');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/retailers/list', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 22,
"monthly_quota": 100000,
"current_usage": 1528,
"error": false,
"data": [
{
"id": 56,
"name": "Amazon UK",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/amazon-uk-compressor.png",
"retailer_domain": "www.amazon.co.uk",
"affiliate_network_id": 12,
"affiliate_network_name": "Amazon UK",
"country": "UK",
"relationship": "Direct",
"lowest_commission_rate": null,
"highest_commission_rate": null,
"commission_rates": []
},
{
"id": 28418,
"name": "Argos",
"logo_url": "https://squirrels-live.getsquirrel.co/assets/images/retailers/argos.png",
"retailer_domain": "www.argos.co.uk",
"affiliate_network_id": 152,
"affiliate_network_name": "[CSV] Commission Junction UK",
"country": "UK",
"relationship": "Direct",
"lowest_commission_rate": null,
"highest_commission_rate": null,
"commission_rates": []
}
],
"request_ids": [],
"message": ""
}Lists the retailers active on your account, by name. No parameters.
| Field | Description |
|---|---|
| id | Squirrel's id for the retailer. |
| name | The retailer's name. |
| logo_url | Squirrel's image of the retailer's logo. |
| retailer_domain | The retailer's website. |
| affiliate_network_id | Squirrel's id for the affiliate network you work with this retailer through. |
| affiliate_network_name | The affiliate network's name. |
| country | The ISO code of the affiliate network's country. |
| relationship | Direct, or the name of the clean-links account the retailer is linked through. |
| lowest_commission_rate, highest_commission_rate, commission_rates | Kept for compatibility: always empty here. |
Countries
/utils/countriescurl 'https://api.getsquirrel.co/api/v2/utils/countries' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/utils/countries');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/utils/countries', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 4,
"monthly_quota": 100000,
"current_usage": 1529,
"error": false,
"data": [
{ "id": 1, "iso": "GB", "name": "United Kingdom" },
{ "id": 2, "iso": "US", "name": "United States" },
{ "id": 13, "iso": "AU", "name": "Australia" },
{ "id": 38, "iso": "CA", "name": "Canada" },
{ "id": 73, "iso": "FR", "name": "France" },
{ "id": 80, "iso": "DE", "name": "Germany" },
{ "id": 99, "iso": "IN", "name": "India" },
{ "id": 105, "iso": "IT", "name": "Italy" },
{ "id": 150, "iso": "NL", "name": "Netherlands" },
{ "id": 199, "iso": "ES", "name": "Spain" },
{ "id": 205, "iso": "SE", "name": "Sweden" }
],
"request_ids": [],
"message": ""
}Lists the countries Squirrel covers: the values country takes in the other endpoints. No parameters. Any key can call it, including keys limited to statistics.
| Field | Description |
|---|---|
| id | Squirrel's id for the country. |
| iso | The country's ISO code. |
| name | The country's name. |
Statistics
Impressions
/stats/impressionscurl --request POST 'https://api.getsquirrel.co/api/v2/stats/impressions' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'start_date=2026-10-01' \
--form 'end_date=2026-10-25' \
--form 'page=1' \
--form 'per_page=3'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/stats/impressions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'start_date' => '2026-10-01',
'end_date' => '2026-10-25',
'page' => '1',
'per_page' => '3',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/stats/impressions', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
start_date: '2026-10-01',
end_date: '2026-10-25',
page: '1',
per_page: '3',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 88,
"monthly_quota": 100000,
"current_usage": 1530,
"error": false,
"data": {
"total_results": 17969,
"page": 1,
"total_pages": 5990,
"stats": [
{
"date": "2026-10-01",
"count": "4",
"squirrel_id": "1234567",
"url": "https://www.example.com/reviews/sony-wh-1000xm5",
"render_type": ""
},
{
"date": "2026-10-01",
"count": "1",
"squirrel_id": "1234568",
"url": "https://www.example.com/best/headphones",
"render_type": "tbc"
},
{
"date": "2026-10-01",
"count": "2",
"squirrel_id": "1234569",
"url": "https://www.example.com/",
"render_type": ""
}
]
},
"request_ids": [],
"message": ""
}The raw impressions of the Squirrel widgets on your account, per day, product and page.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| start_date | date | required | The first day, YYYY-MM-DD. | |
| end_date | date | required | The last day, YYYY-MM-DD. It must be yesterday or earlier, not before start_date, and at most 31 days after it. | |
| page | integer | optional | 1 | The page to return, from 1 to total_pages. |
| per_page | integer | optional | 1000 | Rows per page, at most 1,000. |
Answer
| Field | Description |
|---|---|
| total_results | The number of rows for the dates. |
| page | The page returned. |
| total_pages | The number of pages at this per_page. |
| stats | The rows. |
Each row:
| Field | Description |
|---|---|
| date | The day, YYYY-MM-DD. |
| count | How many impressions. |
| squirrel_id | The product's Squirrel id. |
| url | The page the widget was on, as the browser reported it. Some pages only give your domain (AMP pages, for example). |
| render_type | The kind of impression: empty for a normal one, tbc when the widget had no offers to show. |
Clicks
/stats/clickscurl --request POST 'https://api.getsquirrel.co/api/v2/stats/clicks' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'start_date=2026-10-01' \
--form 'end_date=2026-10-25' \
--form 'page=1' \
--form 'per_page=2'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/stats/clicks');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'start_date' => '2026-10-01',
'end_date' => '2026-10-25',
'page' => '1',
'per_page' => '2',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/stats/clicks', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
start_date: '2026-10-01',
end_date: '2026-10-25',
page: '1',
per_page: '2',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 75,
"monthly_quota": 100000,
"current_usage": 1531,
"error": false,
"data": {
"total_results": 7,
"page": 1,
"total_pages": 4,
"stats": [
{
"date": "2026-10-05",
"count": "1",
"squirrel_id": "1234567",
"url": "https://www.example.com/reviews/sony-wh-1000xm5",
"offer_type": "default",
"retailer": "Currys PC World"
},
{
"date": "2026-10-05",
"count": "2",
"squirrel_id": "1234568",
"url": "https://www.example.com/best/headphones",
"offer_type": "default",
"retailer": "Argos"
}
]
},
"request_ids": [],
"message": ""
}The raw clicks on the offers of the Squirrel widgets on your account, per day, product, page and retailer. The parameters, limits and answer are the same as for impressions, with these fields in each row instead of render_type:
| Field | Description |
|---|---|
| offer_type | The kind of offer clicked: default a retailer's offer, tbc a "Check price here" offer, tds a mobile deal (TDS plugin). |
| retailer | The name of the retailer clicked. |
Errors. Missing or invalid dates, a range over 31 days, an end_date after yesterday or a per_page over 1,000 answer 400, with the reason in data, for example ["The following errors occurred: The interval between start and end dates cannot exceed 31 days"].
Insights
Transactions
/insights/transactionscurl --request POST 'https://api.getsquirrel.co/api/v2/insights/transactions' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'start_date=2026-10-01' \
--form 'end_date=2026-10-25' \
--form 'limit=1' \
--form 'offset=1'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/insights/transactions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'start_date' => '2026-10-01',
'end_date' => '2026-10-25',
'limit' => '1',
'offset' => '1',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/insights/transactions', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
start_date: '2026-10-01',
end_date: '2026-10-25',
limit: '1',
offset: '1',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"execution_time_ms": 0,
"monthly_quota": 100000,
"current_usage": 1532,
"returned_results": 1,
"total_results": 1582,
"error": false,
"data": [
{
"id": 5550001,
"anchor": "https://www.currys.co.uk/products/sony-wh-1000xm5",
"country_id": "UK",
"squirrel_id": 1234567,
"commissions_gross": "6.87",
"commissions_net": "6.87",
"content_type": "reviews",
"merchant_name": "Currys PC World",
"network_name": "Awin",
"order_id": "[ORDER_ID]",
"page": "https://www.example.com/reviews/sony-wh-1000xm5",
"platform": "mobile",
"product_name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"productprice": "228.99",
"referrer_url": "https://www.google.com/",
"sales_gross": "228.99",
"sales_net": "228.99",
"sub_vertical": null,
"transaction_full_date": "2026-10-14 18:22:05",
"transaction_status": "Pending",
"utm_campaign": null,
"utm_content": null,
"utm_medium": "organic",
"utm_source": "google",
"utm_term": null,
"vertical": "Audio",
"updated_at": "2026-10-15 06:01:21",
"is_member": 0,
"is_bounty": 0
}
],
"message": ""
}The transactions (sales) collected on your Squirrel Insights account.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| start_date | date | required | The start, YYYY-MM-DD or YYYY-MM-DD HH:MM:SS. Without a time, 00:00:00. | |
| end_date | date | required | The end, in the same formats. Without a time, 23:59:59. Not before start_date. | |
| limit | integer | optional | 100 | Transactions per page, 1 to 100. |
| offset | integer | optional | 1 | The page number, starting at 1 (not a row offset): page 2 with limit=100 returns rows 101 to 200. |
| show_publisher_stats | boolean | optional | 0 | 1 to include the transactions of every website of your account group, not only this key's. |
Answer
| Field | Description |
|---|---|
| returned_results | The number of transactions in this page. |
| total_results | The number of transactions for the dates. |
| data | The transactions. |
Each transaction:
| Field | Description |
|---|---|
| id | Squirrel's id for the transaction. |
| anchor | The link that was clicked. |
| country_id | The country of the sale. |
| squirrel_id | The product's Squirrel id, when the click came from a Squirrel widget. |
| commissions_gross, commissions_net | The commission, before and after deductions. |
| sales_gross, sales_net | The sale amount, before and after deductions. |
| productprice | The price of the product sold. |
| content_type | The kind of page the click came from (for example reviews). |
| merchant_name | The retailer. |
| network_name | The affiliate network. |
| order_id | The retailer's order reference. |
| page | The page the click came from. |
| platform | The reader's device, for example mobile. |
| product_name | The product sold. |
| referrer_url | Where the reader came to your page from. |
| transaction_full_date | When the sale happened. |
| transaction_status | The network's status: for example Pending, Approved, Rejected. |
| utm_campaign, utm_content, utm_medium, utm_source, utm_term | The UTM values of the visit. |
| vertical, sub_vertical | The page's vertical and sub-vertical. |
| updated_at | When the transaction last changed. |
| is_member | 1 when the reader was a member of your site. |
| is_bounty | 1 for a bounty transaction. |
Errors. Missing or invalid dates, or a start_date after end_date, answer with HTTP status 399 and the reason in message, for example "start_date and end_date are required". A key with no Insights account answers 399 with "You do not have access to insights".
CPC campaigns
/campaignsEnabled on requestcurl 'https://api.getsquirrel.co/api/v2/campaigns?status=active' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$url = 'https://api.getsquirrel.co/api/v2/campaigns?' . http_build_query([
'status' => 'active',
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const url = 'https://api.getsquirrel.co/api/v2/campaigns?' + new URLSearchParams({
status: 'active',
});
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this (one campaign shown):
{
"error": false,
"data": {
"campaigns": [
{
"id": 321,
"name": "Autumn headphones push",
"status": "active",
"source": "squirrel",
"commerce_type": "cpc",
"start_at": "2026-10-01 00:00:00",
"end_at": "2026-10-31 23:59:59",
"timezone": "UTC",
"currency": "GBP",
"cpc_rate": 0.35,
"budget": 3500,
"target_clicks": 10000,
"clicks_delivered": 6420,
"clicks_remaining": 3580,
"partner_clicks": 6388,
"revenue_delivered": 2247,
"revenue_remaining": 1253,
"orders": 41,
"sales_value": 9388.6,
"commission_pending": 281.66,
"commission_approved": 120.4,
"pace_status": "behind",
"required_daily": 325.45,
"projected_total": 10120,
"utm_campaign": "cmp_321"
}
]
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1533
}CPC campaigns are the pay-per-click campaigns your account runs with Squirrel. They are created in the Squirrel dashboard; the API reads them and their daily figures, receives the clicks your partners counted, and creates tracking links.
Every campaign call answers 404 ("Resource not found...") for a campaign that does not exist or is not yours. Invalid input answers with HTTP status 399 and the reason in message.
List campaigns
GET /campaigns
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| status | string | optional | every status but archived | Only the campaigns with this status, for example active or archived. |
Returns your campaigns, newest first, 200 at most, in data.campaigns:
| Field | Description |
|---|---|
| id | The campaign's id. |
| name | The campaign's name. |
| status | The campaign's status. |
| source | Where the campaign runs. |
| commerce_type | The kind of campaign. |
| start_at, end_at | When the campaign starts and ends. |
| timezone | The campaign's time zone (UTC by default). |
| currency | The currency of every amount. |
| cpc_rate | The price of one click. |
| budget | The budget: the one set, or target_clicks × cpc_rate. |
| target_clicks | The clicks to deliver. |
| clicks_delivered | The billable clicks delivered so far. |
| clicks_remaining | The clicks still to deliver. |
| partner_clicks | The clicks your partners reported. |
| revenue_delivered, revenue_remaining | The click revenue delivered so far, and still to deliver. |
| orders, sales_value | The orders and sales the campaign brought. |
| commission_pending, commission_approved | The commission on those sales, pending and approved. |
| pace_status | How the campaign is pacing against its target: ahead, on_track, behind, at_risk or done; null until it is computed or when the campaign has no target. |
| required_daily, projected_total | The clicks a day it needs to reach its target, and the clicks it is projected to reach. |
| utm_campaign | The utm_campaign value of the campaign's links. |
Campaign details
/campaigns/{id}curl 'https://api.getsquirrel.co/api/v2/campaigns/321' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/campaigns/321');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/campaigns/321', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns the campaign's fields and these (shortened):
{
"error": false,
"data": {
"id": 321,
"name": "Autumn headphones push",
"status": "active",
"destination": {
"version": 2,
"primary_url": "https://www.example-retailer.com/headphones",
"overflow_url": "https://www.example-retailer.com/audio",
"since": "2026-10-10 09:00:00"
},
"placements": [
{
"id": 77,
"platform": "newsletter",
"label": "Newsletter",
"active": true,
"link": "https://sqrlinks.ai/[YOUR_CLIENT_ID]/c/autumn-headphones-newsletter-a1b2?utm_source=newsletter&utm_medium=email&utm_campaign=cmp_321"
}
]
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1534
}The campaign, with the fields of the list and:
| Field | Description |
|---|---|
| destination | Where the campaign's clicks go: primary_url, overflow_url (once the target is reached), the rule's version and since when it applies; null when none is set. |
| placements | The campaign's placements: id, platform, label, active and its tracking link (null for campaigns that do not use Squirrel links). |
Daily stats
/campaigns/{id}/statscurl 'https://api.getsquirrel.co/api/v2/campaigns/321/stats?start_date=2026-10-20&end_date=2026-10-25' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$url = 'https://api.getsquirrel.co/api/v2/campaigns/321/stats?' . http_build_query([
'start_date' => '2026-10-20',
'end_date' => '2026-10-25',
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const url = 'https://api.getsquirrel.co/api/v2/campaigns/321/stats?' + new URLSearchParams({
start_date: '2026-10-20',
end_date: '2026-10-25',
});
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this (one day shown):
{
"error": false,
"data": {
"campaign_id": 321,
"start_date": "2026-10-20",
"end_date": "2026-10-25",
"currency": "GBP",
"days": [
{
"date": "2026-10-20",
"placement_id": 0,
"squirrel_clicks": 312,
"billable_clicks": 298,
"bonus_clicks": 6,
"filtered_clicks": 8,
"partner_clicks": 301,
"impressions": 15420,
"pageviews": 9812,
"cpc_revenue": 104.3,
"orders": 2,
"sales_value": 458.98,
"commission_pending": 13.77,
"commission_approved": 0,
"commission_rejected": 0
}
]
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1535
}The campaign's figures per day. Each day has a row with placement_id 0 for the whole campaign, and one row per placement.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| start_date | date | optional | 30 days ago | The first day, YYYY-MM-DD. |
| end_date | date | optional | today | The last day, YYYY-MM-DD, not before start_date. |
| Field | Description |
|---|---|
| squirrel_clicks | The clicks Squirrel counted. |
| billable_clicks | The clicks billed. |
| bonus_clicks | Overflow and over-delivery clicks, beyond the billable ones. |
| filtered_clicks | Clicks filtered out as not valid. |
| partner_clicks | The clicks your partners reported for the day. |
| impressions, pageviews | The impressions and page views reported. |
| cpc_revenue | The click revenue of the day. |
| orders, sales_value | The orders and sales of the day. |
| commission_pending, commission_approved, commission_rejected | The commission on those sales, by status. |
Partner clicks
/campaigns/{id}/partner-clickscurl 'https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks?start_date=2026-10-20&end_date=2026-10-25' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$url = 'https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks?' . http_build_query([
'start_date' => '2026-10-20',
'end_date' => '2026-10-25',
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const url = 'https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks?' + new URLSearchParams({
start_date: '2026-10-20',
end_date: '2026-10-25',
});
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this:
{
"error": false,
"data": {
"campaign_id": 321,
"rows": [
{
"date": "2026-10-20",
"placement_id": 77,
"source": "api",
"clicks": 301,
"impressions": 15420,
"orders": null,
"revenue": null,
"imported_at": "2026-10-21 07:00:12"
}
]
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1536
}The clicks your partners reported for the campaign, per day and placement, with where each row came from (source) and when it was received (imported_at). It takes the same start_date and end_date as daily stats.
Push partner clicks
/campaigns/{id}/partner-clicksRequires the API links featurecurl --request POST 'https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--header 'Content-Type: application/json' \
--data '{"rows": [{"date": "2026-10-25", "placement_id": 77, "clicks": 288, "impressions": 14980, "ref": "daily-report-2026-10-25"}]}'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'rows' => [
[
'date' => '2026-10-25',
'placement_id' => 77,
'clicks' => 288,
'impressions' => 14980,
'ref' => 'daily-report-2026-10-25',
],
],
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/campaigns/321/partner-clicks', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
'Content-Type': 'application/json',
},
body: JSON.stringify({
rows: [
{
date: '2026-10-25',
placement_id: 77,
clicks: 288,
impressions: 14980,
ref: 'daily-report-2026-10-25',
},
],
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"error": false,
"data": {
"saved": 1,
"errors": []
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1537
}Sends the clicks your partner counted, one row per day and placement. A row for a day and placement already sent replaces it.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| rows | array | required | The rows, 366 at most per call (any beyond are ignored). | |
| rows[].date | date | required | The day, YYYY-MM-DD. | |
| rows[].clicks | integer | required | The clicks of the day. | |
| rows[].placement_id | integer | optional | 0 | The placement, from campaign details; 0 for the whole campaign. |
| rows[].impressions | integer | optional | The impressions of the day. | |
| rows[].orders | integer | optional | The orders of the day. | |
| rows[].revenue | number | optional | The revenue of the day. | |
| rows[].ref | string | optional | Your own reference for the row (180 characters at most). |
The answer gives the number of rows saved, and in errors each row that was not, for example "row 2: unknown placement_id". A bad row does not stop the others.
Create a tracking link
/campaigns/{id}/linksRequires the API links featurecurl --request POST 'https://api.getsquirrel.co/api/v2/campaigns/321/links' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'platform=newsletter' \
--form 'label=October newsletter'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/campaigns/321/links');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'platform' => 'newsletter',
'label' => 'October newsletter',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/campaigns/321/links', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
platform: 'newsletter',
label: 'October newsletter',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"error": false,
"data": {
"placement_id": 78,
"platform": "newsletter",
"label": "October newsletter",
"link": "https://sqrlinks.ai/[YOUR_CLIENT_ID]/c/autumn-headphones-newsletter-c3d4?utm_source=newsletter&utm_medium=email&utm_campaign=cmp_321"
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1538
}Creates a new placement for the campaign, with its own tracking link.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| platform | string | optional | other | Where the link will be used: onsite, facebook, instagram, tiktok, x, apple_news, newsletter, app or other. |
| label | string | optional | the platform's name | A name for the placement (180 characters at most). |
CSV Master
/csv-master/list-filesEnabled on requestcurl 'https://api.getsquirrel.co/api/v2/csv-master/list-files' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/csv-master/list-files');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/csv-master/list-files', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
});
const result = await response.json();The above command returns JSON structured like this:
{
"error": false,
"data": [
{
"id": 12,
"csv_master_id": 3,
"master_name": "My Product Feed",
"file_type": "master",
"created_at": "2026-10-25 06:00:00",
"last_download": "2026-10-25 10:15:42"
},
{
"id": 13,
"csv_master_id": 3,
"master_name": "My Product Feed",
"file_type": "diff",
"created_at": "2026-10-26 06:00:00",
"last_download": null
}
],
"message": "",
"monthly_quota": 100000,
"current_usage": 1539
}CSV Master lets you download the CSV export files prepared for your account. There are two types of files:
- master: a full export of your product catalogue;
- diff: only the changes since the last master file.
Files are generated on a schedule the Squirrel team sets, and can be downloaded once ready, in two steps:
- Call List files to see which files are available.
- Call Download file with a
file_idto get a download link valid for 15 minutes. - Open the link (a
GETthat follows redirects): it sends you to the file.
The link is signed: it does not show where the file is stored, and it stops working when it expires or if it is changed.
List files
GET /csv-master/list-files
Returns the files you can download, newest first. No parameters.
| Field | Description |
|---|---|
| id | The file's id: the file_id of Download file. |
| csv_master_id | The id of the CSV Master feed the file belongs to. |
| master_name | The feed's name. |
| file_type | master (full export) or diff (changes only). |
| created_at | When the file was generated. |
| last_download | When your account last downloaded it, or null. |
Download file
/csv-master/download-filecurl --request POST 'https://api.getsquirrel.co/api/v2/csv-master/download-file' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
--form 'file_id=12'<?php
$token = '[YOUR_ACCESS_TOKEN]';
$ch = curl_init('https://api.getsquirrel.co/api/v2/csv-master/download-file');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'file_id' => '12',
]),
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://api.getsquirrel.co/api/v2/csv-master/download-file', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer [YOUR_ACCESS_TOKEN]',
},
body: new URLSearchParams({
file_id: '12',
}),
});
const result = await response.json();The above command returns JSON structured like this:
{
"error": false,
"data": {
"url": "https://api.getsquirrel.co/api/v2/csv-master/fetch?token=[SIGNED_TOKEN]",
"expires_in_minutes": 15
},
"message": "",
"monthly_quota": 100000,
"current_usage": 1540
}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| file_id | integer | required | The id of the file, from List files. |
| Field | Description |
|---|---|
| url | The download link. Open it with a GET that follows redirects; it needs no API key. |
| expires_in_minutes | How long the link works: 15. |
| Status | Meaning |
|---|---|
| 400 | file_id is missing. |
| 403 | The file is not one of your account's files, or your account has no CSV Master. |
| 404 | The file does not exist or is not ready. |
| 500 | The link could not be made: try again or contact support. |
An expired or changed download link answers 400 when you open it.
AI assistants (MCP)
Squirrel runs two Model Context Protocol (MCP) servers, so AI assistants such as Claude and ChatGPT can search products and prices with Squirrel. Both are read-only and use MCP's Streamable HTTP transport.
Product search MCP
https://mcp.getsquirrel.co/mcpcurl 'https://mcp.getsquirrel.co/mcp' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_products","arguments":{"query":"sony wh-1000xm5","country":"GB","max_products":1,"max_offers":3}}}'The tool's structured answer looks like this (shortened):
{
"query": "sony wh-1000xm5",
"country": "GB",
"currency": "GBP",
"products": [
{
"name": "Sony WH-1000XM5 Premium Wireless Noise Cancelling Headphones",
"brand": "Sony",
"image": "https://m.media-amazon.com/images/I/31SNSWUACeL._SL160_.jpg",
"barcodes": ["4548736132580"],
"offers": [
{ "retailer": "Currys Business", "price": 228.99, "currency": "GBP", "in_stock": true, "link": "https://sqrlinks.ai/27/l/[CODE]/[TAG]" },
{ "retailer": "Currys", "price": 229, "currency": "GBP", "in_stock": true, "link": "https://sqrlinks.ai/27/l/[CODE]/[TAG]" },
{ "retailer": "Argos", "price": 229.99, "currency": "GBP", "in_stock": true, "link": "https://sqrlinks.ai/27/l/[CODE]/[TAG]" }
]
}
],
"notice": null,
"offers_incomplete": false
}A public MCP server that anyone can add to an AI assistant, with no account or key. It has one tool, search_products: find products by name, barcode, ASIN, model number or product page URL, and get each product's current offers in one country, cheapest first, in stock first, each with a link to the retailer.
Address: https://mcp.getsquirrel.co/mcp
Add it to an assistant: in Claude, open Settings → Connectors → Add custom connector and paste the address; in ChatGPT, add it as a connector (developer mode). Any MCP client that supports remote servers over Streamable HTTP can use it.
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| query | string | required | A product name, barcode, ASIN, model number or product page URL; 1,000 characters at most. | |
| country | string | optional | GB | The shopper's country: GB, US, AU or CA. |
| max_products | integer | optional | 5 | Products to return, 1 to 10. |
| max_offers | integer | optional | 5 | New-condition offers per product, 1 to 20. |
Limits: 20 searches a minute from one address, and 120 a minute in total; above that the tool answers "Too many searches from this address: at most 20 a minute." or asks to try again in a minute.
Publishers MCP
https://mcp.getsquirrel.co/mcp/publisherEnabled on requestThe Publishers MCP connects an AI assistant to your Squirrel account, so you can ask it about your own websites in plain words: "How did my commission do last week compared with the week before?", "Which retailers got the most clicks on my site this month?"
Address: https://mcp.getsquirrel.co/mcp/publisher
Connect: add the address as a custom connector in your assistant. The first time, the assistant opens a Squirrel sign-in page: sign in with your Squirrel dashboard account and approve the assistant. You can disconnect it from the AI assistants section of your dashboard's Integration pages. It does not use your API key.
What the assistant can do, for the websites your Squirrel user can see:
| Tools | What they give |
|---|---|
list_my_websites, get_help | Your websites, and what the assistant can ask. |
search_products | The product search above, with the links of your website. |
insights_summary, insights_trend, insights_breakdown, insights_top, insights_compare | Your Insights figures (clicks, commission, revenue, sales, orders and your Google Analytics traffic), totals, trends by day, week or month, breakdowns by retailer, network, country, page, product, author, link or UTM, top lists and period comparisons. Data until yesterday, up to two years back. |
offers_overview, offers_widgets, offers_retailers, offers_traffic, offers_squirrelizers, offers_playlists, offers_marketplace_revenue | Your Squirrel widgets' impressions and clicks by widget, retailer, page and day, and your marketplace revenue by month. Each needs the matching permission on your Squirrel user. |
Every tool only reads: the assistant cannot change anything on your account. Calls are limited to 60 a minute per user (10 product searches a minute).
Changelog
What changed in the API and in this reference, newest first. Changes to the API are added here when they ship.
2026-10-26
New in the reference
- Search by name (
POST /products/find): products and their offers from a product name, in one call. - First response live (
first_response_live): a quick first answer on a product search instead of "We are refreshing our cache". - CPC campaigns: list campaigns, their details and daily stats, read and push partner clicks, create tracking links.
- AI assistants (MCP): the public product search MCP and the Publishers MCP.
- A Quickstart, and PHP and JavaScript samples next to curl for every endpoint.
Corrected
- The rate limit is a number of calls per minute shared by your account group, not one call a second. See Limits and quotas.
countryon a product search takes one country; a list of countries answers400.cache_ttlis in minutes.- The clicks endpoint is
POST /stats/clicks(the reference showed the impressions address). - Statistics:
end_datemust be yesterday or earlier. - Insights:
offsetis a page number starting at 1. - Alerts:
alert_method_valueis the address the alert is sent to, and an alert'sidis the alert's own id. - Text search: the example shows the real answer, with its wrapper, and
GBas the default country. - Errors: the statuses the API really gives,
399and403included.
Removed
- A deprecated product search parameter that no longer had an effect.
- The old copy of this reference at
/old_index.html.