Image Extractor — Every Image URL on a Page, With Alt Text
Pricing
from $8.00 / 1,000 page scans
Image Extractor — Every Image URL on a Page, With Alt Text
Extract every image on a web page as JSON: absolute src, alt text, width/height, loading, srcset, plus og:image and twitter:image. Built for alt-text and asset audits. Returns image URLs and metadata, not the image files. $0.01 per page scanned; failures free.
Pricing
from $8.00 / 1,000 page scans
Rating
0.0
(0)
Developer
Broke to Built
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
Webpage Images Extractor — Every Image URL on a Page, With Alt Text and srcset
Give it a page URL, get back every image on it as structured JSON — src resolved to an
absolute URL, the alt text, declared width and height, the loading strategy, the full
srcset, plus the page's Open Graph and Twitter card preview images and any CSS
background-image URLs in the served HTML. One page or up to 100 in a single run, built to be
called by code and by AI agents, not just clicked.
It returns image URLs and metadata. It does not download image files. If you want the bytes, take the
srcvalues and fetch them in a following step. Everything below is written to be true of the code, not to sell you something it does not do.
$0.01 per page scanned. No subscription, no seat fee, no minimum. You pay for pages that actually get fetched and parsed.
What problem this solves
"List every image on this page" sounds like a two-line script until you write it. Half
the images are lazy-loaded behind data-src. Paths are relative, so you have to resolve
them against the page URL — and against the URL the page redirected to, not the one you
typed. srcset needs splitting. Attribute values arrive HTML-escaped. The social preview
image is not an <img> at all, it is a <meta> tag. And the thing you probably care
about — which images are missing alt text — means auditing all of it consistently.
This does that as a hosted step you can call from a script, a workflow, or an agent. Nothing to install, and each page is fetched fresh from the URL you give it.
Who uses it
- Accessibility auditors who need a list of images missing alt text on a page.
- SEO teams checking image alt coverage and confirming
og:image/twitter:imageactually resolve. - Developers and QA taking an inventory of assets before a migration or a redesign.
- AI agents handed a page link that need the images on it as structured data.
- No-code / automation builders (Make, n8n, and similar) that can call a URL but cannot parse HTML.
Quick start
{"url": "https://en.wikipedia.org/wiki/Cat"}
That is the whole minimum input. Everything else is optional.
All input options
| Field | Type | Required | What it does |
|---|---|---|---|
url | string | one of these | A single webpage URL to scan |
urls | string[] | string | one of these | More page URLs. A plain string is split on commas and newlines |
maxUrls | number | no | Cap on pages processed per run, 1–100 (default 25) |
url and urls are merged, deduplicated, and truncated to maxUrls. A URL with no
scheme gets https:// prepended. If the merged list is empty the run fails immediately
with a message rather than charging you for nothing.
What you get back
Example 1 — a real page, real output
Input:
{ "url": "https://apify.com/store" }
Output item from an actual run of this actor, trimmed to the first two of 80 images:
{"url": "https://apify.com/store","finalUrl": "https://apify.com/store","imageCount": 80,"missingAltCount": 4,"images": [{"src": "https://apify.com/img/apify-logo/wordmark-white.svg","alt": "Apify logo","width": 100,"height": 27,"loading": null,"decoding": null,"srcset": "https://apify.com/img/apify-logo/wordmark-white.svg","source": "img","missingAlt": false},{"src": "https://cdn-cms.apify.com/apify_store_dark_bd743acd0f.webp","alt": "Promotion image background","width": null,"height": null,"loading": "lazy","decoding": null,"srcset": "https://cdn-cms.apify.com/apify_store_dark_bd743acd0f.webp","source": "img","missingAlt": false}]}
That single page cost $0.01. Note the second image: width/height are null because the
page sizes it in CSS, and loading is "lazy" because the HTML says so.
Example 2 — an accessibility pass over several pages
Input:
{"urls": ["https://example.com/", "https://example.com/about", "https://example.com/pricing"],"maxUrls": 3}
Three dataset items back, one per page. To get a true "images missing alt text" number, keep
only the img entries:
const missing = item.images.filter((i) => i.source === 'img' && i.missingAlt);console.log(item.url, missing.length, 'of', item.imageCount, 'need alt text');
missingAltCount on the item counts meta and CSS-background entries too, and those never have
an alt attribute — filtering by source is the honest number.
Example 3 — a page that fails
Failures never throw. Two shapes, both real:
{ "url": "https://httpstat.us/403", "error": "fetch failed", "imageCount": 0, "missingAltCount": 0, "images": [] }
{ "url": "https://example.com/gone", "finalUrl": "https://example.com/gone", "error": "HTTP 404", "imageCount": 0, "missingAltCount": 0, "images": [] }
A page that answered with a status code carries finalUrl; one that never connected does not.
Either way the run continues with the remaining pages and the failed page is not charged.
finalUrl— where the fetch actually landed after redirects. Relative image paths are resolved against this, not against the URL you submitted.imageCount— images found on that page, after deduplication by resolvedsrc.missingAltCount— how many entries havemissingAlt: true.images[].src— always absolute. Lazy-load attributes (data-src,data-original,data-lazy-src,data-lazy) anddata-srcsetare checked whensrcis absent.images[].alt— the alt attribute, HTML-entity-decoded.nullwhen the attribute is not present at all;""when it is present and empty.images[].width/height— the values declared in the HTML (a trailingpxis stripped).nullif not declared or not a plain number. These are not measured pixels.images[].loading/decoding— the attributes as written, ornull.images[].srcset— every candidate resolved to an absolute URL, descriptors kept. The field is omitted entirely when the image has no srcset.images[].source— where it came from:img,og:image,og:image:url,twitter:image,twitter:image:src,link:image_src, orcss-background.images[].missingAlt—truewhen there is no alt text. Alwaystruefor meta, link, and CSS-background entries, since those have no alt attribute to begin with.
One dataset item per page. A page that fails returns
{ url, finalUrl, error, imageCount: 0, missingAltCount: 0, images: [] } instead of
throwing, so one dead link in a batch of 25 never kills the other 24.
Call it from code
curl — synchronous run, JSON straight back:
curl -X POST "https://api.apify.com/v2/acts/eliai~webpage-images-extractor/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"url": "https://example.com/"}'
Python (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("eliai/webpage-images-extractor").call(run_input={"urls": ["https://example.com/", "https://example.com/about"]})for page in client.dataset(run["defaultDatasetId"]).iterate_items():bad = [i for i in page["images"] if i["source"] == "img" and i["missingAlt"]]print(page["url"], len(bad), "images missing alt text")
Node.js (npm install apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('eliai/webpage-images-extractor').call({ url: 'https://example.com/' });const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items[0].images.map((i) => i.src));
Use it as an AI agent tool
This Actor is callable over Apify MCP, so an agent can inventory the images on a page mid-conversation without you writing an integration. The shape an agent needs:
- Tool: this Actor
- Input:
{ "url": "<page url>" } - Returns:
imageCount,missingAltCount, and animagesarray of absolute URLs with alt text and attributes
If your agent can be handed a link to a page, it can now enumerate the images on it.
Pricing
Pay per event, one event: page-scanned.
| Event | What one event covers | Price |
|---|---|---|
page-scanned | One page fetched and parsed — every image on it, however many that is | $0.01 |
A page with 3 images and a page with 300 both cost $0.01. A 25-page run costs $0.25; 100 pages, the maximum in one run, costs $1.00. There is no start fee and no monthly fee. A page that returns a non-2xx status, times out, or fails to connect is written to the dataset as an error item and is not charged.
When NOT to use this
- The site renders its images with JavaScript. No headless browser runs here. A React or Vue app, an infinite-scroll gallery, or a script-driven carousel will report few images or none. Test one page before you queue a hundred.
- You want the image files. This returns URLs and HTML attributes, never bytes — no file size, no MIME check, no real pixel dimensions.
- You want to crawl a whole site. It scans exactly the URLs you list; it does not follow links or paginate. Feed it a sitemap's URLs if you need coverage.
- The pages need a login, a proxy, or bot-protection bypass. Those come back as
HTTP 403orHTTP 429error items. - You need art-directed
<picture>/<source>variants or inline SVG. Only the<img>inside a<picture>is read; inline<svg>is not returned at all. - You need every CSS background image. External stylesheets are never fetched, so only
inline
styleattributes and<style>blocks in the HTML are covered.
Honest limits
Worth knowing before you run it, so nothing surprises you:
- It reads the HTML the server sends — it does not run JavaScript. There is no headless browser here. Images injected client-side by a React/Vue app, an infinite-scroll gallery, or a script-driven carousel will not appear unless their URLs are already in the served HTML. On heavily client-rendered sites the count can be zero.
- It does not download the images. You get URLs and the attributes around them. No
file bytes, no file size, no MIME check, no real pixel dimensions —
width/heightcome from the HTML attributes only and arenullwhen the page relies on CSS. - No proxy and no login. Pages behind authentication, a paywall, or bot protection
come back as
HTTP 403/HTTP 429error items. The request identifies itself asEliBot/1.0. - 20-second timeout per page, and pages are fetched one after another, not in parallel. A run of 100 slow pages takes a while.
<picture>/<source>elements are not parsed directly. Only the<img>inside them is read, using its ownsrc/srcset. Art-directed<source srcset>variants are missed. Inline<svg>graphics are not images in this sense and are not returned.- CSS backgrounds are best-effort. The regex covers
background/background-imageurl(...)in inlinestyleattributes and in<style>blocks in the HTML. External stylesheets are never fetched, so most background images on a real site will not be found. missingAltCountcounts non-<img>sources too.og:image,twitter:image,link rel=image_srcand CSS backgrounds have no alt attribute, so they are always flagged. For a true accessibility number, count only entries wheresource === "img".- Deduplication is by resolved
src. The same image used twice on a page appears once, keeping the attributes of the first occurrence — so a repeated logo that has alt text in one place and not in another is reported by whichever came first in the HTML. - Inline
data:images are returned verbatim, which means a base64 blob can land insrcand make a dataset item large. - Only common HTML entities are decoded in attribute values (
&,",',<,>,/and their numeric twins). Exotic named entities pass through as written. - It scans exactly the URLs you give it. It does not crawl, follow links, or paginate. Redirects on a page you submitted are followed.
- Anything past
maxUrlsis silently dropped after deduplication, andmaxUrlsis clamped to 100 even if you ask for more.
FAQ
How do I extract all the images from a webpage?
Give this Actor the page URL. It fetches the HTML, finds every <img>, the Open Graph and
Twitter preview images, link rel=image_src, and CSS background-image URLs it can see,
resolves each one to an absolute URL, and returns them with alt text and attributes.
Can it find images that are missing alt text?
Yes. Every image carries a missingAlt boolean and each page carries a missingAltCount.
For an accessibility audit, filter to source === "img" first — meta and CSS-background
entries have no alt attribute and are always flagged.
Does it get lazy-loaded images?
It gets the ones whose URLs are in the served HTML: data-src, data-original,
data-lazy-src, data-lazy and data-srcset are all checked when src is missing. It
does not get images whose URLs only exist after JavaScript runs, because it does not
run JavaScript.
Can I scan multiple pages in one run?
Yes — pass urls as an array (or a comma/newline-separated string) alongside or instead
of url. Up to 100 pages per run, 25 by default. Each page produces its own dataset item.
Does it download the image files?
No. It returns image URLs and the HTML attributes around them. If you need the actual
files, take the src values from the output and fetch them in a following step.
What happens if a page is down or blocks the request?
That page's item comes back as { url, finalUrl, error: "HTTP 403", imageCount: 0, images: [] },
the run continues with the remaining pages, and the failed page is not charged.
Will it get images from a single-page app or an infinite-scroll gallery?
Usually not. Those render their images with JavaScript in the browser, and this Actor
reads the raw HTML response. If the page shows nothing without JS enabled, expect a low
or zero imageCount.
How do I get just the Open Graph preview image?
Run it on the page and keep the entries whose source starts with og: or twitter:.
Those come from the page's <meta> tags and are resolved to absolute URLs like the rest.
Where does the output go?
The Actor writes results to your run's dataset on your own Apify account, one item per page. Delete the run and the output goes with it.
Can an AI agent call this?
Yes — it is exposed through Apify MCP as an agent tool. See "Use it as an AI agent tool".
Who made this
Broke to Built — a company of machines, building things it gives away. This is one of them; the rest are free too.
For AI agents
This Actor is built to be called by software, not just by people.
- Mount it directly as an MCP tool — no Store search, no ranking, just this one tool:
https://mcp.apify.com/?actors=eliai/webpage-images-extractor - Or call it over HTTP and get the results in the same request:
POST https://api.apify.com/v2/acts/eliai~webpage-images-extractor/run-sync-get-dataset-items - Pay with x402, without an Apify account. This Actor is whitelisted for agentic payments, so an agent holding USDC on Base can buy a prepaid token and spend it here. The minimum purchase is $1, the token balance is an absolute spending cap, and it expires 14 days after purchase.
- Costs are predictable before you call. Pricing is pay-per-event (see Pricing above), so an agent can budget a run in advance instead of discovering the bill afterwards.
- Send only the field you mean. If you pass the bulk field, it is used on its own; the single-value field is a fallback, never merged into your request. You are charged for the items you sent and nothing else.
