Text and metadata tell you what a page says. A screenshot tells you what it looks like, which is the only thing that catches a broken stylesheet, a cookie wall covering the content, a hero image that failed to load, or a competitor's redesign. And an image inventory tells you what the page loads, which matters for performance work, asset audits and checking that nobody is hotlinking your CDN.
Both APIs in this lesson return URLs rather than file contents. That one design decision drives most of the code you write around them.
Capturing a page
Website Screenshot renders a URL in a browser and stores the image. It costs 40 credits, the most expensive call in this course, so it pays to know the parameters before you start looping.
| Parameter | Default | Notes |
|---|---|---|
url | required | The page to capture |
type | png | Image format; see the note below |
width | 1920 | Viewport width, 100 to 3840 pixels |
height | 1080 | Viewport height, 100 to 3840 pixels |
fullpage | false | Premium. Capture the whole scrollable page instead of the viewport |
curl -X POST "https://api.apiverve.com/v1/webscreenshots" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","width":1280,"height":800,"type":"jpeg"}'imageName ends in .jpg. downloadURL is a signed link and expires is in milliseconds.
The response:
{
"width": 1280,
"height": 800,
"scaleFactor": 1,
"imageName": "1423a6ce-....jpg",
"expires": 1792867371256,
"url": "https://example.com",
"downloadURL": "https://storage.googleapis.com/apiverve/APIData/webscreenshots/1423a6ce-....jpg?GoogleAccessId=...&Expires=1792867371&Signature=..."
}
width, height and scaleFactor (the device pixel ratio used) are premium fields that report what was actually captured. imageName and downloadURL are what you use.
On format: png and jpeg behaved as expected in testing. A request for webp came back with a .png image name, so check the extension of imageName rather than trusting the format you asked for, and convert locally if you need WebP. JPEG is the better default for photographs and busy pages, typically a fraction of the PNG size. PNG is better for text-heavy pages you plan to compare pixel by pixel, because JPEG compression noise shows up as differences that are not there.
The viewport is the layout
width is not just the image size. It is the browser window the page is laid out in, so it decides which responsive breakpoint you capture. A 390-pixel width gets the mobile layout with the hamburger menu; 1280 gets the desktop layout. If you are checking that a page works on phones, capture at a phone width. Do not capture at 1920 and shrink the image afterwards, which gives you a tiny desktop page, not a mobile one.
height is the visible window, and without fullpage the capture stops there. For "what does a visitor see before scrolling", that is exactly what you want.
You need to check that a landing page's mobile layout is not broken. Which capture is right?
Answer the check above to continue.
Every image a page loads
Website Images Scraper lists the images on a page: images[] with src and external, plus imageCount, internalImageCount, externalImageCount, uniqueDomains and maxLinksReached. It costs 25 credits.
curl -X POST "https://api.apiverve.com/v1/webimagescraper" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.wikipedia.org"}'external is true for images on a different host. uniqueDomains lists the external hosts.
On wikipedia.org, the logo is served from www.wikipedia.org itself and the donation badges come from upload.wikimedia.org, so the page reports one internal image, the rest external, and uniqueDomains: ["upload.wikimedia.org"]. "External" means a different hostname from the page, not a different company: a site that serves its own images from cdn.example.com will show all of them as external. Group by uniqueDomains and decide for yourself which hosts are yours.
The default limit is 25 images, raising it with maxlinks is premium, and maxLinksReached tells you whether you hit the cap. As with links in lesson two, a capped result cannot prove an image is absent.
Query strings on image URLs
includequery defaults to false, which trims query strings from each src. For deduplication that is helpful. For image CDNs it changes the meaning of the URL: photo.jpg?w=1600&q=80 and photo.jpg?w=200 are different files, and the trimmed photo.jpg may be the full-size original or may not resolve at all. If you are auditing image weight, or you intend to fetch the images, set includequery to true so you get the URLs the browser actually requested.
What an inventory is good for
- Asset audits. Find images still served from an old domain after a migration, or third-party hosts you did not know the page depended on.
- Broken image checks. Send a
HEADrequest to eachsrcand report anything that is not200. Throttle these, since you are now making requests to someone's server. - Choosing a preview image. When a page has no
og:image, the first sizeable internal image is often a better fallback than nothing.
from collections import Counter
from urllib.parse import urlparse
import requests
from apiverve import apiverve
def audit(url: str) -> None:
data = apiverve("webimagescraper", {"url": url, "includequery": True})
hosts = Counter(urlparse(img["src"]).hostname for img in data["images"])
print(f"{data['imageCount']} images, {data['externalImageCount']} external")
for host, n in hosts.most_common():
print(f" {n:3} {host}")
if data["maxLinksReached"]:
print(" (list truncated at the limit)")
for img in data["images"]:
try:
r = requests.head(img["src"], timeout=10, allow_redirects=True)
if r.status_code != 200:
print(f" BROKEN {r.status_code} {img['src']}")
except requests.RequestException as e:
print(f" ERROR {img['src']}: {e.__class__.__name__}")
if __name__ == "__main__":
audit("https://www.wikipedia.org")Rights and manners
A list of image URLs is not a licence to use the images. Photos, illustrations and logos belong to whoever made them, and finding them on a public page does not change that. Inventories for audits, monitoring and your own sites are routine. Downloading competitors' product photos to use on your store is not.
Screenshots sit in a similar place. Capturing a page to see whether it is up, to archive your own marketing site, or to show a thumbnail next to a link that points back to the source is normal practice. Publishing screenshots of other people's content as if it were yours is not. If a page shows personal data, so does its screenshot, and it sits in your storage with whatever retention obligations apply to you.
Finally, both calls make a real browser or crawler request to the target site. Spread captures of the same domain out rather than firing a hundred at once.
Next, the polite alternative to scraping altogether: feeds that publishers maintain for exactly this purpose, and sitemaps, in RSS feeds and sitemaps.