I've been a Cloudflare fan for a while now, but have mainly focused on their Workers Serverless platform. I was aware, of course, that they did a lot more, but I just haven't had the time to really look around and explore. Recently I was doing some investigation into "url to screenshot" services and discovered that Cloudflare had this, and not only that, it's part of a suite of browser APIs that are really freaking awesome.

Cloudflare's Browser Rendering APIs do things like:

  • Get the HTML of a page, but after JavaScript has executed, allowing it to get dynamic HTML
  • Render a PDF to PDF
  • Scrape HTML via selectors
  • Parse out content via JSON schema (I'm absolutely going to be testing this soon)
  • Convert a page to Markdown
  • And of course, make screenshots

The capture screenshot is incredibly flexible. While you can just pass it the URL, you can also do things like:

  • Specify a viewport size
  • Modify the CSS and JavaScript on the page (could be useful for hiding full page modals)
  • Pass your own HTML instead of using a URL
  • Wait until a selector is visible

And more. I linked to the doc just above, but it's pretty minimal, the reference page for the API shows a lot more options you can tweak.

Best of all - this is available on the free tier. The limits are reasonable, but note that there is a max of 6 calls per minute (again, on the free tier), so keep this in mind if you are attempting to grab a bunch of screenshots at once.

Ok, how about a quick demo? I wrote a simple Python script that lets me pass a URL, and an optional width and height to the command line. The script will then hit the API, generate the image, and save it to a slugified version of the URL:

import os 
import sys
import requests 
from slugify import slugify

cfAccountId = os.environ.get('CF_ACCOUNTID')
cfKey = os.environ.get('CF_BR_KEY')

if len(sys.argv) < 2:
  print('Usage: python screenshot.py url <<width, defaults to 720>> <<height, defaults to 1280>>')
  sys.exit(1)
else:
  url = sys.argv[1]
  width = 720
  height = 1280
  if(len(sys.argv) > 2):
    width = int(sys.argv[2])
  if(len(sys.argv) > 3):
    height = int(sys.argv[3])

req = requests.post(f"https://api.cloudflare.com/client/v4/accounts/{cfAccountId}/browser-rendering/screenshot", 
	json={ 
		"url":url,
		"viewport": {
			"width":width,
			"height":height,
		}
	}, 
	headers={"Authorization": f"Bearer {cfKey}", "Content-Type": "application/json"}
)

if req.status_code == 200:
	filename = slugify(url) + '.png'
	with open(filename, 'wb') as f:
		f.write(req.content)
		print(f"Saved to {filename}")
else:
	print("Error", req.json())

This is kind of ridiculously simple but that's what you want in an API. As I mentioned above, the reference shows a huge number of additional options you can pass, but this script will let you test out basic stuff. How about some quick examples?

First, this blog, with default width (720) and height (1280):

Screenshot from my blog

And here's a larger version, width 1720 and height 1200 (the image here has been shrunk to about 700 wide for display):

Screenshot from my blog, wider

Notice how the responsive design correctly renders in both options.

So as I said, this is a pretty cool API, and I've got some thoughts on how to use within a BoxLang application soon. I also want to dig into other aspects, especially the structured data aspect, something I've used GenAI for in the past. If you've used any of the browser rendering APIs from Cloudflare, please let me know in a comment below.