Keep your screenshot product. Move one render call.

Use Latchshot behind an existing screenshot API when the upstream job is one bounded capture of one public URL returning PNG or JPEG bytes. Your application remains responsible for customer authentication, cache keys, object storage, CDN URLs, plan quotas, billing, and support.

Screenshot-product backend pattern · Direct image bytes · Updated July 20, 2026

The commercial boundary

Latchshot renders. Your product stays the product.

The integration belongs behind your trusted server. A customer calls your API with your credential. Your application authorizes that customer, checks its own cache and plan, asks Latchshot for uncached public-page bytes, stores the artifact, records usage, and returns its own response contract.

Your customerYour endpoint, API key, request shape, and price
Your backendAuthorize, cache, quota, store, meter, and return
LatchshotOne public URL → synchronous image bytes
Only the bounded render step moves. Customer identity, artifacts, delivery, billing, and product support remain in your system.

Good backend fit

Direct bytes are already the seam.

Input
One public HTTP or HTTPS URL on port 80 or 443
Output
Synchronous PNG or JPEG bytes your code already stores or returns
Controls
Bounded viewport, full page, format, quality, wait, delay, and cleanup

Keep another backend

The provider owns more than rendering.

Delivery
Hosted or signed image URLs, async jobs, webhooks, or provider storage are required
Page state
Authentication, cookies, arbitrary headers, scripts, clicks, custom or multi-step scrolling, or selectors are required
Reach
Private networks, non-standard ports, proxies, regions, or anti-bot behavior are required
No white-label or revenue claim.

This page documents a technical architecture. It does not claim that any screenshot product uses Latchshot, that the plans guarantee margin, or that Latchshot replaces your obligations to customers. Review your own terms, support load, tax, refunds, storage, transfer, and availability commitments.

Return a validated buffer from one server-only function.

Keep the Latchshot key in a server-only environment variable. Bound the caller deadline, require the expected content type, reject oversized artifacts, and preserve provider diagnostics without logging the credential or target URL.

TypeScript / Node.jspublic URL → validated PNG buffer
const maxArtifactBytes = 15 * 1024 * 1024;

export async function renderPublicPage(url: string) {
  const response = await fetch("https://latchshot.fly.dev/v1/render", {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.LATCHSHOT_API_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      url,
      kind: "screenshot",
      format: "png",
      width: 1440,
      height: 900,
      fullPage: false,
      waitUntil: "domcontentloaded",
      timeout: 20_000,
    }),
    signal: AbortSignal.timeout(25_000),
  });

  if (!response.ok) {
    const detail = (await response.text()).slice(0, 500);
    throw new Error(`render failed (${response.status}): ${detail}`);
  }

  const contentType = response.headers.get("content-type") ?? "";
  if (!contentType.startsWith("image/png")) {
    throw new Error(`unexpected artifact type: ${contentType || "missing"}`);
  }

  const bytes = Buffer.from(await response.arrayBuffer());
  if (bytes.length === 0 || bytes.length > maxArtifactBytes) {
    throw new Error(`artifact size outside caller limit: ${bytes.length}`);
  }

  return {
    bytes,
    renderMs: response.headers.get("x-latchshot-render-ms"),
    remaining: response.headers.get("x-quota-remaining"),
  };
}

The response body is the artifact, not a hosted URL. Keep your existing object-storage upload, CDN URL, base64 conversion, or binary API response after this function.

Authorize before rendering.

Do not expose this function as an unauthenticated proxy. Validate your customer credential, enforce your plan and rate limit, normalize the target contract, and check your cache before calling the upstream renderer.

Keep six product decisions on your side.

  1. Customer authentication: accept only your own API key or session. Never forward a customer-supplied Latchshot key.
  2. Request policy: define formats, dimensions, deadlines, and target rules that your product supports. A smaller public contract is easier to price and support.
  3. Cache identity: hash every output-affecting option, not just the URL. Decide freshness and invalidation explicitly.
  4. Artifact ownership: upload validated bytes to your storage before returning a hosted URL. Set retention, content type, and access policy there.
  5. Usage accounting: count customer usage according to your contract. Record a successful uncached render only after the artifact is safely committed.
  6. Billing and support: your plan, checkout, tax, refunds, service promises, and customer communication remain separate from Latchshot.
ResponsibilityYour productLatchshot
Customer identity and entitlementOwns and enforcesNot aware of your customer
Render targetSubmits a permitted public URLRevalidates public-network policy
ArtifactStores, names, retains, and deliversReturns transient image bytes
Customer quotaDefines and recordsEnforces your upstream key allowance
PaymentOwns checkout and settlementNo automatic customer or overage charge
Support contractOwns customer-facing promisesExposes bounded API behavior and diagnostics

On a narrow screen, scroll the responsibility table horizontally.

Model cost per successful uncached render.

Latchshot counts successful direct renders. Failed direct renders do not consume monthly quota, and automatic overages are disabled. Cache hits in your application do not reach Latchshot. At full included usage, the current list-price input is:

PlanSuccessful renders / UTC monthMonthly pricePrice ÷ full allowance
Free100$0$0
Launch2,500$19$0.0076
Build12,000$49about $0.0041
Scale50,000$149about $0.0030

On a narrow screen, scroll the plan table horizontally.

Unit modeluse measured values
eligible_uncached_successes
  = customer_successes × (1 - measured_cache_hit_rate)

gross_margin
  = customer_revenue
  - upstream_plan_cost
  - storage_and_transfer
  - other_variable_compute
  - refunds_and_support_cost

The full-allowance division is not a metered rate, a guaranteed saving, or an overage price. Underused allowance raises the effective cost. Jobs outside Latchshot's boundary still need another provider and belong in the same model.

Do not collapse upstream failure into a customer 500.

Map failures deliberately. Preserve a short upstream code and request ID in internal diagnostics, but return your own stable error schema without exposing a credential, raw authorization header, customer URL, or unrestricted provider body.

  • Permanent until input changes: 400, 401, 403, unsupported target, or option-validation failures.
  • Bounded retry candidates: 429, 502, 503, and 504, only while the caller deadline still has room.
  • Quota exhausted: stop upstream work and return your product's explicit capacity state. Do not silently bill an overage.
  • Artifact rejected: do not store or count a zero-byte, wrong-type, or caller-oversize response.

A fallback provider is useful only if it preserves the same product contract. Exercise it under a known upstream failure before depending on it.

Keep the current provider when it owns the workflow.

Latchshot is not a browser-session platform or hosted-artifact service. A split-provider backend is the correct design when only part of your traffic fits.

  • Direct browser embedding, query-string credentials, or customer-side capture.
  • Provider-hosted or signed image URLs, async jobs, callbacks, webhooks, or provider storage.
  • Cookies, login state, arbitrary headers, authenticated pages, private previews, or private networks.
  • Inline HTML, scripts, actions, clicks, typing, custom or multi-step scrolling, selectors, element clips, or multi-step navigation. Bounded full-page lazy-content activation is available separately through scrollPage.
  • CAPTCHA solving, stealth behavior, proxy rotation, browser regions, or anti-bot bypass.
  • Video, animated output, WebP, extraction, Lighthouse, or other non-render browser APIs.
Do not put sensitive scope in a public request.

A migration-fit issue or implementation-pilot request must use a safe public sample and non-secret contract details. Never include an API key, authorization header, private or signed URL, customer data, payment details, or sensitive artifact.

Prove one seam before moving traffic.

  1. Inventory the exact current provider request and mark every job that hits the stop list.
  2. Create a public acceptance set covering ordinary, font-heavy, animated, tall, consent-overlay, slow, and known-failure pages.
  3. Compare image dimensions, visible content, fonts, cleanup, bytes, and end-to-end time at identical settings.
  4. Unit-test authorization-before-render, cache hits, quota exhaustion, content-type rejection, size rejection, permanent errors, bounded retries, storage failure, and usage commit.
  5. Route a bounded share of eligible uncached jobs. Keep the former provider path until rollback is exercised.
Fit first. Implementation only after approval.

The $99 implementation pilot covers one eligible JavaScript, TypeScript, or Python backend call site in a public GitHub repository you control, with a focused patch, tests, approved public acceptance sample, and rollback note. API usage is separate. The request takes no payment and authorizes no work; the owner separately confirms scope, payment, and start.

Test the render seam, not the whole business.

Use the recurring Free allowance for representative public pages. Keep customer identity, artifacts, quotas, billing, and support in your product.

Get a free key