Screenshot API Bun Guide 2026

Integrate SnapAPI into Bun applications using Bun.serve(), native fetch, Bun.file streaming, SQLite caching, and the Elysia framework for high-performance screenshot services.

Get Your Free API Key

Why Bun Is a Compelling Runtime for Screenshot Services

Bun is the fastest JavaScript runtime available in 2026, outperforming Node.js by 3-4x on HTTP throughput benchmarks and starting up in milliseconds rather than hundreds of milliseconds. For screenshot services where low latency is critical — link preview generators, real-time OG image creation, and CI pipeline visual regression tools — Bun's performance characteristics translate directly to a better end-user experience. Bun is also a complete JavaScript toolkit: it includes a package manager (faster than npm and pnpm), a bundler, a test runner, and a TypeScript transpiler with zero configuration. This means your Bun screenshot service requires no build step, no Babel configuration, and no separate tool for each task in the development workflow.

This guide covers building a production-ready screenshot service with Bun, including Bun.serve() HTTP server, native fetch() for SnapAPI calls, SQLite-based response caching using bun:sqlite, Bun.file() for efficient binary streaming, and the Elysia framework for structured route handling with TypeScript validation.

Bun.serve() Screenshot HTTP Server

The simplest possible Bun screenshot service uses Bun.serve() directly with no framework dependencies:

// server.ts
const SNAPAPI_KEY = process.env.SNAPAPI_KEY!

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url)

    if (req.method === "POST" && url.pathname === "/screenshot") {
      const body = await req.json()
      if (!body.url) return Response.json({ error: "url required" }, { status: 400 })

      const res = await fetch("https://api.snapapi.pics/v1/screenshot", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-API-Key": SNAPAPI_KEY,
        },
        body: JSON.stringify(body),
      })

      if (!res.ok) return Response.json({ error: "capture failed" }, { status: res.status })
      return Response.json(await res.json())
    }

    return new Response("Not found", { status: 404 })
  },
})

console.log("Screenshot service running on :3000")

Start with: bun run server.ts. No transpilation step, no build command — Bun executes TypeScript directly. This server handles thousands of requests per second with minimal memory overhead.

SQLite Caching with bun:sqlite

Bun ships with a built-in SQLite driver (bun:sqlite) that is significantly faster than better-sqlite3 for Node.js. Use it to cache screenshot results without adding a Redis dependency:

import { Database } from "bun:sqlite"
import { createHash } from "crypto"

const db = new Database("screenshots.db")
db.run(`CREATE TABLE IF NOT EXISTS cache (
  key TEXT PRIMARY KEY,
  data TEXT NOT NULL,
  expires INTEGER NOT NULL
)`)

const TTL = 3600 // 1 hour in seconds

function getCached(params: object): any | null {
  const key = createHash("md5").update(JSON.stringify(params)).digest("hex")
  const now = Math.floor(Date.now() / 1000)
  const row = db.query("SELECT data FROM cache WHERE key = ? AND expires > ?").get(key, now)
  return row ? JSON.parse((row as any).data) : null
}

function setCache(params: object, data: object): void {
  const key = createHash("md5").update(JSON.stringify(params)).digest("hex")
  const expires = Math.floor(Date.now() / 1000) + TTL
  db.run("INSERT OR REPLACE INTO cache (key, data, expires) VALUES (?, ?, ?)",
    [key, JSON.stringify(data), expires])
}

async function screenshotCached(params: object) {
  const cached = getCached(params)
  if (cached) return cached

  const res = await fetch("https://api.snapapi.pics/v1/screenshot", {
    method: "POST",
    headers: { "X-API-Key": SNAPAPI_KEY, "Content-Type": "application/json" },
    body: JSON.stringify(params),
  })
  const data = await res.json()
  setCache(params, data)
  return data
}

PDF Streaming with Bun.file()

For PDF generation endpoints, stream the binary response directly to the client without buffering. Bun's native fetch() response body can be streamed directly:

if (req.method === "POST" && url.pathname === "/pdf") {
  const { url: targetUrl } = await req.json()
  const upstream = await fetch("https://api.snapapi.pics/v1/screenshot", {
    method: "POST",
    headers: { "X-API-Key": SNAPAPI_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ url: targetUrl, format: "pdf", full_page: true }),
  })
  return new Response(upstream.body, {
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": "attachment; filename=screenshot.pdf",
    },
  })
}

Elysia Framework Integration

Elysia is the premier web framework for Bun, featuring end-to-end TypeScript type inference, schema validation with TypeBox, and plugin architecture. Build a structured screenshot service with Elysia:

import { Elysia, t } from "elysia"

const schema = t.Object({
  url: t.String({ format: "uri" }),
  width: t.Optional(t.Number({ minimum: 320, maximum: 2560 })),
  height: t.Optional(t.Number({ minimum: 200 })),
  format: t.Optional(t.Union([t.Literal("png"), t.Literal("jpeg"), t.Literal("pdf")])),
  full_page: t.Optional(t.Boolean()),
})

new Elysia()
  .post("/screenshot", async ({ body }) => {
    const res = await fetch("https://api.snapapi.pics/v1/screenshot", {
      method: "POST",
      headers: { "X-API-Key": process.env.SNAPAPI_KEY!, "Content-Type": "application/json" },
      body: JSON.stringify(body),
    })
    if (!res.ok) throw new Error("Screenshot failed")
    return res.json()
  }, { body: schema })
  .listen(3000)

console.log("Elysia screenshot service running")

Bun Test Runner for Screenshot Services

Bun's built-in test runner is faster than Jest and requires zero configuration. Write tests for your screenshot service with mock fetch calls to avoid consuming API quota during CI runs. Use bun test to execute the suite — it runs TypeScript test files natively without any transpilation overhead. Get your free SnapAPI key at snapapi.pics and start building your Bun screenshot service today. The combination of Bun's performance and SnapAPI's managed browser infrastructure gives you a screenshot service that is both fast and operationally simple to maintain in production.

Bun Performance Benchmarks for Screenshot Services

Bun's performance advantage over Node.js is most visible in high-concurrency scenarios — exactly the pattern that screenshot services encounter when many users trigger captures simultaneously. In benchmarks using autocannon for load testing, a Bun.serve() screenshot proxy handling 50 concurrent connections achieves approximately 3.2x higher throughput than an equivalent Express.js server on Node.js 22, with 40% lower p99 latency. For screenshot services where each request involves an outbound HTTP call to SnapAPI (adding 1.5-3 seconds of latency), the Bun runtime overhead contribution is negligible — but the connection handling efficiency means Bun queues and dispatches concurrent screenshot requests more efficiently, reducing the latency spike when multiple users trigger captures simultaneously.

Memory usage is another area where Bun excels. A Bun screenshot service with the bun:sqlite cache layer uses approximately 45 MB of RAM at idle, compared to approximately 85 MB for an equivalent Node.js service with better-sqlite3. For teams deploying screenshot services on constrained infrastructure — small cloud VMs, container services with strict memory limits, or Raspberry Pi servers for internal tooling — Bun's memory efficiency translates directly to lower hosting costs.

Deploying Bun Screenshot Services to Production

Bun deploys to any Linux-based hosting environment. For containerized deployments, the official oven/bun Docker image is significantly smaller than node:alpine — around 90 MB versus 150 MB — reducing container registry storage costs and deployment times. A production Dockerfile for a Bun screenshot service looks like:

FROM oven/bun:1 AS base
WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

COPY . .

ENV NODE_ENV=production
EXPOSE 3000
CMD ["bun", "run", "server.ts"]

For Fly.io deployments, create a fly.toml that exposes port 3000 and set your SNAPAPI_KEY as a Fly secret using fly secrets set SNAPAPI_KEY=your_key. Fly deploys Bun containers to the geographic region closest to your users, minimizing network latency for the client-to-server leg of each screenshot request. Render, Railway, and DigitalOcean App Platform all support Bun natively as well — select Bun as the runtime in the platform settings and deploy with a standard git push workflow.

Bun Macros for Static Screenshot Configuration

Bun macros allow you to run JavaScript at bundle time and embed the results as constants in your application bundle. For screenshot services with a fixed set of monitored URLs — a competitor monitoring tool, a documentation QA suite, or a dashboard archive system — use a Bun macro to load the URL list from a JSON file at build time rather than at runtime. This eliminates the file system read overhead on each request and makes the URL list part of the compiled bundle for faster startup. Combined with Bun's sub-millisecond startup time, this pattern produces screenshot services that are ready to handle requests virtually instantaneously after process start — a significant advantage in serverless and edge deployment environments where cold start latency matters. Sign up at snapapi.pics to get your free API key and deploy your first Bun screenshot service today.

Choosing the Right Screenshot API: What to Look For

When evaluating screenshot APIs for production use, five criteria matter most. First is rendering accuracy: does the API use a real, up-to-date browser engine that executes JavaScript, loads web fonts, and renders CSS correctly? Lightweight HTTP-based screenshot tools that do not execute JavaScript fail on the majority of modern web pages. SnapAPI uses Chromium — the same engine powering Chrome and Edge — ensuring accurate rendering of even the most JavaScript-heavy applications.

Second is latency: for user-facing screenshot features, capture time directly affects perceived performance. SnapAPI returns results in 1.5 to 2.5 seconds for standard pages, and under 5 seconds for complex pages requiring JavaScript hydration and dynamic data loading. Third is reliability: a screenshot API that returns errors 5% of the time is a source of production incidents, not a production dependency. SnapAPI maintains a 99.9% monthly uptime SLA across all paid plans, with automatic retry and failover built into the API infrastructure. Fourth is scalability: your screenshot API should handle burst traffic — the Thursday afternoon spike, the launch day surge — without degrading. SnapAPI scales horizontally under load with no per-customer rate limit degradation. Fifth is developer experience: clear documentation, idiomatic SDK libraries, predictable error responses, and responsive support all reduce the friction of building and maintaining a screenshot integration. All five SnapAPI SDKs ship with comprehensive documentation, TypeScript definitions, and example code for the most common use cases. The REST API documentation at snapapi.pics/docs includes interactive examples, a live API explorer, and copy-pasteable curl commands for every endpoint. Sign up for free at snapapi.pics — no credit card, 200 captures per month, and full feature access from day one.