Google Shopping API: Product Prices and Offers as JSON
To get Google Shopping prices as JSON, send one GET request to /v1/google-shopping/search with a search query in q. Each result comes back with a title, a seller, a price as text, the same price as a number, a rating and a product ID. A successful call costs 1 credit, and a failed call costs nothing.
This guide covers the request, the parameters, a recorded response and what a daily price check for 500 products costs in credits.
Is there an official Google Shopping API for price research?
Google's official option is the Merchant API, and it serves sellers. Google describes it as a way to manage how your business and products appear on Google. That is a job for the seller who owns the products, not a search over what other sellers charge.
If you sell on Google and want to update your own feed, use the Merchant API; it fits that job better than anything here. If you want to know what a product costs across retailers, you need search results, which is what the endpoint below returns. Perceptlayer collects the data from publicly available sources and returns it as found at the time of the call, so a price is a snapshot, not a guarantee.
How do you request product prices as JSON?
You make a GET request with a search query and your key in the X-API-Key header. The only required parameter is q. Adding gl (country) and hl (language) keeps results consistent from one run to the next, which matters when you compare prices over time.
curl "https://api.perceptlayer.com/v1/google-shopping/search?q=iphone+15&gl=us&hl=en" \
-H "X-API-Key: $API_KEY"
Keys start with sk_live_. If you don't have one, get a free API key; the quick start takes you to a first call, and authentication covers the header and the api_key query parameter for places where headers aren't possible. You can also run the call from the playground on the Google Shopping API page before writing any code.
Which parameters does the search endpoint take?
The search endpoint takes one required parameter, q, and eight optional ones: gl, hl, location, uule, start, device, small_business and shoprs. Types and limits below are as documented for /v1/google-shopping/search. Nothing else about defaults or page sizes is documented, so test before you depend on them.
| Parameter | Type | Required | Notes |
|---|---|---|---|
q |
string | yes | The search query, up to 500 characters |
gl |
string | no | Country code, up to 2 characters |
hl |
string | no | Language code, up to 2 characters |
location |
string | no | Search location, up to 63 characters |
uule |
string | no | Encoded location, up to 500 characters |
start |
integer | no | Offset for the next page of results |
device |
string | no | One of desktop, mobile, tablet |
small_business |
boolean | no | Small-business filter |
shoprs |
string | no | Filter token, up to 2000 characters |
Prices differ by country, so set gl and hl deliberately and keep them the same across runs. Use location or uule when you need a more specific place than a country. The recorded response below echoes the parameters it ran with in search_parameters, including device, which is worth logging next to every price you store.
What does the response look like?
The response has three parts: search_parameters, which echoes the request; a shopping_results array with one object per product listing; and a pagination object that points to the next page. This is an excerpt of a recorded response for the request above, trimmed with ….
{
"search_parameters": {
"q": "iphone 15",
"gl": "us",
"hl": "en",
"start": 0,
"device": "desktop"
},
"shopping_results": [
{
"position": 1,
"category": "Popular products",
"title": "Apple iPhone 15",
"source": "Walmart - Wireless Source",
"rating": 4.7,
"reviews": 45000,
"product_id": "4263606017411565992",
"link": "https://www.google.com/shopping/product/4263606017411565992",
"price": "$393.73",
"extracted_price": 393.73,
…
},
{
"position": 2,
"title": "Apple iPhone 15 128GB",
"source": "Best Buy",
"rating": 4.4,
"reviews": 94,
"product_id": "4379032623638529631",
"price": "$441.00",
"extracted_price": 441,
…
}
],
"pagination": {
"current_page": 1,
"start": 0,
"next_start": 10
}
}
The fields a price pipeline uses are these:
| Field | What it holds |
|---|---|
title |
The product name as listed |
source |
The seller the result points to |
price |
The price as display text, with currency symbol |
extracted_price |
The same price as a number |
product_id |
An ID for each product, usable as a key to store it under |
rating, reviews |
Star rating and review count |
link |
The Google Shopping product page |
The full results also carry product_token, thumbnail, extensions and rank_absolute; they're cut here for length. Store extracted_price for arithmetic and keep price only if you need the display string. Note that the two results above come from different sellers, so a price in one row isn't the price of the same offer in the next.
How do you track prices for 500 products every day?
The endpoint answers a search; it doesn't track anything on its own. A price tracker needs a product list, a daily loop, a store for history and a retry rule around it. Run one search per product, pick the matching result, and write product_id, source, extracted_price and the date to a table.
This script does that for a list of queries. It keeps the first result as the match, which is a shortcut: for products with common names, match on title or a stored product_id instead.
import os
import time
import requests
API = "https://api.perceptlayer.com/v1/google-shopping/search"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}
def fetch(query, tries=3):
params = {"q": query, "gl": "us", "hl": "en"}
for _ in range(tries):
r = requests.get(API, params=params, headers=HEADERS, timeout=30)
if r.status_code == 200:
return r.json()
if r.status_code in (429, 503):
time.sleep(int(r.headers.get("Retry-After", "5")))
continue
r.raise_for_status()
return None
def daily_prices(queries):
rows = []
for q in queries:
data = fetch(q)
results = (data or {}).get("shopping_results", [])
if not results:
continue
top = results[0]
rows.append((q, top["product_id"], top["source"], top["extracted_price"]))
return rows
The requests library handles the HTTP. Rate-limited (429) and temporarily unavailable (503) responses carry a Retry-After header with the seconds to wait, and the loop honours it. There is no idempotency key, so a call that timed out on your side may still have been billed; retrying without paying twice covers how to handle that. For results beyond the first page, see paginating a web data API safely, using next_start from pagination as the next start.
What does tracking 500 products a day cost?
Tracking 500 products once a day costs 500 credits a day, because each product is one search at 1 credit. Over 30 days that is 500 × 30 = 15,000 credits. The arithmetic scales linearly, so doubling the list or checking twice a day doubles the credits.
| Plan of calls | Calls | Credits |
|---|---|---|
| 500 products, once a day, 1 day | 500 | 500 |
| 500 products, once a day, 30 days | 15,000 | 15,000 |
| 500 products, once a day, 365 days | 182,500 | 182,500 |
| 500 products, twice a day, 30 days | 30,000 | 30,000 |
The 1,000 free credits every month cover two daily runs of 500 products. A month of daily checks, 15,000 credits, is more than the 10,000-credit Starter pack and fits inside the 50,000-credit Build pack with 35,000 left over. A full year at 182,500 credits fits inside the 200,000-credit Growth pack. Purchased credits last 12 months, there's no subscription, and pricing shows what credits cost. The credits docs explain how debits work; failed calls return X-Credits-Used: 0.
Rate limits are per account. Free accounts get 1 request a second, so 500 sequential calls take roughly 500 seconds. Paying accounts run at 5 requests a second at least, which brings the same loop to about 100 seconds. Details are in the rate limits docs.
What should you check before trusting a price?
Treat each stored price as an observation: this query, this country, this device, this moment. It isn't a fixed fact about the product, because sellers, variants and countries change what a search returns. Three habits make the stored history usable for comparison over time.
- Log
search_parameterswith every row, so a change inglordeviceisn't mistaken for a price move. - Compare on
product_idandsource, not on rank. Position changes without any price change. - Alert on large jumps instead of acting on them. A result can be a different variant, so check
titlewhen a price moves sharply.
Errors arrive as an HTTP status with a JSON body holding code, message and request_id. Handle invalid_parameters (422) by fixing the request, and insufficient_credits (402) by topping up, since nothing else is debited when the balance reaches zero. The errors docs list every code.
If you're building an agent rather than a pipeline, the same endpoint is available as a tool through the MCP server; see the AI agents docs. For sibling Google endpoints, the guides to Google Hotels prices and Google Flights fares follow the same pattern. Next step: run the iPhone query in the Google Shopping playground, then swap in your own first product.
APIs in this post
Frequently asked questions
Does the Google Shopping API return prices from several sellers?
Each item in shopping_results has a source field naming the seller and a price for that listing. In the recorded response, one result came from Walmart and another from Best Buy. Search for a product and read the results to compare sellers; the endpoint returns what Google Shopping shows for that query at call time.
How much does one Google Shopping API call cost?
A successful call costs 1 credit, and a failed call costs nothing. Every response carries X-Credits-Used and X-Credits-Remaining headers, so you can track spend per call. Accounts get 1,000 free credits every month after verifying an email address, with no credit card. See pricing for what credit packs cost.
Can I get prices for a specific country?
Yes. Set gl to a two-character country code and hl to a language code, and use location or uule for a more specific place. Keep these the same across runs, because the same product can show different prices and sellers in different countries. The response echoes the values in search_parameters.
How do I get the next page of results?
Pass start with the next_start value from the previous response's pagination object. The recorded response ran with start 0 and returned next_start 10. Page sizes aren't documented, so read next_start from each response instead of assuming a step. The Google Search guide shows a similar loop.
What happens when I hit the rate limit?
You get a 429 rate_limited response with a Retry-After header giving the seconds to wait. Limits are per account and counted per minute: free accounts get 60 requests a minute, and paying accounts at least 300. Wait for Retry-After, then retry; a rate-limited call isn't billed.