Google Images API 11 min read

Google Image Search API with Python: Get Images as JSON

Use a Google Image Search API with Python to get image titles, thumbnails, original URLs, dimensions, and source pages as structured JSON in one request.

2,079 words

Python can fetch an image page with a normal HTTP client, but parsing Google Images markup is a fragile way to build a product. The page structure changes, fields are inconsistent, and a script that worked yesterday may return an empty grid today. A structured image search API keeps the Python side focused on the data you actually need.

Short answer: send an authenticated POST request to the SearchCans Google Images API with t set to google_images, put the text query in s, and optionally choose a country. The response returns image records under data.images_results, including result positions, source pages, thumbnails, original image URLs, dimensions, and source names.

Python sends an image search request, receives structured JSON, and maps the response into an image result grid.

Figure 1. A text query becomes structured image result records that Python can filter before any image is downloaded.

Key takeaways

  • The current endpoint is POST https://www.searchcans.com/api/v1/search.
  • Use "t": "google_images", not the older or provider-specific value "images".
  • The source page is in link; the direct image asset is in original; the preview is in thumbnail.
  • This tutorial covers text-to-image-result search. It does not upload an image or perform reverse-image matching.
  • Keep the API key in an environment variable and review image rights before downloading or republishing any result.

What this Python tutorial builds

The finished script will:

  1. search Google Images with a text query;
  2. validate both the HTTP response and the SearchCans response envelope;
  3. read images_results without assuming every optional field is present;
  4. filter by image dimensions and orientation;
  5. remove duplicate original URLs; and
  6. save selected metadata as JSON or CSV.

It will not scrape HTML, launch a browser, solve a CAPTCHA, or download every image returned by the search. Those are separate jobs with different operational and rights considerations.

These phrases often get mixed together, but they describe different request shapes.

Request Input Expected output Covered here
Keyword image search A text query such as electric cargo bike Ranked Google Images result records Yes
Image download A direct image URL Binary image bytes No
Reverse image search An uploaded image or image URL Visually similar images or matching pages No
Image classification Image bytes Labels, objects, or embeddings No

SearchCans uses the first pattern in this tutorial. A request searches Google Images for text and returns structured metadata. Do not present the code below as an upload-based Google Lens or reverse-image API implementation.

Prerequisites

You need Python 3.9 or newer, the requests package, and a SearchCans API key. New SearchCans accounts receive 100 credits for initial testing.

Install the HTTP client:

python -m pip install requests

Store the key outside the source file. In Git Bash, macOS, or Linux:

export SEARCHCANS_API_KEY="your_api_key_here"

In PowerShell:

$env:SEARCHCANS_API_KEY = "your_api_key_here"

Never commit the real value to Git, paste it into a screenshot, or place it in a client-side application.

Make the first Google Images request

The smallest useful version is still strict about missing keys and API errors:

import os

import requests


API_URL = "https://www.searchcans.com/api/v1/search"
API_KEY = os.environ.get("SEARCHCANS_API_KEY")

if not API_KEY:
    raise RuntimeError("Set SEARCHCANS_API_KEY before running this script")

response = requests.post(
    API_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "t": "google_images",
        "s": "electric cargo bike",
        "country": "us",
    },
    timeout=35,
)
response.raise_for_status()

payload = response.json()
if payload.get("code") != 0:
    raise RuntimeError(f"SearchCans error: {payload.get('msg', 'unknown error')}")

images = payload.get("data", {}).get("images_results", [])

for image in images[:5]:
    print(f"{image.get('position')}. {image.get('title', 'Untitled')}")
    print(f"   source page: {image.get('link', '')}")
    print(f"   original: {image.get('original', '')}")
    print(
        "   size: "
        f"{image.get('original_width', '?')} x "
        f"{image.get('original_height', '?')}"
    )

There are two error layers on purpose. raise_for_status() catches HTTP failures. The code check catches a valid JSON response that reports an application-level error. Treating every JSON response as a successful search makes failures hard to diagnose.

Understand the image result fields

The response contains a data.images_results array. A result can include these fields:

Field Meaning Practical use
position Rank within the returned image results Preserve result order
title Title associated with the image result Display and review context
link Page where the result was found Attribution and source review
thumbnail Search-result preview URL Lightweight preview UI
original Original image asset URL Candidate download or processing input
original_width Original width in pixels Resolution filtering
original_height Original height in pixels Orientation and resolution filtering
source Source website name Source grouping and review
is_product Whether the result is marked as product-related Commerce-oriented filtering

The link field is usually the right place to investigate provenance. The original field points to an asset, not proof that the asset is licensed for your use. Search metadata and usage permission are separate questions.

Wrap the request in a reusable Python function

A helper keeps authentication, timeout, and response validation in one place:

import os
from typing import Any

import requests


API_URL = "https://www.searchcans.com/api/v1/search"


def search_google_images(
    query: str,
    *,
    country: str = "us",
    timeout_seconds: float = 35,
) -> list[dict[str, Any]]:
    api_key = os.environ.get("SEARCHCANS_API_KEY")
    if not api_key:
        raise RuntimeError("SEARCHCANS_API_KEY is not set")
    if not query.strip():
        raise ValueError("query must not be empty")

    response = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {api_key}"},
        json={
            "t": "google_images",
            "s": query,
            "country": country,
        },
        timeout=timeout_seconds,
    )
    response.raise_for_status()

    payload = response.json()
    if payload.get("code") != 0:
        message = payload.get("msg") or "unknown SearchCans error"
        raise RuntimeError(message)

    data = payload.get("data")
    if not isinstance(data, dict):
        raise RuntimeError("SearchCans response is missing the data object")

    images = data.get("images_results", [])
    if not isinstance(images, list):
        raise RuntimeError("images_results is not a list")

    return [item for item in images if isinstance(item, dict)]

The function does not silently retry. A production application can add bounded retries for connection failures and selected server errors, but it should not replay every request without understanding whether a prior attempt completed.

Filter by size, orientation, and duplicate URL

Image results are candidates, not a finished dataset. Filter metadata before downloading files:

from typing import Any


def select_landscape_images(
    images: list[dict[str, Any]],
    *,
    min_width: int = 1200,
    min_height: int = 630,
) -> list[dict[str, Any]]:
    selected = []
    seen_urls = set()

    for image in images:
        original = image.get("original")
        width = image.get("original_width")
        height = image.get("original_height")

        if not isinstance(original, str) or not original:
            continue
        if original in seen_urls:
            continue
        if not isinstance(width, int) or not isinstance(height, int):
            continue
        if width < min_width or height < min_height or width <= height:
            continue

        seen_urls.add(original)
        selected.append(image)

    return selected


results = search_google_images("electric cargo bike", country="us")
landscape_results = select_landscape_images(results)

for image in landscape_results:
    print(image["original"], image.get("source", "unknown source"))

This filter is intentionally conservative. It skips records with unknown dimensions instead of guessing. Change the thresholds to match the target layout, but keep the missing-field checks.

Save the metadata as JSON or CSV

Saving metadata is safer than immediately fetching every asset. It creates a review queue with the source page attached.

import csv
import json


fields = [
    "position",
    "title",
    "link",
    "source",
    "original",
    "original_width",
    "original_height",
    "thumbnail",
    "is_product",
]

with open("image-results.json", "w", encoding="utf-8") as file:
    json.dump(landscape_results, file, ensure_ascii=False, indent=2)

with open("image-results.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=fields, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(landscape_results)

Keep link in both exports. A list of direct image URLs without the corresponding source pages is much harder to review responsibly.

Use the SearchCans Python SDK instead of raw requests

The official SearchCans Python SDK wraps the same endpoint and supports synchronous and asynchronous clients. Until you choose a tagged release, install the reviewed main branch explicitly:

python -m pip install "git+https://github.com/SearchCans/searchcans-python.git@main"

Then use the image engine by name:

import os

from searchcans import SearchCans


with SearchCans(api_key=os.environ["SEARCHCANS_API_KEY"]) as client:
    response = client.serp.search(
        "electric cargo bike",
        engine="google_images",
        country="us",
    )

images = response.data.get("images_results", [])
for image in images[:5]:
    print(image.get("title"), image.get("original"))

Raw requests code is useful when one endpoint is all you need. The SDK is a better fit when the same application also uses Google Search, Google News, Reader, screenshots, account preflight, typed errors, or async requests.

Production safeguards worth keeping

Use explicit timeouts

Never let an outbound request wait forever. Set a client timeout slightly above the server-side timeout you intend to use, then record timeouts separately from empty result sets.

Bound retries

Retry connection resets and selected temporary server failures with backoff. Do not retry authentication errors, invalid parameters, or insufficient-credit responses. Keep the request count visible so a retry policy cannot create an unexpected burst.

Match concurrency to Parallel Lanes

Parallel Lanes describe simultaneous in-flight requests. They are not a monthly result quota. If a batch worker has three available lanes, keep no more than three requests active at once rather than launching a large unbounded task group.

Separate discovery from downloading

First search and filter metadata. Then review source pages and download only approved candidates. This reduces bandwidth, avoids unnecessary requests, and keeps rights review attached to the selected assets.

An original URL can expire, reject hotlinking, change content, or disappear. If your use is permitted, move the approved asset through your own controlled media pipeline and preserve attribution records.

Practical uses for structured image results

Visual content research

Collect result titles, source pages, and dimensions into an editorial review queue. Editors can compare themes and sources without opening every image result manually.

Brand and product monitoring

Run bounded searches for a brand, model name, or campaign phrase and compare the returned source pages over time. Image search can reveal where a visual appears, but it does not by itself prove ownership, sentiment, or infringement.

Multimodal retrieval pipelines

Use the result metadata to select candidate assets, then send only approved images to a vision model. Keeping search and model inference as separate stages makes failures and costs easier to inspect.

Commerce research

The is_product field can help prioritize commerce-related results. Treat it as a result attribute, not a guarantee that price, availability, or merchant details are current.

Cost and request planning

A successful standard Google Images request uses one SearchCans credit. New accounts receive 100 credits for testing, and current paid plans use prepaid credits rather than a required monthly subscription. Check the current SearchCans pricing page before estimating a production workload because plan terms and available lanes can change.

For a batch, estimate credits from the number of planned searches, then cap concurrent calls at the observed Parallel Lane count. Search once, reuse the structured response where appropriate, and avoid repeating the same query inside multiple downstream tasks.

Common questions

Q: Is this Google Cloud’s Custom Search JSON API?

A: No. The Python examples call the SearchCans Search API with the google_images engine. The input is a text query and the output is structured Google Images result metadata.

Q: Can this code search by an uploaded image?

A: No. It performs keyword-based image search. An upload-based reverse-image workflow requires a different request contract and should not be inferred from these examples.

Q: Does the original field grant permission to reuse an image?

A: No. It identifies an image asset found in search results. Review the source page, ownership, license, and intended use before downloading, republishing, or training on an image.

A: link provides the source-page context needed for attribution and review. original is the image asset URL used by a downstream download or vision-processing step. They serve different purposes.

Q: Can I call the API asynchronously from Python?

A: Yes. The SearchCans Python SDK includes AsyncSearchCans. Keep the async task count within the account’s available Parallel Lanes and use bounded retries.

Q: How many credits does one Google Images request use?

A: A successful standard request uses one credit. Check current pricing and account information before a larger batch rather than assuming every workflow has the same cost.

Start with one query and print five records. Confirm that the source page, original URL, dimensions, and title fit your application before adding downloads or concurrency. Review the live Google Images API documentation, create a free SearchCans account, and keep the first production version deliberately small.

Tags:

Google Images API Python Image Search API SERP API SearchCans
SearchCans Team

SearchCans Team

SERP API & Reader API Experts

The SearchCans engineering team builds high-performance search APIs serving developers worldwide. We share practical tutorials, best practices, and insights on SERP data, web scraping, RAG pipelines, and AI integration.

Ready to build with SearchCans?

Test SERP API and Reader API with 100 free credits. No credit card required.