Changelog
Every shipped feature, endpoint, and SDK release. Newest first.
v1.17.0
2026-10-10
Current
A free tool with no signup, and the renderer can no longer be aimed at a private network
New: capture a page without an account
- getsnap.dev/tools/screenshot.html — paste a URL, get a PNG. No account, no card, no extension.
- Six successful captures an hour per visitor. Only successful ones count: a typo, an unreachable site or a blocked address costs you nothing, and repeat URLs are served from cache.
- Fixed at 1280×720 PNG with no options. Everything else — full page, PDF, video, custom viewports, authenticated pages, scheduling, batch — stays in the API, where the free tier is 100 captures a month.
Security: capture URLs are checked against their resolved address
- Until now only the URL's syntax was validated, which accepts anything the URL parser understands. The renderer could be pointed at
file://, atlocalhost, or at a private or link-local address including the cloud metadata endpoint. - Every capture URL is now resolved and judged by the addresses it points to, not by how the hostname reads — a public name resolving to
127.0.0.1is refused. Redirects are re-checked, because a redirect is a second navigation. - Behaviour change: capturing an intranet address directly now returns
400 url_not_allowed. It was never a supported path — it worked only because the server could route there. To reach a private network, use a proxy.
Fixed
- Rejected requests no longer reach route handlers. Authentication replied correctly and the handler ran anyway, which showed up as a status code that disagreed with its own response body on
POST /v1/render-link. Traced to a framework behaviour, reported upstream, and written up. - Horizontal scrolling on phones. Several pages were wider than the viewport, which pushed the centred content off to one side — worst on the homepage, where a decorative element extended 115px past the left edge.
- Links inside paragraphs and lists are underlined again, so they are not distinguished from surrounding text by colour alone.
- The homepage no longer downloads all six demo captures on load. It fetches the visible one and the next, which cut about 1.2MB from first load.
v1.16.0
2026-10-09
Breaking
API keys are stored hashed, and render links are signed with a server-held secret
We no longer store your API key
- Only a SHA-256 hash of your key is kept. It is enough to recognise the key you send, and cannot be turned back into the key.
- Your key is shown once — in the
POST /v1/signupresponse and in your welcome email. Nobody, including us, can retrieve it afterwards. Lost it? Email support@getsnap.dev and we will revoke it and issue a new one. - Why it matters beyond the obvious: the database, and every nightly backup of it, previously contained live credentials. Now they contain hashes.
- SHA-256 rather than a password hash is deliberate. Slow hashes exist to defend low-entropy human-chosen secrets against offline guessing; an API key is 32 random characters, so there is nothing to guess, and a slow hash would be paid on every authenticated request.
- Nothing to do. Existing keys keep working exactly as before.
Breaking: render links signed before this release stop working
- Regenerate them with
POST /v1/render-link. The URL format is unchanged — stillkey=pk_…plus a signature — so nothing in your code needs to change. - v1.15.0 moved the secret out of the URL but still keyed the signature with a value stored on your account row. Anyone able to read the database, or a backup of it, could therefore mint valid links for any account and burn its quota.
- The signature is now keyed by a secret held only in the server environment, combined with your account. Reading the database no longer yields anything that can sign.
- There is deliberately no grace period accepting old signatures: honouring them would preserve exactly the weakness this removes.
Fixed
- Rejected requests no longer reach route handlers. Replying from an authentication hook stopped halting the request chain once the app registered a second
onSendhook — a Fastify behaviour we reproduced in isolation and reported upstream as fastify/fastify#7090. Visible symptom was a status code that disagreed with its response body onPOST /v1/render-link. - Four unhandled promise rejections that could terminate the API process: a welcome email sent without a catch, an async shutdown handler, an async screencast frame listener, and a selector failure that discarded its original error.
- The API no longer serves the marketing site, so
api.getsnap.devcan no longer answer with a duplicate of a page that belongs ongetsnap.dev.
v1.15.0
2026-10-05
Breaking
Signed render links no longer carry your API key, plus a correctness sweep
Breaking: render links use a public handle
POST /v1/render-linknow returns a URL containingkey=pk_…, a non-secret handle for your account, instead ofapi_key_id=sk_live_….- Links generated before this release stop working. Regenerate them. The change is deliberate rather than incidental:
/v1/render-linkexists so you can put a capture in an<img>tag without exposing a key, and it was doing the opposite — publishing the secret into page source, Referer headers, CDN logs and ours. Because the signature is HMAC'd with that same secret, anyone holding one link could mint any other. - The signature is still keyed by your secret, so only you can create a valid link. The secret simply never leaves the server now.
- Every account gained a
public_id, backfilled automatically. Nothing to do beyond regenerating links.
Fixed
- Scheduled captures that omitted
formatfailed on every run, withpage.screenshot: options.quality is unsupported for the png screenshots. A schedule created as{"url": "…"}— the obvious thing to create — never once succeeded. The worker now applies the same defaults a direct/v1/screenshotcall gets. - Team members could be charged for nothing.
/v1/upgradehad no notion of teams, so a member of a free team could buy Starter, have it charged, and see no change — because effective limits come from the team row. It now returns409 team_billing_requiredand points owners at/v1/teams/:id/checkout. - Cache keys ignored options that change the capture.
lazy_load,wait_for_selectorandwait_for_network_idlewere absent, so a request asking forlazy_loadcould be served an image taken without it. Also addedfail_if_selector_missingand proxy credentials. Existing cache entries are unaffected. - A missing
x-api-keyreturned400describing the request body, and on routes with required body fields it reported those instead, hiding the auth failure entirely. Now401 unauthorizedwith a message naming the header. - Validation errors name the part that failed —
Invalid request headers/querystring/params— rather than always claimingbody. - Favicon corners rendered as white pixels on dark browser tabs; the raster copies had been flattened without an alpha channel.
Changed
POST /v1/signupreturnsverification_required, and its message no longer claims the key is ready to use. With email verification enabled the key returns403 email_not_verifieduntil the activation link is clicked, which the response and the docs now both say.- Stripe Checkout metadata carries your public handle instead of your live API key.
usage_logrows are deleted automatically after 90 days, which the privacy policy had always claimed and nothing enforced.- Logs redact any
sk_live_/sk_test_token before writing.
Added
captures_totalis now actually emitted on/v1/metrics, labelled bykind,statusandcache. It had been declared and never incremented, so the counter read as a flat zero./v1/upgrade,/v1/billingand/v1/signupdocument their full response set in the OpenAPI spec, including the new409.- Per-language quick starts for Python, Node.js, PHP, Ruby, Go, Java and C#, and hosted-vs-self-hosted comparisons for Puppeteer, Playwright, Selenium and html2canvas.
v1.14.0
2026-09-20
Minor
Runtime Discord alerting + full-site a11y sweep + docs completeness
Runtime alerts (Discord)
- New
src/services/alerts.tsservice fires color-coded Discord embeds for three classes of prod incident:- Stripe webhook signature failure - 1h Redis dedup. Fires when someone posts to
/v1/webhook/stripewith a missing / rotated / spoofed signature header. Muted so a bot sniffing the endpoint can't spam the channel. - API 5xx response - 10min Redis dedup (env-tunable via
ALERT_5XX_DEDUP_SECONDS). Fires on the first 5xx per rolling window with request_id + method + path + error so on-call can grep the droplet logs. - Cron script fatal error - no dedup.
sendDrips/backupDb/restoreDball alert on top-level exceptions. The shell wrappers (hourly-drip.sh,daily-backup.sh) also POST to Discord on non-zero exit to catchdocker exec-level failures the Node script can't self-report.
- Stripe webhook signature failure - 1h Redis dedup. Fires when someone posts to
- Wired via Fastify
onSendhook (catches all 5xx including explicitreply.code(500)from route handlers, not just uncaught exceptions). - All alerts are fire-and-forget from the call site; a broken webhook or Redis outage cannot mask the underlying error being reported. Fail-open on Redis errors (prefer noisy to silent). No retry on Discord POST (5s timeout, drop over back-up).
- 10 new tests covering dedup + payload shape + failure resilience. See
ops/ALERTS.mdfor the triage runbook.
Deploy notifications (GitHub Actions)
- New "Notify Discord" step in
.github/workflows/deploy.ymlfires for every deploy (success / failure / cancelled) with color-coded embeds. Failure variant says "auto-rolled back"; title links to the workflow run URL for one-click log access. - CI failures ping Discord too now (extension of the deploy pattern to
.github/workflows/ci.yml).
Full-site WCAG 2.1 AA sweep
- Audited all 26 user-facing pages via the
/v1/auditendpoint (dogfooding). Found 18+ violations across 8 pages; fixed all of them. - Landmark fixes:
<main>added to 11 pages (changelog + 10 blog posts + docs/playground). Fixeslandmark-one-main+regionviolations that flagged 20-30 nodes each. - Heading order: footer
h4→h3on landing; release-bodyh4→h3on changelog; "Pricing Comparison"h3→h2on all 4 compare pages. - 50 unassociated
<label>tags on/docs/playground.htmlgainedfor=attributes. - Color-contrast: every
color:var(--primary)used as text bumped tovar(--primary-text).method.deletegained--red-light. - Link-in-text-block: global underline rules for
.footer-links a,.article p/li/td a, changelog paragraph links,.docs-content h3 a. - Scrollable-region-focusable: new
landing/a11y.js(250B gz) referenced from all 26 pages addstabindex="0"to every<pre>so keyboard users can arrow-scroll code blocks. - Result: 0 violations across all 26 pages.
Documentation completeness
README.mdendpoints table went from 19 rows to 30, grouped into 8 categories.- /docs/api-reference.html gained a "Complete endpoint index" section listing every operation with links to detailed sections or the interactive Swagger UI.
- New blog post: Team-owned Stripe subscriptions in 3 endpoints - case study of shipping v1.13 team billing without a per-seat model.
- New runbook:
ops/ALERTS.mdwith per-event triage playbook + Redis DEL commands for forcing re-fire during triage.
SDKs (getsnap@1.14.0)
- Python: added
__version__module attribute (was missing since v1.0). Nowgetsnap.__version__returns"1.14.0". - No API surface changes - identical to 1.13.0 for both npm + PyPI. Bump is for cache invalidation + documentation timestamp.
Config
DISCORD_ALERT_WEBHOOK_URL+ALERT_5XX_DEDUP_SECONDSadded to.env.exampleand documented insrc/config.tszod schema.
Regression check
- Test suite: 254 → 264 tests, all passing (10 new alert tests).
- Live smoke test: all 12 API paths return expected shapes; auth wall correct; team billing endpoints route through
metadata.team_idcorrectly (verified via direct Stripe API subscription create with metadata + observing team row update). - All 25 pages audited: 0 WCAG violations.
v1.13.0
2026-09-20
Minor
Team-owned Stripe subscriptions (billing Phase 2)
Team billing
- Teams now have their own Stripe subscription, decoupled from the creator's personal billing. Previously, at team-creation time the team copied the creator's
stripe_customer_id/stripe_subscription_idand any downstream Stripe events only ever touched the creator'sapi_keysrow, never the team — the team's plan silently drifted whenever the creator up/downgraded. - The owner now runs
POST /v1/teams/:id/checkoutwhich creates a fresh Stripe customer for the team, launches Checkout withmetadata.team_idstamped on the resulting subscription, and returns the checkout URL. New team subs get a 7-day trial withmissing_payment_method: cancel. - Downstream webhook events (
customer.subscription.created / updated / deleted / trial_will_end) checksubscription.metadata.team_idand route to the team row via newupdateTeamPlan/downgradeTeamToFreeservice functions. Redis cache is punched for every attached api_key so members see the new limits instantly, not on TTL.
API endpoints
POST /v1/teams/:id/checkout— owner-only. Body{plan: starter | pro | business}. Returns{url, type: "checkout" | "portal"}; if the team already has an active sub, punts to billing portal.POST /v1/teams/:id/portal— owner-only. Stripe billing portal for the team's subscription. Returns 400 if no subscription exists yet.GET /v1/teams/:id/billing— any team member. Returns plan, limits,subscription_status(active / trialing / past_due / canceled / unpaid / incomplete / null),current_period_end,cancel_at_period_end,has_team_subscription.- Total endpoints in Swagger: 27 → 30. Team paths: 7 → 10 (11 operations).
DELETE /v1/teams/:idnow also cancels the team's Stripe subscription before dropping the DB row (fire-and-forget with error logging) so owners aren't charged after dissolving.
Schema migrations
- Three new columns on
teamsvia idempotentALTER TABLE:subscription_status TEXT,current_period_end TEXT,cancel_at_period_end INTEGER DEFAULT 0. POST /v1/teamsno longer copies the creator'sstripe_customer_idorstripe_subscription_id. New teams start with those fields empty; existing teams keep whatever inherited values they already have (owner can re-run/checkoutto migrate).
SDKs
getsnap@1.13.0on npm and PyPI. Node:snap.teams.checkout(teamId, {plan}),snap.teams.portal(teamId),snap.teams.billing(teamId). Python:snap.create_team_checkout(team_id, plan),snap.get_team_billing_portal(team_id),snap.get_team_billing(team_id).- CLI:
getsnap teams checkout <team_id> <plan>,getsnap teams portal <team_id>,getsnap teams billing <team_id>.
Regression check
- Test suite: 246 → 254 tests, all passing. 8 new tests cover metadata-based routing, empty-metadata fallthrough,
subscription_details.metadatafallback path, team dedup, owner-email lookup for trial_will_end. - Live end-to-end smoke: created a test team → ran
/checkout(freshcus_...created with team metadata) → directly created a subscription via Stripe API withmetadata.team_id→ verified webhook fired and updatedteams.plan,monthly_limit,rate_limit_per_min,subscription_status,stripe_subscription_id. Cancelled the sub → team status transitioned correctly.
v1.12.0
2026-09-20
Minor
Team lifecycle: transfer ownership + dissolve
API
POST /v1/teams/:id/transfer-ownership— owner promotes a member to owner and gets demoted to admin. Every team has exactly one owner at all times; this is the only way to change ownership without dissolving. Body:{ new_owner_user_id }.DELETE /v1/teams/:id— owner permanently dissolves the team, detaches every member's api_key back to their personal plan, revokes outstanding invites, drops the team'smonthly_usagerow. Returns{ detached_key_count, invites_revoked }. One-way action, no undo.- Total endpoints in Swagger: 25 → 27. Team routes now cover the full lifecycle owner → admin transitions without support tickets.
Usage rollover on team creation
- When a solo user creates a team, their current-month personal
monthly_usagerow is now copied into the team's counter as part of the create transaction. Previously usage reset to zero when a solo Pro user made a team on the 20th of the month, effectively giving them a fresh 25,000 quota. Fixed.
SDKs
getsnap@1.12.0on npm and PyPI. Node:snap.teams.transferOwnership(teamId, { new_owner_user_id })+snap.teams.dissolve(teamId). Python:snap.transfer_team_ownership(team_id, new_owner_user_id)+snap.dissolve_team(team_id).- CLI:
getsnap teams transfer <team_id> <new_owner_user_id>+getsnap teams dissolve <team_id> --yes. Dissolve requires an explicit--yesflag because it's irreversible.
Docs
- /docs/teams.html and /docs/referrals.html are now full reference documentation (previously just interactive dashboards). Teams doc gained a permissions matrix, all 8 endpoint references with curl / Node / Python / CLI samples, a data model, and a 10-question FAQ. Referrals doc gained reward-mechanics tables, cookie attribution deep-dive, anti-abuse rules, and an expanded 8-question FAQ.
v1.11.0
2026-09-20
Patch
SDK team methods + WCAG AA fix
SDKs
getsnap@1.11.0on npm and PyPI — adds thesnap.teams.*namespace (Node) /snap.create_team()et al. (Python) wrapping all six team endpoints with typed request / response shapes.- CLI adds a
getsnap teamsfamily:list,create,members,invite,accept,leave. Terminal-friendly output; all subcommands accept--jsonfor machine-readable output.
Accessibility
- Ran our own
POST /v1/auditagainst getsnap.dev and found 60 WCAG AA color-contrast violations, all stemming from three color patterns (primary-on-dark text, white-on-primary buttons, red 'no' cells in the comparison table). - Fixed by splitting
--primaryinto two roles:--primary(#4f46e5) for backgrounds and borders where white text sits on top;--primary-text(#a5b4fc) for text on dark backgrounds. Plus--red-light(#fca5a5) for the comparison table crosses. - Re-audit: 60 → 0 violations. Full WCAG AA sweep now returns zero total violations.
Bug fixes
- Team-member api_keys were returning
500 FOREIGN KEY constraint failedon every capture becausemonthly_usage.api_key_idhad a FK toapi_keys.id, but the teams refactor started routing usage under the team ID (which doesn't exist in api_keys). Fix: inline migration drops the FK constraint (table-swap since SQLite has no ALTER DROP CONSTRAINT).
v1.10.0
2026-09-20
Minor
Team accounts
Six new endpoints
POST /v1/teamscreates a team, hoists your current plan/quota/rate onto it, and attaches your api_keyGET /v1/teamslists teams you belong to with your role in eachGET /v1/teams/:id/memberslists the roster with emails and rolesPOST /v1/teams/:id/invitesinvites a member by email (owner/admin only). Fires a branded invite email with a single-use link that expires in 7 days.POST /v1/teams/acceptconsumes an invite token, adds the caller to the team, and attaches their api_key to the shared planPOST /v1/teams/:id/leavelets non-owners leave the team; api_keys detach and fall back to personal limits
How the shared pool works
- All requests from any team member's api_key deduct from ONE shared monthly quota and one 60-second rate-limit window.
- Individual api_key logging still tracks each user's usage inside
usage_log, so you can see who made what. - Team members keep their own api_keys — no key sharing, no unrevealed secrets.
- Solo users are unaffected:
api_keys.team_iddefaults to NULL and everything works as before.
Roles
owner— created the team. Cannot leave via /leave (transfer or dissolve instead). Every team has exactly one.admin— can invite / remove members, cannot dissolve.member— can view team info and use the shared quota.
Anti-abuse
- Invites verify caller's email against the invited email before accepting
- Cannot re-invite an existing member (returns
409 already_member) - Owners cannot leave via /leave endpoint
- Role escalation via double-invite is blocked (existing membership takes precedence)
v1.9.0
2026-09-20
Minor
Accessibility audit endpoint
New endpoint
POST /v1/auditruns axe-core against any URL and returns categorised WCAG 2.0 / 2.1 / 2.2 violations grouped by impact (critical,serious,moderate,minor).- Every violation includes the rule ID, plain-English description, WCAG success criterion tags, and the failing HTML elements with CSS selectors so you can jump straight to the fix in your codebase.
- Optional
tagsfilter (wcag2aa,wcag21aa,wcag22aa,section508,ACT, etc.) narrows the audit to a specific conformance level. - Optional
rulesfilter (e.g.color-contrast,label,region) runs only specific axe rules for targeted CI gates. - Optional
include_screenshotflag captures a viewport screenshot alongside the report (+1 credit) so you have visual context in review.
CLI
- New
getsnap audit <url>command with a color-coded, terminal-friendly summary. Shows impact tier counts, top violations with rule ID + help + deque.university docs link, and a status marker (!!,!,*,OK) at the start of each line for CI log skimming.
SDKs
getsnap@1.10.0on npm and PyPI — adds typedaudit()methods with fullAuditOptions/AuditResponse/AxeViolationshapes.
Pricing
- 1 credit per audit + 1 more if you request a screenshot. No cache (DOM state is per-invocation) — but audits are 3-5x cheaper than a full capture on average since we skip the heavy image encoding.
v1.8.0
2026-09-20
Minor
Visual regression diff + CLI
New endpoint
POST /v1/diffcaptures two URLs at identical viewport dimensions, compares them pixel-by-pixel with pixelmatch, and returns a similarity score (0-100) plus three CDN URLs: before, after, and a red-overlay diff image.- Both captures run in parallel to halve latency. Cache HITs on either side reduce the credit charge - a diff of two pages already in cache costs 0 credits.
- Per-side overrides for
wait_for_selector,extra_delay_ms, andcsshandle sites where each side settles at different rates. - pixelmatch tunables exposed:
threshold(0-1 YIQ sensitivity),anti_alias,include_aa. Sensible defaults for typical marketing / product pages.
CLI
- Every SDK install now ships a
getsnapexecutable. No code required for one-off captures:
export GETSNAP_KEY=sk_live_... npx getsnap screenshot https://news.ycombinator.com --out hn.png npx getsnap pdf https://news.ycombinator.com --out hn.pdf --full-page npx getsnap og https://blog.example.com/post --out og.png npx getsnap video https://getsnap.dev --out demo.mp4 --duration 5 npx getsnap diff https://staging.mysite.com https://mysite.com npx getsnap usage npx getsnap referrals
Every capture flag from the SDK is available on the CLI: --full-page, --viewport 1440x900, --wait-for #hero, --dark-mode, --device mobile, etc. Full reference: npx getsnap --help.
SDKs
getsnap@1.9.0on npm and PyPI — adds typeddiff()methods with fullDiffOptions/DiffResponseshapes. Node package now ships the CLI too via abinentry.
v1.7.0
2026-09-20
Minor
Referral program
New endpoint
GET /v1/referralsreturns your unique referral code, ready-to-share URL, reward tier, and aggregated stats (total, paid, pending, reverted, lifetime earnings in cents).
How it works
- Every account gets an auto-generated 8-character referral code (uppercase alphanumeric, ambiguous chars like 0/O/1/I/L excluded).
- Share
https://getsnap.dev/?ref=YOURCODE. Landing JS captures the code into a 30-day first-party cookie on the referee's visit. - When the referee signs up, the code follows them into the request. On their first paid checkout (Starter, Pro, or Business), we attach a Stripe coupon giving them a discount on their first month.
- When the referee's first invoice is actually paid (i.e. they finished the trial + kept the plan), you get a $5.00 credit added to your Stripe customer balance.
Anti-abuse
- Self-referral (same email on both sides) is silently ignored.
- Each referee can only be attributed to one referrer, ever, enforced by a UNIQUE constraint at the DB level.
- Reward fires only on the FIRST successful invoice - trial cancellations or later refunds don't burn credits.
- Reward is idempotent under Stripe webhook retries and repeat-billing invoices for the same subscription.
v1.6.0
2026-09-20
Minor
OG image endpoint + billing hardening
New endpoint
- Added
POST /v1/og-image: a convenience wrapper over/v1/screenshotwith defaults tuned for social share images — 1200×630 PNG, hi-DPI (2x), ad and popup blocking on, waits for network idle. - Same billing as
/v1/screenshot: 1 credit per fresh capture, 0 for cache hits. - Supports
response_type: "url"(JSON with CDN URL, default) or"binary"(raw image bytes withCache-Control: public, max-age=604800, immutable).
Billing reliability
- Stripe webhook is now idempotent. Retried event deliveries (up to 3 days) no longer risk double-charging or double-applying subscription changes. Backed by a
stripe_eventsUNIQUE-constrained log with transactional rollback.
Onboarding
- Day-3 activation email lands automatically for new signups who haven't made an API call yet. Real paste-and-run code (curl, Node.js SDK, Python SDK) instead of a marketing pitch.
- Trial-ending email fires 72 hours before your Stripe trial expires. Includes a personalised billing portal link so you can update or cancel in one click. Wired via Stripe's
customer.subscription.trial_will_endwebhook.
SDKs
getsnap@1.6.0on npm and PyPI — addsogImage()/og_image()convenience methods with typedOgImageOptions/OgImageResponseshapes.
Ops
- Automated production deploys on git tag push.
.github/workflows/deploy.ymlchecks out the tag, rebuilds viadocker compose, polls/v1/healthfor up to 90 seconds, and auto-rolls back on failure. - Restore script now supports
RESTORE_TARGET_DB_PATHfor safe drills against a scratch path. Documented RTO/RPO inops/RESTORE-DRILL.md. - Load test script + baseline results at
ops/load-test.jsandops/LOAD-TEST-RESULTS.md. Verified production sustains 51 req/s at p95 <30ms with 12% memory utilisation.
v1.5.0
2026-09-20
Minor
Geo/proxy routing + full OpenAPI schemas
API
- New
proxy_url,proxy_username,proxy_password,proxy_countryfields on/v1/screenshotand/v1/video - Supports
http,https,socks5,socks5hschemes. Credentials stripped before handing to Playwright and SHA-256–truncated in cache keys. - Managed geo pool via
PROXY_URL_TEMPLATEenv var. When configured, clients passproxy_country: "US"and we substitute. - Optional
PROXY_ALLOWED_COUNTRIESallow-list restricts which ISO codes are accepted.
Docs
- Swagger UI at api.getsnap.dev/docs now shows the full request body (up to 74 parameters), response shape, and every error code for each endpoint. Previously it rendered barebones stubs.
- The interactive playground at /docs/playground exposes every Phase 6–11 field in collapsible sections: proxy, PDF metadata, extract_metadata, clip, slicing, blocking, emulation.
SDKs
getsnap@1.5.0on npm and PyPI — addsproxy_*fields toScreenshotOptionsandVideoOptions, plus HMAC verification example.
v1.4.0
2026-09-19
Minor
Scrolling video capture (MP4 + GIF)
New endpoint
POST /v1/videorecords a smoothly-scrolling capture as MP4 (libx264 + faststart, quality-driven CRF) or GIF (two-pass palettegen → paletteuse).- 10–30 fps, 2–30 second duration, viewport up to 1920×1080.
- Uses Chrome DevTools'
Page.screencastprotocol for high-fps frame sampling (not a screenshot loop) and ffmpeg for encoding. - Sync / async webhook / raw binary response modes. Async webhooks include a
video.completedevent. - Costs 1 credit per second of video (rounded up, min 1).
Infrastructure
- Dockerfile now installs
ffmpegalongsidecurl. - New
SCHEDULE_WORKER_CONCURRENCYenv var so cron-driven captures don't starve the sync pool.
SDKs
getsnap@1.4.0Node:client.video(),client.videoBinary(), fullVideoOptionstypes.getsnap@1.4.0Python:gs.video(**kwargs),gs.video_binary(**kwargs). First release published to PyPI.
v1.3.0
2026-09-20
Minor
Scheduled captures with HMAC-signed webhooks
New endpoints
POST /v1/schedules,GET /v1/schedules,GET /v1/schedules/:id,PATCH /v1/schedules/:id,DELETE /v1/schedules/:idPOST /v1/schedules/:id/run— trigger a schedule immediately for testingGET /v1/schedules/:id/runs— execution history (last 100 runs kept)
Design
- BullMQ repeatable jobs, Redis-backed, survive restarts via startup DB hydration
- IANA timezone support (validated with cron-parser)
- Optional
webhook_secrettriggers HMAC-SHA256 delivery signing:X-Webhook-Signature: sha256=<hex>andX-Webhook-Timestampso receivers can verify authenticity and defeat replay attacks
SDKs
- Node:
client.schedules.{create, list, get, update, remove, runNow, runs} - Python:
create_schedule / list_schedules / get_schedule / update_schedule / delete_schedule / run_schedule_now / list_schedule_runs
v1.2.0
2026-09-20
Minor
Feature parity blast: 30+ new screenshot options
Category A — new capture behaviour
clip: { x, y, width, height }— capture a specific rectangle of the viewport, overridingfull_pageandselectorfull_page_max_height— hard cap on infinite-scroll pagesfull_page_slices,full_page_slice_height,full_page_slice_overlap_height— split tall captures into overlapping strips for AI vision workflowsreduced_motionandmedia_type: "screen" | "print"emulationclick_accept— auto-click cookie-consent Accept buttons (OneTrust, Cookiebot, FundingChoices, generic heuristics)press_escape— dismiss modals via Escape keypressfail_if_selector_missing— hard-fail on missing selectors instead of silently ignoringattachment_name— setsContent-Dispositionon binary responsesAVIFas a first-class output format
Category B — PDF completeness
- Paper sizes: A0–A6, Letter, Legal, Tabloid, Ledger
- Orientation, per-side margins (top/right/bottom/left),
pdf_background,pdf_scale - Templated
pdf_header/pdf_footer(Chromium format:<span class="pageNumber">,totalPages,title,url,date) - Page range filter (
pdf_page_range: "1-5,8") - Document metadata:
pdf_title,pdf_subject,pdf_author,pdf_keywords,pdf_creator
Category C — extraction
extract_metadatareturns{title, favicon, open_graph, fonts, http_status, http_status_text, http_headers}in one field
Category D — request blocking
block_images,block_fonts,block_scripts,block_frames,block_urls(wildcard patterns),bypass_csp
Dependencies
sharpfor AVIF conversion + full-page slicingpdf-libfor PDF metadata post-processing
v1.1.0
2026-08-29
Minor
Rebrand: SnapAPI → getsnap.dev
- New brand across the entire stack: landing, docs, compare pages, SDKs, email templates, Swagger title, webhook User-Agent
- Class renamed
SnapAPI→GetSnapin the Node SDK; backwards-compat aliases preserved (SnapAPI,SnapAPIError) - New comparison landing table with 6 columns and honest research-based rows for ScreenshotAPI, Urlbox, ScreenshotOne, ApiFlash
- Four dedicated
/compare/*.htmldeep-dive pages - Node SDK
getsnappublished to npm (renamed from a legacy internal name)
v1.0.0
2026-08-15
Major
Initial public release
Core API
POST /v1/screenshot— screenshot or PDF of any URL or HTML stringPOST /v1/batch— up to 100 URLs in one requestPOST /v1/render-link+GET /v1/render— signed GET URLs for<img>embeddingGET /v1/usage,GET /v1/health
Capture features
- AI popup removal (90+ heuristic selectors), ad blocking
- Full-page, mobile/tablet emulation, dark mode, Retina/HiDPI
- Element capture via selector, transparent PNG
- Wait-for-selector, lazy-load auto-scroll, click/scroll before capture
- Custom CSS/JS injection, cookies, headers, locale, timezone, user agent
- Text and HTML extraction
- Async webhooks, S3 upload to user-owned buckets
Platform
- Fastify + Playwright + Redis + SQLite stack on a single droplet
- DigitalOcean Spaces CDN with hash-based caching
- Sliding-window rate limiting, monthly quota enforcement
- Stripe metered billing with 4 plans (Free / Starter / Pro / Business)