Send automated website screenshots to your own webhook.
Schedule a capture and have PagePixels POST it to any endpoint you control, as JSON, on every run or only when the page changes. Nothing to poll and no platform in the middle. Built in on every plan, including the free one.
- Built in. No other tools and no code beyond the handler you already have.
- POST on every scheduled capture, only when the page text changes, or both.
- Log in, click through, and fill forms before the capture is taken.
25 free screenshots / month · no credit card · no phone number
{
"screenshot_config_id": "827b7f5d-da55-44aa-a2d5-236b75cb1a14",
"url": "https://competitor.com/pricing",
"id": 90210,
"direct_link": "https://cdn.pagepixels.com/…/9a2f.jpg",
"direct_thumbnail_link": "…/site_index_9a2f.jpg",
"embed_url": "https://cdn.pagepixels.com/…/latest",
"taken_at": "2026-07-28T09:00:04+00:00",
"taken_at_timestamp": "1785315604000",
"custom_title": "Competitor pricing",
"ai": {
"ai_analyzed": true,
"ai_prompt": "What is the monthly price?",
"ai_response": "The monthly price is $34."
},
"is_proxy_screenshot": true,
"proxy_server": "Germany",
"job_id": "5f0f8b52-2b1c-4a1e-9e0c-2b0a51d6c1aa"
}
How to send screenshots to a webhook in four steps
No SDK to install and no polling loop to run. If your endpoint can accept a POST, it can receive captures.
-
Create a screenshot
Click + Create Screenshot in PagePixels and paste the page URL. Set the interval while you are there, from every five minutes to once a year.
+ Create Screenshot -
Add your webhook address
Paste the endpoint you want the capture posted to. Anything that accepts a POST works: your own service, a serverless function, or an automation platform's inbound URL.
https://yourapp.com/hooks/... -
Pick what fires it
Leave it there and every scheduled capture posts. Add a Change Notification step on a CSS selector and you get a second, separate POST whenever that part of the page moves.
every run / on change -
Save the screenshot
Hit Save Screenshot, top right. The first capture runs immediately, so you can watch the request land while you are still in the tab.
Save Screenshot
Doing it from code instead? The same config is one POST to /screenshot_configs with your webhook URL, or one tool call from an agent over MCP.
The image, and enough context to act on it.
A webhook that delivers only a file leaves you to work out what it was, when it was taken, and which config it belongs to. Every capture delivery here carries the image, a thumbnail, a permanent embed URL, the timestamp in two formats, and the capture's own identifiers, so your handler can write the row without calling back for more.
It also carries what you asked the capture to work out: the AI prompt and its written answer, and whether the page was captured through a Real Location and which one. Enough to file the result under the right market without a second lookup.
A change alert is a separate, deliberately smaller POST. The next section shows that body in full, so you can write both handlers before you connect anything.
- Which config sent it screenshot_config_id · url
- The config's own id and the page it captured, so one endpoint can serve every watch you run.
- The image direct_link
- The capture itself, in the format and size you set, full page or a single selector.
- A thumbnail direct_thumbnail_link
- A smaller copy of the same capture, for the list view you are about to render.
- A permanent link embed_url
- Always resolves to the latest capture of that config, with no API key exposed, so it is safe to embed.
- When it was taken taken_at · taken_at_timestamp
- ISO 8601 and a millisecond epoch, so you do not have to parse one into the other.
- The AI answer ai.ai_response
- If you asked a question of the capture, the prompt and the written answer arrive in the same body.
- Where it captured from is_proxy_screenshot · proxy_server
- Whether a Real Location was used and which one, for a row that has to say which market it describes.
The change alert is its own request, with its own shape.
This is the part people get caught by. A change notification does not reuse the capture body: it is a small object naming the notification, the configuration it came from, the value before, the value now, and the watched selector that moved.
That is deliberate. The alert is about the difference, not the picture, and everything about the capture is reachable from the configuration id whenever your handler wants it.
If it is easier, point the change alert at a second endpoint. Its webhook URL is set on the change step itself, independently of the one on the config, so the two shapes never have to meet in one handler.
{
"id": "da4b9237bacccdf19c0760cab7aec4a8359010b0",
"screenshot_configuration_id": "827b7f5d-da55-44aa-a2d5-236b75cb1a14",
"previous_value": "$29",
"retrieved_value": "$34",
"config": {
"type": "change",
"selector": ".plan-price",
"send_to": "webhook",
"url": "https://yourapp.com/hooks/pagepixels"
},
"sent_at": "1785315604263"
}
Three triggers, and the middle one is why most people wire this up.
All three are on every plan, including free, and a single config can use more than one destination at once.
-
Every scheduled capture
scheduleThe interval fires, the capture runs, your endpoint gets the POST. Right for filling a table, building a report, or feeding a dashboard you own.
every 1 hour · webhook URL set -
Only when something changed
change alertWatch a selector and a separate, smaller POST arrives only when it moves, carrying the previous and current values. This is the trigger most handlers actually want.
type: "change" · send_to: "webhook" -
On demand, from code or an agent
api · mcpFire a capture from your own service or let an agent do it over MCP, and have the result posted to the same endpoint rather than held in the response.
POST /screenshot_configs
Your endpoint should receive the page, not the login wall.
The pages worth posting to a handler are usually gated: a dashboard behind a login, a report you have to generate, a consent wall over the content. A renderer that only takes a URL will happily deliver the wrong screen, on schedule, forever.
Multi-step capture runs your steps in order in a real browser session first: fill the fields, submit, wait for the element, then capture and POST. Thirteen action types, point-and-click in the app or as parameters in the API, on every plan.
You can also capture from a Real Location in 150+ countries, states, and cities, so your handler receives the page as a customer in that market sees it.
-
Go to the login page url
-
Fill email and password text_field ×2
-
Click submit click
-
Wait for the dashboard wait_for_selector
-
Capture the page png · full page
-
POST to your endpoint send_to: webhook
Four handlers worth writing
Each of these is one config plus a few lines in your own service. No queue to run and nothing to poll.
-
POST /hooks/pricing
A pricing table you own
Write a row every time a competitor's price element moves, with the image link beside it, and let your own BI tool chart the history.
hourly · watch .plan-price -
POST /hooks/incidents
Open the ticket automatically
When a supplier's status page changes wording, your handler files the ticket, attaches the capture, and pages whoever is on call.
every 5 min · watch .status -
POST /hooks/visual-qa
Visual checks after a deploy
Capture the pages that matter on a schedule, post them to your pipeline, and archive them next to the release they belong to.
daily · 12 configs -
POST /hooks/agent-memory
Give an agent a memory
Let the handler store each capture and change record so an agent can ask what a page looked like last month instead of guessing.
on change · stored by config id
Webhooks are not a developer tier here.
This list is the same on the free plan and the largest one. The only number that changes between plans is how many screenshots you take.
Capture
5 INCLUDED- Multi-step capture Log in, click, fill forms, and wait for elements before the shutter fires.
- Scheduled screenshots Recurring captures from every five minutes to once a year.
- Real Locations Capture through a proxy network in 150+ countries, US states, and cities.
- Ad, tracker, and banner removal Strip cookie consent walls, ads, and third-party scripts before capture.
- CSS and JS injection Restyle or manipulate the page before the capture is taken.
Understand
4 INCLUDED- AI analysis Ask a question of any capture and get a written answer back with the image.
- Custom HTML screenshots Render email, private spreadsheet rows, or API payloads into an image.
- HTML extraction Pull fully rendered HTML from JavaScript-built pages, whole page or one selector.
- Website domain research Extract structured fields across a list of domains, as JSON or CSV.
Watch
3 INCLUDED- Change notifications Watch a selector and get a webhook, Slack message, or Zap when it moves.
- Archived history Every capture is kept and listable through the API, with dated thumbnails.
- CDN embed URLs A permanent link that always shows the latest capture, no API key exposed.
Deliver
3 INCLUDED- Every integration Zapier, Make, n8n, Power Automate, Slack, Dropbox, webhooks, Chrome, and MCP.
- Full REST API Every option is a parameter, with NPM, PyPI, and RubyGem packages.
- Custom headers and cookies Send your own headers, cookies, user agent, language, and time zone.
Screenshot webhooks, answered.
How do I send a screenshot to a webhook?
Create a screenshot config with the page URL and an interval, paste your endpoint into the webhook field, and save. The first capture runs immediately so you can watch the request arrive. From the API it is one POST to /screenshot_configs with the webhook URL set.
Is the webhook integration included on the free plan?
Yes. Webhooks, and every other integration, are on every plan including the free one. There are no connector tiers, no per-destination fees, and no separate developer plan. The free plan is 25 screenshots a month with no credit card and no phone number.
What does the request body look like?
A POST with a JSON body. A capture delivery carries screenshot_config_id, the page url, the screenshot id, direct_link and direct_thumbnail_link, embed_url, taken_at and taken_at_timestamp, your custom_title and custom_description, an ai object with the prompt and the written answer, is_proxy_screenshot and proxy_server, bandwidth_in_bytes, and job_id.
Is the change-alert request the same shape?
No, and this catches people out. A change notification is its own POST with its own body: id, screenshot_configuration_id, previous_value, retrieved_value, the config that fired it including the watched selector, and sent_at. Write the handler for both shapes, or point them at two endpoints.
Can it POST only when the page changes?
Yes. Add a Change Notification step, point it at a CSS selector, and give it a webhook URL. Each capture is compared with the one before it and the request only fires when that selector moves. It is a separate delivery from the per-capture webhook, so you can run either on its own or both together.
Do I need Zapier, Make, or n8n as well?
No. A custom webhook is a native destination, so nothing sits between the capture and your endpoint. Reach for a platform when you want the capture to fan out into apps we do not connect to directly, or when someone non-technical needs to edit the flow.
What happens if my endpoint is down when a capture fires?
The capture itself is not lost. Every capture is stored here while the account is active, listable through the API with dated thumbnails, and reachable through a permanent embed URL, so a missed delivery can be reconciled from the archive. Change notifications are listable through the API after the fact as well. For the exact delivery and retry behaviour, check the webhook guide or ask support before you depend on it.
Can one capture post to a webhook and somewhere else too?
Yes. A single config can POST to your endpoint, message a Slack channel, and save the file to Dropbox on the same run, and the capture is still filed in the archive. Delivery is a list, not a single choice.