Track Which Sites Rank in Google Images for Your Keywords
Use Google Images Scraper to see which domains hold the top image positions for your keywords in each country, then rerun weekly to catch movement.
The actor referenced in this article. Pay only for results delivered.
Image search is a ranking of its own. If you run SEO for a store, a publisher or a client, you want to know which domains own the first rows of Google Images for your keywords, where your own pages sit, and whether any of that changed since last week. Doing it by hand means scrolling a grid of about 100 tiles per keyword per country and writing domains into a sheet, and nobody keeps that up past the second week.
Try it live: Google Images Scraper: Full Size Image URLs, Sizes, Sources. Pay per result delivered. Failed and empty results are never charged.
The Google Images Scraper reads the Google Images results for each search term and returns one row per image, in Google’s order, with the domain of the page the image appears on. Group those rows by domain and you have an image ranking report.
What you get and who it is for
| Field | Why it matters for rank tracking |
|---|---|
query | The keyword the row belongs to |
position | Rank within that keyword, from 1, after duplicates are removed |
page | The Google results page the image came from |
source_domain | The site that owns the ranking page, without www |
source_name | The site name Google shows, such as Vogue Arabia or Etsy |
source_page_url | The exact page that ranks |
image_id | Google’s id for the image, the same in every run, so you can follow one image over time |
is_product | True when Google shows the image as a shop listing with a price |
country, language | The market the ranking belongs to |
scraped_at | When the row was collected |
This is for SEO leads at online stores who sell through product images, for publishers and design sites that get traffic from image search, and for agencies that report share of image results to clients. Shop listings are a large part of commercial grids: in our runs, 8 to 10 of the top 20 images for red sneakers carried a price.
The input
{
"queries": ["red sneakers", "white leather sneakers", "chunky sneakers"],
"maxResultsPerQuery": 100,
"country": "us",
"language": "en",
"imageSize": "any",
"imageType": "any",
"timeRange": "any",
"usageRights": "any",
"safeSearch": false
}
maxResultsPerQuery: 100 is about one Google page for a common term. That is enough to count the top 20 and to spot your own site further down. The minimum is 20.countryandlanguage: Google’s gl and hl settings. They apply to the whole run, so a second market needs a second run.- Filters stay on
any. A ranking report should match what a searcher sees before touching the Tools menu.
What the output looks like
[
{
"query": "modern kitchen interior",
"position": 11,
"page": 1,
"image_id": "JU22XoW4cpxlNM",
"title": "What Is the 2026 Trend for Kitchens?...",
"source_page_url": "https://www.occasionallycrafty.com/what-is-the-2026-trend-for-kitchens/",
"source_domain": "occasionallycrafty.com",
"source_name": "Occasionally Crafty",
"country": "us",
"language": "en",
"scraped_at": "2026-10-06T09:07:36.200Z"
},
{
"query": "modern kitchen interior",
"position": 5,
"page": 1,
"image_id": "JU22XoW4cpxlNM",
"title": "What Is the 2026 Trend for Kitchens?...",
"source_page_url": "https://www.occasionallycrafty.com/what-is-the-2026-trend-for-kitchens/",
"source_domain": "occasionallycrafty.com",
"source_name": "Occasionally Crafty",
"country": "us",
"language": "en",
"scraped_at": "2026-10-07T09:06:31.775Z"
},
{
"query": "red sneakers",
"position": 3,
"page": 1,
"image_id": "tHi8WUh0jLzN6M",
"title": "Fuego Red High-top Sneaker | Size 14 Men / 15 Women...",
"source_domain": "fuegodance.com",
"is_product": true,
"product_price": 155,
"product_currency": "USD",
"country": "us",
"language": "en",
"scraped_at": "2026-10-07T09:06:36.098Z"
}
]
These rows come from two of our own runs 24 hours apart, on October 6 and October 7, 2026, for red sneakers and modern kitchen interior in the US. The same Occasionally Crafty image moved from position 11 to 5. For red sneakers, fuegodance.com held 6 of the top 20 slots on October 6 and 2 on October 7, and the number of distinct domains in the top 20 went from 13 to 17. Most of the grid held still: 15 of the top 20 image ids for red sneakers and 17 of 20 for modern kitchen interior were the same on both days.
Run it in Apify Console
- Open https://apify.com/themineworks/google-images-scraper and click Try for free.
- In Search terms, enter one keyword per line.
- Set Images per search term to 100, Country to us and Language to en. Leave Size, Type, Time and Usage rights on their Any settings.
- Click Start and watch rows arrive in the Output tab.
- Click Export and download CSV, JSON or Excel. In a spreadsheet, a pivot of
source_domainbyquery, filtered toposition20 or less, gives the domain count per keyword.
Run it from Python
pip install apify-client
import csv
import glob
from collections import Counter, defaultdict
from datetime import date
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
KEYWORDS = ["red sneakers", "white leather sneakers", "chunky sneakers"]
MARKETS = [("us", "en"), ("gb", "en")]
MY_DOMAIN = "yourstore.com"
TOP = 20
rows = []
for country, language in MARKETS:
run = client.actor("themineworks/google-images-scraper").call(run_input={
"queries": KEYWORDS,
"maxResultsPerQuery": 100,
"country": country,
"language": language,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("_type") == "info":
continue # the run summary row, never billed
rows.append(item)
today = f"image_ranks_{date.today()}.csv"
fields = ["query", "country", "language", "position", "source_domain",
"source_name", "image_id", "title", "source_page_url", "scraped_at"]
with open(today, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
writer.writerows(rows)
def best_positions(items):
best = {}
for r in items:
key = (r["query"], r["country"], r["source_domain"])
pos = int(r["position"])
if key not in best or pos < best[key]:
best[key] = pos
return best
groups = defaultdict(list)
for r in rows:
groups[(r["query"], r["country"])].append(r)
for (query, country), items in sorted(groups.items()):
top = Counter(r["source_domain"] for r in items if r["position"] <= TOP)
mine = [r["position"] for r in items if r["source_domain"] == MY_DOMAIN]
print(f"{query} [{country}]: {len(items)} images, {len(top)} domains in the top {TOP}")
for domain, n in top.most_common(5):
print(f" {domain}: {n} of {TOP}")
print(f" {MY_DOMAIN}: best position {min(mine) if mine else 'none'}")
older = sorted(p for p in glob.glob("image_ranks_*.csv") if p != today)
if older:
with open(older[-1], encoding="utf-8") as f:
before = best_positions(csv.DictReader(f))
for key, pos in sorted(best_positions(rows).items()):
old = before.get(key)
if pos <= TOP and old != pos:
change = "new in the list" if old is None else f"{old} to {pos}"
print("MOVED", key, change)
The script runs once per market, saves a dated CSV, prints how many domains share the top 20 for each keyword and country, and shows the best position of your own domain. From the second run on, it compares against the previous file and lists every domain whose best position inside the top 20 changed.
Run it every week
In Apify Console, save the input as a task, one task per market. Open Schedules, create a schedule with the cron expression 0 7 * * 1 (Mondays at 07:00) and add the tasks. Every run reads Google fresh with the same input, so the weekly datasets line up row for row. The actor has no “only new” option, and rank tracking does not need one: you want the full grid each time.
To get the results out, Apify’s Google Sheets integration can append each run to a sheet, and a webhook on run success can trigger your own code or a Make, Zapier or n8n flow that posts the movers to Slack. If you prefer the Python script, run it from cron on your own machine with the same timing; it keeps the dated files it compares. Once several markets and keywords pile up, those dated files are a dataset in their own right, and it is worth deciding early how a raw run becomes a clean, stored table.
Weekly is a sensible cadence. In our two runs a day apart, three quarters or more of the top 20 stayed the same, so daily runs mostly repeat themselves.
What it costs
You pay per image delivered: $2.09 per 1,000 on the Bronze plan, $1.79 on Silver, and $1.49 on Gold, Platinum and Diamond, plus a flat $0.005 per run start.
Three keywords, 100 images each, two markets, every Monday: 600 images a week, about 2,400 a month, which is about $5.02 on Bronze plus $0.04 in start fees for eight runs. If you only care about the top 20, set Images per search term to 20 and the same schedule is 480 images a month, about $1.00 on Bronze. A term that comes back short costs less, and pages of unrelated filler, where no title holds a word of your search, are not charged.
Limits worth knowing
- One market per run. Country, language and filters are set once for the whole run.
- A term can come back short. Our run at 11:11 UTC on October 7, 2026 asked for 100 per term and delivered 26 for red sneakers and 11 for modern kitchen interior, two hours after a run of the same input had delivered 79 and 100. The run’s
OUTPUTrecord gives each term a status such asno_match,no_resultsorblocked. Compare only runs that reached the depth you track, or every lower site will look like it dropped out. - Google’s depth is the ceiling. The input allows 1,000 per term, but Google stopped at 355 for red sneakers in the actor’s own test.
- Rank, not traffic. You get who ranks and where. There is no search volume or click data in the rows.
- The domain is the page, not the file host.
source_domainis the site of the page that ranks. The file inimage_urloften sits on a CDN, such as i5.walmartimages.com for a walmart.com listing, so count bysource_domain.
Related
Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.
Frequently asked questions
Can one run track several countries? +
No. Country, language and the filters apply to every search term in a run. Start one run per market, as the Python script below does, and keep the country column when you merge the results.
How do I find where my own site ranks? +
Filter the rows on source_domain equal to your domain and take the lowest position. If your site is not in the first 100, raise maxResultsPerQuery, but expect Google to stop serving at about 300 to 400 images for a common term.
Will my own browser show the same order? +
Not always. The actor reads Google signed out, for the country and language you set, at the moment of the run. A fixed scheduled run is a steadier baseline than a check in your own browser.
Do repeated images within one search cost extra? +
No. The same image twice for one search term is never charged, and position counts images after those duplicates are removed.
Build a Labeled Image Dataset From Google Images Search Terms
Turn one search term per class into a labeled image manifest with Google Images Scraper: full size URLs, width, height and the source page to check.
Track Daily Rank Changes in an Amazon Best Sellers Category
Run Amazon Bestsellers Scraper every morning, diff each ASIN's rank against yesterday, and flag new entrants, drop outs and big movers in Python.
Monitor Competitor LinkedIn Company Posts on a Schedule
Get every new post from competitor LinkedIn company pages each day or week, with only-new mode and an Apify schedule. No LinkedIn login or cookies.
Monitor the Newest Comments on Your YouTube Videos Every Day
Read the newest comments on your YouTube videos every day with YouTube Comments Scraper, keep only unseen ones, and flag questions you have not answered.