Screenshot API for Supabase Edge Functions
Capture website screenshots from Supabase Edge Functions with ScreenshotAPI. Deno-based handlers, database integration, and storage patterns.
Last updated: 2026-03-25
Try ScreenshotAPI free
5 free credits. No credit card required.
Capture Website Screenshots from Supabase Edge Functions with ScreenshotAPI
Supabase Edge Functions run Deno at the edge, giving you a serverless execution environment close to your users. They pair naturally with Supabase's database, auth, and storage services. Adding screenshot functionality without ScreenshotAPI would mean trying to run Puppeteer in a Deno edge environment, which is not supported.
ScreenshotAPI fits perfectly into the Supabase Edge Functions screenshot workflow. One fetch call returns a rendered PNG, JPEG, or WebP image that you can return directly, store in Supabase Storage, or save metadata to your Postgres database.
Quick Start
- Sign up for ScreenshotAPI and copy your API key. 5 free credits are included.
- Store the key as a Supabase project secret.
- Create an Edge Function that calls the API.
Installation
Set up the Supabase CLI and create a function:
bashsupabase functions new screenshot
Store the API key as a secret:
bashsupabase secrets set SCREENSHOTAPI_KEY=sk_live_xxxxx
Basic Edge Function
typescript// supabase/functions/screenshot/index.ts const API_BASE = "https://screenshotapi.to/api/v1/screenshot"; Deno.serve(async (request: Request) => { const url = new URL(request.url); const targetUrl = url.searchParams.get("url"); if (!targetUrl) { return Response.json( { error: "url parameter is required" }, { status: 400 } ); } const params = new URLSearchParams({ url: targetUrl, width: url.searchParams.get("width") ?? "1440", height: url.searchParams.get("height") ?? "900", type: url.searchParams.get("type") ?? "webp", quality: url.searchParams.get("quality") ?? "80", }); const colorScheme = url.searchParams.get("colorScheme"); if (colorScheme) params.set("colorScheme", colorScheme); const fullPage = url.searchParams.get("fullPage"); if (fullPage) params.set("fullPage", fullPage); const apiKey = Deno.env.get("SCREENSHOTAPI_KEY")!; const response = await fetch(`${API_BASE}?${params}`, { headers: { "x-api-key": apiKey }, }); if (!response.ok) { return Response.json( { error: "Screenshot capture failed" }, { status: 502 } ); } const imageType = url.searchParams.get("type") ?? "webp"; return new Response(await response.arrayBuffer(), { headers: { "Content-Type": `image/${imageType}`, "Cache-Control": "public, max-age=3600", }, }); });
Deploy the function:
bashsupabase functions deploy screenshot
Test it:
bashcurl "https://your-project.supabase.co/functions/v1/screenshot?url=https://example.com" \ -H "Authorization: Bearer YOUR_ANON_KEY" \ --output screenshot.webp
Store Screenshots in Supabase Storage
Capture a screenshot and upload it to a Supabase Storage bucket:
typescript// supabase/functions/screenshot-store/index.ts import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const API_BASE = "https://screenshotapi.to/api/v1/screenshot"; Deno.serve(async (request: Request) => { const { url, type = "webp", quality = 80 } = await request.json(); if (!url) { return Response.json({ error: "url is required" }, { status: 400 }); } const apiKey = Deno.env.get("SCREENSHOTAPI_KEY")!; const params = new URLSearchParams({ url, width: "1440", height: "900", type, quality: String(quality), }); const response = await fetch(`${API_BASE}?${params}`, { headers: { "x-api-key": apiKey }, }); if (!response.ok) { return Response.json({ error: "Screenshot capture failed" }, { status: 502 }); } const imageBuffer = await response.arrayBuffer(); const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! ); const filename = `${Date.now()}-${crypto.randomUUID()}.${type}`; const { error: uploadError } = await supabase.storage .from("screenshots") .upload(filename, imageBuffer, { contentType: `image/${type}`, cacheControl: "86400", }); if (uploadError) { return Response.json({ error: uploadError.message }, { status: 500 }); } const { data: publicUrl } = supabase.storage .from("screenshots") .getPublicUrl(filename); return Response.json({ url: publicUrl.publicUrl, size: imageBuffer.byteLength, filename, }); });
Save Metadata to Database
Track screenshot captures in your Postgres database:
typescript// supabase/functions/screenshot-track/index.ts import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const API_BASE = "https://screenshotapi.to/api/v1/screenshot"; Deno.serve(async (request: Request) => { const { url, type = "webp" } = await request.json(); if (!url) { return Response.json({ error: "url is required" }, { status: 400 }); } const apiKey = Deno.env.get("SCREENSHOTAPI_KEY")!; const params = new URLSearchParams({ url, width: "1440", height: "900", type, quality: "80", }); const startTime = Date.now(); const response = await fetch(`${API_BASE}?${params}`, { headers: { "x-api-key": apiKey }, }); const durationMs = Date.now() - startTime; const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! ); if (!response.ok) { await supabase.from("screenshot_logs").insert({ url, success: false, status_code: response.status, duration_ms: durationMs, }); return Response.json({ error: "Screenshot capture failed" }, { status: 502 }); } const imageBuffer = await response.arrayBuffer(); const filename = `${Date.now()}.${type}`; await supabase.storage.from("screenshots").upload(filename, imageBuffer, { contentType: `image/${type}`, }); const { data: publicUrl } = supabase.storage .from("screenshots") .getPublicUrl(filename); await supabase.from("screenshot_logs").insert({ url, success: true, status_code: 200, duration_ms: durationMs, file_size: imageBuffer.byteLength, storage_path: filename, public_url: publicUrl.publicUrl, }); return Response.json({ url: publicUrl.publicUrl, size: imageBuffer.byteLength, duration: durationMs, }); });
Auth-Protected Screenshot Function
Require authentication before allowing screenshot capture:
typescript// supabase/functions/screenshot-auth/index.ts import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const API_BASE = "https://screenshotapi.to/api/v1/screenshot"; Deno.serve(async (request: Request) => { const authHeader = request.headers.get("Authorization"); if (!authHeader) { return Response.json({ error: "Missing authorization" }, { status: 401 }); } const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_ANON_KEY")!, { global: { headers: { Authorization: authHeader } } } ); const { data: { user }, error } = await supabase.auth.getUser(); if (error || !user) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } const url = new URL(request.url); const targetUrl = url.searchParams.get("url"); if (!targetUrl) { return Response.json({ error: "url parameter is required" }, { status: 400 }); } const params = new URLSearchParams({ url: targetUrl, width: "1440", height: "900", type: "webp", quality: "80", }); const apiKey = Deno.env.get("SCREENSHOTAPI_KEY")!; const response = await fetch(`${API_BASE}?${params}`, { headers: { "x-api-key": apiKey }, }); if (!response.ok) { return Response.json({ error: "Screenshot capture failed" }, { status: 502 }); } return new Response(await response.arrayBuffer(), { headers: { "Content-Type": "image/webp", "Cache-Control": "public, max-age=3600", }, }); });
Production Tips
Secrets Management
Always use supabase secrets set for API keys. Never hardcode secrets in your function code. Access them with Deno.env.get() at runtime.
CORS
If calling the Edge Function from a browser, handle CORS headers:
typescriptconst corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; if (request.method === "OPTIONS") { return new Response("ok", { headers: corsHeaders }); }
Storage Buckets
Create a screenshots bucket in the Supabase dashboard. Set it to public if you want direct URL access to images. Add lifecycle policies to clean up old screenshots.
Rate Limiting
Supabase Edge Functions do not include built-in rate limiting. Implement request counting in your database or use Supabase RLS policies to control access. Visit the pricing page for credit tier options.
Further Reading
- The Deno integration covers standalone Deno screenshot patterns.
- See the Cloudflare Workers integration for another edge-based approach.
- The API documentation has the full parameter reference for all endpoints.
Frequently asked questions
Can I call ScreenshotAPI from a Supabase Edge Function?
Yes. Supabase Edge Functions run Deno and support the standard fetch API. Call ScreenshotAPI like any other HTTP endpoint and return the image data.
How do I store the API key in Supabase Edge Functions?
Use Supabase project secrets. Set the secret with the Supabase CLI and access it with Deno.env.get() inside your function.
Can I store screenshots in Supabase Storage?
Yes. After capturing a screenshot, use the Supabase client to upload the image to a Storage bucket. This gives you a public URL for the image.
Do I need to install any packages for Edge Functions?
No. The built-in fetch API handles the HTTP request. Import the Supabase client from the esm.sh CDN if you need database or storage access.
Related resources
Start capturing screenshots today
Create a free account and get 5 credits to try the API. No credit card required. Pay only for what you use.