Google Images API: Search Image Results as JSON
You can get Google Images results as JSON with one call: GET /v1/google-images/images with a q parameter and your key in the X-API-Key header. Each result in the array has a thumbnail, the page it came from, the original image URL and its pixel size. A successful call costs 1 credit, and a failed call costs nothing. Results are pointers to other people's images, not licensed assets, so read the licensing section before you reuse any of them.
How do you get Google Images results as JSON?
Send a GET request to https://api.perceptlayer.com/v1/google-images/images with the search text in q and your key in the X-API-Key header. The response is a JSON array, one object per image result, with named fields instead of a page of HTML to parse. The call costs 1 credit when it succeeds and nothing when it fails.
Here is the smallest useful request:
curl "https://api.perceptlayer.com/v1/google-images/images?q=coffee" \
-H "X-API-Key: $API_KEY"
Keys start with sk_live_. If your client can't set headers, the api_key query parameter works too, though a header keeps the key out of logs and URLs. The authentication docs cover both. You can also run the same call without writing code in the playground on the Google Images API page.
Which parameters does the Images endpoint take?
The endpoint has one required parameter, q, and nine optional ones that narrow the results by language, country, size, type, colour, aspect ratio and safe-search setting. The table lists every parameter and the values the endpoint documents for it. Nothing beyond these is documented, so don't rely on defaults or a fixed page size.
| Parameter | Type | Required | Allowed values or limit |
|---|---|---|---|
q |
string | yes | Search text |
hl |
string | no | Up to 2 characters |
gl |
string | no | Up to 2 characters |
imgsz |
string | no | large, medium, icon |
imgtype |
string | no | photo, clipart, lineart, gif, face |
imgcolor |
string | no | color, gray, trans, red, orange, yellow, green, teal, blue, purple, pink, white, black, brown |
imgar |
string | no | tall, square, wide |
tbs |
string | no | Up to 500 characters |
safe |
string | no | active, off |
page |
integer | no | Page number |
Use imgsz, imgtype, imgcolor and imgar before anything else, because they have fixed values and invalid ones are rejected with an invalid_parameters (422) error. A wide, large photo search looks like this:
curl "https://api.perceptlayer.com/v1/google-images/images?q=coffee&imgsz=large&imgtype=photo&imgar=wide&safe=active" \
-H "X-API-Key: $API_KEY"
The hl and gl parameters accept up to two characters each, which fits a language code and a country code. The tbs parameter takes a free-form string of up to 500 characters. The endpoint page is the reference for the full list.
What does a response look like?
The response is a JSON array of result objects, ordered by position. This excerpt is a real response to the q=coffee request above, trimmed with … where results and long URLs were cut:
[
{
"position": 1,
"thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:…",
"source": "Wikipedia",
"title": "Coffee - Wikipedia",
"link": "https://en.wikipedia.org/wiki/Coffee",
"original": "https://upload.wikimedia.org/wikipedia/commons/e/e4/Latte_and_dark_coffee.jpg?…",
"original_width": 3200,
"original_height": 2000,
"is_product": false
},
{
"position": 2,
"thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:…",
"source": "InstaCuppa Store",
"title": "Easy Coffee Recipe at Home: Simple Cafe-Style Coffee in 2 Minutes",
"link": "https://instacuppastore.com/blogs/recipes/easy-coffee-recipe-at-home?…",
"original": "https://instacuppastore.com/cdn/shop/articles/blog-easy-coffee-recipe-at-home-cover_….jpg?…",
"original_width": 1100,
"original_height": 738,
"is_product": true
}
]
| Field | What it holds |
|---|---|
position |
The result's rank in the list |
thumbnail |
A small preview image URL |
source |
The name of the site that shows the image |
title |
The title of the page |
link |
The page the image appears on |
original |
The full-size image file |
original_width, original_height |
Pixel dimensions of the original |
is_product |
Whether the result is flagged as a product |
The two results show why link and original are separate. The first image is hosted on Wikimedia Commons and shown on a Wikipedia page. The second comes from a shop's blog post. Same field names, very different owners.
How do you call it from Python?
Python needs only requests. The script below searches for large photos, keeps results at least 1,000 pixels wide and prints the page each one came from. It reads the key from the environment and raises on any error status, so a failed call stops the script instead of passing an error body along as data.
import os
import requests
resp = requests.get(
"https://api.perceptlayer.com/v1/google-images/images",
headers={"X-API-Key": os.environ["API_KEY"]},
params={"q": "coffee", "imgsz": "large", "imgtype": "photo"},
timeout=30,
)
resp.raise_for_status()
print("credits used:", resp.headers.get("X-Credits-Used"))
print("credits left:", resp.headers.get("X-Credits-Remaining"))
for item in resp.json():
if item["original_width"] >= 1000:
print(item["position"], item["source"], item["link"])
Two headers help here. X-Credits-Used shows what the call cost and X-Credits-Remaining shows your balance afterwards. If a request times out on your side, it may still have succeeded and been billed, because requests have no idempotency key. The errors docs list every code and what to do about each.
Can you reuse the images you find?
Not automatically. A search result tells you where an image appears, not that you may copy it. Google's own help page says that "images might be subject to copyright" and tells you to confirm an image's license information through the license details link and the host site (Google Search Help, 2026). Treat every result as owned by someone else until you've checked.
The API gives you the facts needed for that check, but not the licence itself. Here is a practical way to use the fields:
- Keep
linkwith every image. It is the page you will need to open to find the owner, the licence or a contact. Store it next tooriginal. - Don't hotlink
original. The file belongs to the host, which can change or remove it, and serving it from your own pages may breach their terms. - Check the licence on the host page. Google documents a
licenseproperty in image structured data that sites can use to earn a Licensable badge, which links to the licence and usage details (Google Search Central, Image license metadata, 2026). A site that doesn't publish this isn't granting you anything. - Treat
is_productresults with extra care. Product images are usually a seller's or brand's marketing material. - Ask when in doubt. Licences differ. Some require attribution and some prohibit commercial use, so the host page or the owner is the only authority.
This is general guidance, not legal advice. If your product republishes images, take advice on the licences that apply.
What is it good for instead?
Reading image results as data is safe in ways that copying them isn't. The metadata and the pointers are the useful part, and you can build on them without storing a single image file.
- Visual research. Count which sites appear for a query, and read
sourceandtitleto see how a topic is illustrated. - Product and brand monitoring. Filter on
is_productto see which shops show an item, with alinkto each page. - Asset sourcing. Build a shortlist of candidates with sizes from
original_widthandoriginal_height, then clear each one with its owner or a licensed library. - Agent tools. Give an agent a search tool that returns titles and page links, so it can cite where an image appears rather than reproducing it. The AI agents docs explain the MCP server, which exposes the same endpoint through
call_endpoint.
If you need web results rather than images, see Google Search API: Get Search Results as JSON. For product prices, see Google Shopping API: Product Prices and Offers as JSON.
What does it cost?
Each successful call to /v1/google-images/images costs 1 credit. A failed call costs 0 and returns X-Credits-Used: 0. There is no per-seat fee or subscription, and purchased credits last 12 months. New accounts get 1,000 free credits a month after verifying an email address, with no credit card.
| Searches | Credits |
|---|---|
| 1,000 | 1,000 |
| 20,000 | 20,000 |
| 50,000 | 50,000 |
Twenty thousand calls × 1 credit = 20,000 credits, which fits inside the Build pack's 50,000. The pricing page has what credits cost, and the credits docs explain how debits work. Rate limits are per account and counted per minute: free accounts get 1 request a second, and the rate limits docs cover paid rates and the Retry-After header on 429 responses.
To run your first search, get a free API key and paste the cURL call above. Then try imgcolor or imgar in the playground on the Google Images API page.
APIs in this post
Frequently asked questions
Is there an official Google Images API?
This endpoint is not an official Google service. It returns Google Images results as JSON from publicly available sources, as found at the time of the call. If you need a licensed, contractual image source, a stock library or the image owner's own API will fit better than a search result.
How much does a Google Images search cost?
A successful call costs 1 credit, so 10,000 searches cost 10,000 credits. Failed calls cost nothing and return X-Credits-Used: 0. The free tier includes 1,000 credits a month with no credit card, and the pricing page lists what credit packs cost.
Can I use the images that come back?
Not by default. Google says images might be subject to copyright and advises confirming license information with the host site. Use link to find the owner, check the licence there, and don't assume that a visible or downloadable file is free to publish or sell.
How do I filter by size, colour or type?
Add imgsz (large, medium, icon), imgtype (photo, clipart, lineart, gif, face), imgcolor or imgar (tall, square, wide) to the request. Values outside the allowed lists return an invalid_parameters (422) error. Failed calls aren't billed.
What happens if I hit the rate limit?
You get a rate_limited (429) response with a Retry-After header giving the seconds to wait. Rate-limit headers (RateLimit-Remaining, RateLimit-Reset) show how much of the minute is left. The rate limits docs cover the details.