Copy the complete workflow.
This manually triggered workflow asks for a URL and whether the page needs a bounded lazy-content sweep, then uploads the PNG for 14 days. The Action exposes the final path, render time, quota remaining, navigation state, and scroll state as step outputs.
name: Capture website screenshot
on:
workflow_dispatch:
inputs:
url:
description: Public webpage URL
required: true
default: https://example.com
full_page:
description: Capture the bounded full page
required: true
type: boolean
default: false
scroll_page:
description: Activate lazy content; requires full_page
required: true
type: boolean
default: false
permissions:
contents: read
jobs:
capture:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Capture webpage
id: screenshot
uses: BaiqingL/latchshot-action@v1
with:
api_key: ${{ secrets.LATCHSHOT_API_KEY }}
url: ${{ inputs.url }}
output: webpage.png
width: "1440"
height: "900"
full_page: ${{ inputs.full_page }}
scroll_page: ${{ inputs.scroll_page }}
- name: Upload screenshot
uses: actions/upload-artifact@v4
with:
name: webpage-capture
path: ${{ steps.screenshot.outputs.path }}
if-no-files-found: error
retention-days: 14
No checkout step is required when the workflow only creates the screenshot artifact. Pin to the moving major tag, @v1, for compatible updates or to a full release tag when the workflow requires an immutable dependency.
The Free plan returns a key with 100 successful renders each UTC calendar month. No credit card or approval queue is required.
Store the API key as an Actions secret.
- Open the target repository on GitHub.
- Choose Settings → Secrets and variables → Actions.
- Create a repository secret named
LATCHSHOT_API_KEY. - Paste the Free or paid key once, then run the workflow from the Actions tab.
The workflow passes the secret through the Action input and into an HTTPS authorization header. Do not put the key in the URL, workflow source, artifact name, or job summary. GitHub does not pass repository secrets to workflows triggered from forks.
Keep the artifact and its diagnostics together.
A successful step writes the requested image and exposes five outputs. Record them in a later step when the workflow needs evidence about how the capture completed.
| Output | Meaning | Example |
|---|---|---|
path | Requested output path | webpage.png |
render_ms | Server-side render duration | 1842 |
quota_remaining | Successful renders left this month | 87 |
navigation | Complete navigation or usable timed-out fallback | complete |
scroll | Bounded sweep completed or remained off | complete |
Only a successful render consumes direct-plan quota. A failed step does not produce an artifact, and if-no-files-found: error prevents a green workflow with missing evidence.
Reveal lazy-loaded sections before the artifact.
A full-page frame does not automatically change the page's scroll state. When lower sections appear only after movement, request both options and require the scroll diagnostic to be complete.
- name: Capture lazy full page
id: screenshot
uses: BaiqingL/latchshot-action@v1
with:
api_key: ${{ secrets.LATCHSHOT_API_KEY }}
url: https://example.com/catalog
output: catalog.png
full_page: true
scroll_page: true
- name: Require completed sweep
env:
SCROLL: ${{ steps.screenshot.outputs.scroll }}
run: test "$SCROLL" = "complete"
The sweep is deterministic and returns to the top before capture. It stops at 48 steps, five seconds, 20,000 CSS pixels high, or 5,000 pixels wide. An unsafe or unfinished sweep fails explicitly instead of labeling a partial page complete.
The Action does not accept scripts, selectors, clicks, custom speed or direction, sessions, private targets, or multi-step interaction. The lazy-loading guide shows real production before/after evidence and the provider boundary.
Schedule a fixed public page.
Replace the manual trigger with a UTC cron schedule and a fixed URL for recurring documentation, status-page, or release evidence. Scheduled workflows run from the default branch and GitHub may delay them during busy periods.
on:
schedule:
- cron: "17 6 * * 1-5"
# Keep the capture step from the complete workflow.
with:
api_key: ${{ secrets.LATCHSHOT_API_KEY }}
url: https://example.com/status
output: status-page.png
For recurring jobs, check quota_remaining, retain the previous successful artifact outside GitHub when history matters, and avoid retrying permanent 400 or 401 responses.
Choose hosted rendering when the page is public.
Moving Chromium out of the runner is a specific tradeoff, not a universal replacement for browser automation.
| Decision | Hosted Latchshot Action | Browser in your runner |
|---|---|---|
| Browser setup | No install or cache in the workflow | Install, cache, or maintain an image |
| Reachable pages | Public HTTP/HTTPS only | Can reach the runner's private environment |
| Customization | Bounded capture inputs and one lazy sweep | Arbitrary scripts and sessions |
| Operating model | External API key and quota | Runner minutes and browser upkeep |
Use a browser in your own runner for authenticated previews, private deployments, multi-step interaction, cookies, or arbitrary page scripts. Use the hosted Action when the input is a public URL and the desired output is a bounded screenshot artifact.
Make failures predictable.
- Targets must be public HTTP or HTTPS pages on ports 80 or 443.
- Private, loopback, link-local, special-use, and mixed public/private DNS answers are rejected.
- Viewport dimensions, full-page height, delay, timeout, retries, concurrency, and queue depth are bounded.
scroll_pagerequiresfull_pageand never accepts custom scroll logic or page actions.- Cookies, login sessions, arbitrary scripts, CAPTCHA solving, proxy rotation, and anti-bot bypass are not supported.
- The Action retries transient transport failures twice and treats invalid requests or credentials as permanent errors.
The public Action source is MIT licensed and its validation workflow is visible. The hosted renderer is a separate metered service. Read the benchmark method and beta boundaries before making the capture a release gate.
Start with 100 successful artifacts.
The Free plan renews to 100 successful renders each UTC calendar month. Launch is $19 for 2,500 per month, Build is $49 for 12,000, and Scale is $149 for 50,000. Automatic overages are disabled, and failed direct renders do not consume quota.
The current supported-page benchmark is 197/200 with 2.42-second p50 and 7.68-second p95 end to end. It is engineering evidence, not an SLA.
Need to prove a lazy full page?
Compare the exact production off/on artifacts, limits, diagnostics, and provider choices before changing a release workflow.