Generate screenshots, PDFs, and scrape web content from your Nuxt.js application — one HTTP call to SnapAPI, no Puppeteer, no browser infrastructure to manage.
Get Free API KeyNuxt 3's server/ directory gives you full-stack capability. Add a screenshot endpoint in seconds:
// server/api/screenshot.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const targetUrl = query.url as string
if (!targetUrl) throw createError({ statusCode: 400, message: 'Missing url param' })
const params = new URLSearchParams({
access_key: process.env.SNAPAPI_KEY!,
url: targetUrl,
format: 'png',
width: '1280',
full_page: 'false',
})
const res = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
if (!res.ok) throw createError({ statusCode: 502, message: 'Screenshot failed' })
const buf = Buffer.from(await res.arrayBuffer())
setHeader(event, 'Content-Type', 'image/png')
setHeader(event, 'Cache-Control', 'public, max-age=3600')
return buf
})
// composables/useScreenshot.ts
export function useScreenshot() {
const loading = ref(false)
const error = ref<string | null>(null)
async function capture(url: string, format: 'png' | 'pdf' = 'png') {
loading.value = true
error.value = null
try {
const res = await $fetch('/api/screenshot', { query: { url, format }, responseType: 'blob' })
return URL.createObjectURL(res as Blob)
} catch (e: any) {
error.value = e.message
return null
} finally {
loading.value = false
}
}
return { capture, loading, error }
}
<script setup lang="ts">
const { capture, loading } = useScreenshot()
const previewUrl = ref('')
async function takeScreenshot() {
const url = await capture('https://example.com')
if (url) previewUrl.value = url
}
</script>
<template>
<div>
<button @click="takeScreenshot" :disabled="loading">
{{ loading ? 'Capturing...' : 'Take Screenshot' }}
</button>
<img v-if="previewUrl" :src="previewUrl" alt="Screenshot preview" />
</div>
</template>
// server/api/pdf.get.ts
export default defineEventHandler(async (event) => {
const { url } = getQuery(event)
const params = new URLSearchParams({
access_key: process.env.SNAPAPI_KEY!,
url: url as string,
format: 'pdf',
pdf_format: 'A4',
full_page: 'true',
delay: '1000',
})
const res = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
const buf = Buffer.from(await res.arrayBuffer())
setHeader(event, 'Content-Type', 'application/pdf')
setHeader(event, 'Content-Disposition', 'attachment; filename=page.pdf')
return buf
})
Nuxt Content powers many documentation and blog sites. Auto-generate Open Graph images for every content page using SnapAPI at build time or via a dynamic route:
// server/api/og/[slug].get.ts
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, 'slug')
const templateUrl = encodeURIComponent(`https://yoursite.com/og-template/${slug}`)
const params = new URLSearchParams({
access_key: process.env.SNAPAPI_KEY!,
url: templateUrl,
format: 'png',
width: '1200',
height: '630',
})
const res = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
const buf = Buffer.from(await res.arrayBuffer())
setHeader(event, 'Content-Type', 'image/png')
setHeader(event, 'Cache-Control', 'public, max-age=86400, s-maxage=86400')
return buf
})
Add your SnapAPI key to .env and reference it via process.env in server routes. For Nuxt's runtime config, add it to nuxt.config.ts under runtimeConfig.snapapiKey (server-only — never expose under runtimeConfig.public).
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
snapapiKey: process.env.SNAPAPI_KEY,
// public: {} — never put API keys here
}
})
Nuxt 3's Nitro engine includes a storage abstraction. Cache screenshots in Redis, filesystem, or any KV store without adding extra packages:
// server/api/screenshot.get.ts (with Nitro cache)
export default defineCachedEventHandler(async (event) => {
const { url } = getQuery(event)
const params = new URLSearchParams({
access_key: process.env.SNAPAPI_KEY!,
url: url as string,
format: 'png',
width: '1280',
})
const res = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
return Buffer.from(await res.arrayBuffer())
}, {
maxAge: 60 * 60, // 1 hour cache
getKey: (event) => `snap:${getQuery(event).url}`,
})
Free tier: 200 captures/month. Starter: $19/month (5K). Pro: $79/month (50K). All plans include screenshots, PDFs, scrape, and extract endpoints. Sign up at snapapi.pics.
Nuxt 3's architecture — built on Nitro for server-side rendering and Vue 3 for the client — makes it one of the most capable full-stack frameworks for integrating external APIs like SnapAPI. Here are the patterns that production Nuxt apps use most.
A common use case is displaying live website previews in a gallery or list. The user adds a URL, your Nuxt app captures a screenshot via SnapAPI, stores it, and displays it as a thumbnail. Here's the full flow from Pinia store to component:
// stores/previews.ts
import { defineStore } from 'pinia'
export const usePreviewStore = defineStore('previews', {
state: () => ({ items: [] as Array<{ url: string; preview: string }> }),
actions: {
async addPreview(url: string) {
const { data } = await useFetch('/api/screenshot', { query: { url } })
const objectUrl = URL.createObjectURL(new Blob([data.value as ArrayBuffer]))
this.items.push({ url, preview: objectUrl })
}
}
})
When using Nuxt SSR, screenshots generated server-side can be embedded directly in the initial HTML response as base64 data URIs — eliminating the need for a separate client-side fetch. This gives instant first-paint for screenshot-heavy pages:
// pages/preview.vue
<script setup lang="ts">
const route = useRoute()
const { data: screenshot } = await useAsyncData('screenshot', async () => {
const params = new URLSearchParams({
access_key: useRuntimeConfig().snapapiKey,
url: route.query.url as string,
format: 'png',
width: '1280',
})
const res = await fetch(`https://api.snapapi.pics/v1/screenshot?${params}`)
const buf = Buffer.from(await res.arrayBuffer())
return `data:image/png;base64,${buf.toString('base64')}`
})
</script>
<template>
<img v-if="screenshot" :src="screenshot" alt="Preview" />
</template>
For event-driven use cases — capture a screenshot every time a new blog post is published — add a Nuxt server route that handles the webhook and queues a screenshot job:
// server/api/webhook/content.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const postUrl = `https://yoursite.com/blog/${body.slug}`
// Fire and forget — don't block the webhook response
fetch('https://api.snapapi.pics/v1/screenshot?' + new URLSearchParams({
access_key: process.env.SNAPAPI_KEY!,
url: postUrl,
format: 'png',
width: '1200',
height: '630',
})).then(r => r.arrayBuffer()).then(buf => {
// Upload to your storage
return uploadToR2(buf, `og/${body.slug}.png`)
}).catch(console.error)
return { ok: true }
})
Use Nuxt's built-in testing utilities to write integration tests for your screenshot routes without making real API calls:
// tests/screenshot.test.ts
import { describe, it, expect, vi } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'
describe('Screenshot API route', async () => {
await setup({ server: true })
it('returns 400 for missing url param', async () => {
const res = await $fetch('/api/screenshot', { ignoreResponseError: true })
expect(res.status).toBe(400)
})
})
Nuxt's Nitro engine adapts to any deployment target automatically. For Vercel, set SNAPAPI_KEY in the project environment variables. For Cloudflare Pages + Workers, add SNAPAPI_KEY as a secret via wrangler secret put. For self-hosted deployment with Node.js, set it in your systemd environment or Docker container env. The screenshot server routes run identically on all platforms since they use standard fetch with no native dependencies.
Does it work with Nuxt Layers? Yes. Place ScreenshotService in a shared layer and import it in any Nuxt app in your monorepo. Server routes defined in a layer are automatically available in host applications.
Can I use it with Nuxt UI? Yes. Nuxt UI components and SnapAPI are completely independent. Drop a screenshot preview into any Nuxt UI card or modal by binding the returned blob URL to an img src.
Is there a rate limit on concurrent requests? SnapAPI processes requests concurrently based on your plan's quota. For burst workloads, implement a request queue in your Nuxt server to avoid overwhelming the API and to stay within your monthly limit.
Ready to add screenshots to your Nuxt app? Sign up free at snapapi.pics — 200 captures/month, no credit card.
Key package: No additional packages needed — use the native fetch available in Nuxt server routes. API base URL: https://api.snapapi.pics/v1/screenshot for screenshots and PDFs, /v1/scrape for HTML scraping, /v1/extract for structured data. Auth: Pass your key as the access_key query parameter or as the X-Api-Key header. Response: Binary for PNG/WebP/PDF, JSON for scrape and extract. Timeout: Set Nitro's fetch timeout to at least 30 seconds to accommodate complex page renders. Cold starts: SnapAPI uses warm browser pools so your first request is as fast as subsequent ones — no cold start penalty unlike self-hosted Puppeteer. Deployment: Works identically on Vercel, Cloudflare, Netlify, Railway, and self-hosted Node.js. Pricing: Free 200/month at snapapi.pics, no credit card needed to start building.
Questions about integrating SnapAPI with Nuxt or Remix? The team is available at hello@snapapi.pics and typically responds within one business day. We also offer custom onboarding calls for teams on the Pro or Business plan to walk through architecture decisions, caching strategies, and deployment-specific configuration for your stack.
Start building today at snapapi.pics. Free tier, no credit card required, instant API key.