TUTORIAL

Screenshot API Go Guide 2026
net/http, concurrent goroutines, and the SnapAPI Go SDK

Take screenshots, scrape content, and generate PDFs from Go without running a browser. Complete examples using net/http, goroutines for batch processing, and the official SnapAPI Go SDK.

Get Free API Key — 200 calls/mo

Why Go Developers Choose a Screenshot REST API

Go is a natural fit for API-driven workflows. Its standard library net/http package handles HTTP calls cleanly, goroutines make concurrent batch processing trivial, and the type system catches integration errors at compile time rather than runtime. These strengths apply directly to screenshot and web scraping workflows. Instead of embedding a Chromium binary in your Go binary (which is effectively impossible) or running a sidecar Node.js process to execute Puppeteer, you call SnapAPI’s REST endpoint from your Go service using standard net/http. The result is a PNG, PDF, clean Markdown, or typed JSON that you can process with Go’s excellent concurrency primitives.

Go microservices that handle screenshots as part of a larger pipeline benefit particularly from this pattern. A Go service that processes incoming URL webhooks, captures screenshots, uploads to S3, and notifies downstream services can handle hundreds of concurrent requests without the memory overhead of running browser instances. Each goroutine makes one HTTP call to SnapAPI and processes the response — the entire workflow is non-blocking and scales horizontally without additional infrastructure.

Basic Screenshot with net/http

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
)

func takeScreenshot(targetURL, apiKey string) ([]byte, error) {
	params := url.Values{}
	params.Set("url", targetURL)
	params.Set("width", "1280")
	params.Set("format", "png")

	req, err := http.NewRequest("GET",
		"https://api.snapapi.pics/v1/screenshot?"+params.Encode(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-API-Key", apiKey)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("snapapi: HTTP %d", resp.StatusCode)
	}
	return io.ReadAll(resp.Body)
}

func main() {
	png, err := takeScreenshot("https://example.com", os.Getenv("SNAP_API_KEY"))
	if err != nil {
		panic(err)
	}
	os.WriteFile("screenshot.png", png, 0644)
	fmt.Printf("Saved %d bytes\n", len(png))
}

Concurrent Batch Screenshots with Goroutines

Go’s concurrency model makes batch screenshot processing elegant. Use a semaphore channel to cap concurrency and a WaitGroup to wait for all goroutines to finish. This pattern processes hundreds of URLs with controlled parallelism and aggregates results safely:

package main

import (
	"fmt"
	"sync"
	"os"
)

type Result struct {
	URL   string
	Bytes []byte
	Err   error
}

func batchScreenshots(urls []string, apiKey string, concurrency int) []Result {
	sem := make(chan struct{}, concurrency)
	results := make([]Result, len(urls))
	var wg sync.WaitGroup

	for i, u := range urls {
		wg.Add(1)
		go func(idx int, targetURL string) {
			defer wg.Done()
			sem <- struct{}{}
			defer func() { <-sem }()

			bytes, err := takeScreenshot(targetURL, apiKey)
			results[idx] = Result{URL: targetURL, Bytes: bytes, Err: err}
		}(i, u)
	}

	wg.Wait()
	return results
}

func main() {
	urls := []string{
		"https://example.com",
		"https://github.com",
		"https://golang.org",
	}
	results := batchScreenshots(urls, os.Getenv("SNAP_API_KEY"), 5)
	for _, r := range results {
		if r.Err != nil {
			fmt.Printf("FAIL %s: %v\n", r.URL, r.Err)
		} else {
			fmt.Printf("OK   %s: %d bytes\n", r.URL, len(r.Bytes))
		}
	}
}

Web Scraping and Data Extraction in Go

SnapAPI’s scrape endpoint returns clean Markdown from any URL, and the extract endpoint returns typed JSON based on a schema you define. Both are straightforward to call from Go. The scrape response is a JSON object with a content field containing the Markdown. The extract response is a JSON object matching your schema. Go’s encoding/json package handles both cleanly:

type ScrapeResponse struct {
	Content string `json:"content"`
}

type ProductSchema struct {
	Name        string  `json:"name"`
	Price       float64 `json:"price"`
	Currency    string  `json:"currency"`
	InStock     bool    `json:"in_stock"`
	Rating      float64 `json:"rating"`
	Description string  `json:"description"`
}

func scrapeURL(targetURL, apiKey string) (string, error) {
	params := url.Values{"url": {targetURL}, "format": {"markdown"}}
	req, _ := http.NewRequest("GET", "https://api.snapapi.pics/v1/scrape?"+params.Encode(), nil)
	req.Header.Set("X-API-Key", apiKey)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil { return "", err }
	defer resp.Body.Close()

	var result ScrapeResponse
	json.NewDecoder(resp.Body).Decode(&result)
	return result.Content, nil
}

PDF Generation from Go

Generating PDFs from Go traditionally requires cgo-based bindings to wkhtmltopdf, embedding a binary in your Docker image, or calling a Node.js subprocess to run a headless browser. SnapAPI eliminates all of these approaches. Send a POST request with a URL or raw HTML string and receive PDF bytes in the response body. No C bindings, no subprocess management, no system dependencies:

func generatePDF(targetURL, apiKey string) ([]byte, error) {
	body, _ := json.Marshal(map[string]string{"url": targetURL, "format": "A4"})
	req, _ := http.NewRequest("POST", "https://api.snapapi.pics/v1/pdf",
		bytes.NewReader(body))
	req.Header.Set("X-API-Key", apiKey)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil { return nil, err }
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("pdf error: %d", resp.StatusCode)
	}
	return io.ReadAll(resp.Body)
}

Using Context for Request Cancellation

Production Go services use context.Context for request lifecycle management. SnapAPI calls should respect the parent context so they cancel cleanly when a request times out or the client disconnects. Wrap your API calls with http.NewRequestWithContext to propagate cancellation correctly:

func takeScreenshotCtx(ctx context.Context, targetURL, apiKey string) ([]byte, error) {
	params := url.Values{"url": {targetURL}, "width": {"1280"}, "format": {"png"}}
	req, err := http.NewRequestWithContext(ctx, "GET",
		"https://api.snapapi.pics/v1/screenshot?"+params.Encode(), nil)
	if err != nil { return nil, err }
	req.Header.Set("X-API-Key", apiKey)
	return httpDo(req)
}

Go Screenshot API FAQ

Is there an official Go module?

Yes. The official SnapAPI Go SDK is at github.com/Sleywill/snapapi-go. Install with go get github.com/Sleywill/snapapi-go. It wraps all four endpoints with typed structs, automatic retries with exponential backoff, and context support.

How do I stream the PNG response to S3 without buffering in memory?

Use io.Pipe to connect the HTTP response body directly to an S3 upload. This keeps memory usage constant regardless of file size. The S3 SDK PutObject accepts an io.Reader, so you can pass resp.Body directly after checking the status code.

What’s the recommended timeout for screenshot requests?

Set http.Client.Timeout to 30 seconds for standard pages and 60 seconds for complex SPAs or PDF generation. If you use context.WithTimeout, set it slightly longer than the HTTP client timeout so the context cancellation is the last resort, not the first trigger.

Start Taking Screenshots in Go

Free tier: 200 calls/month. No browser install. Pure HTTP, works in any Go service.

Get Your Free API Key

Building a Screenshot Microservice in Go

A standalone Go screenshot microservice is a common architecture for teams that need screenshot generation from multiple services. The microservice exposes an HTTP endpoint that accepts a URL, calls SnapAPI, and returns the image. Other services in the platform call the screenshot service rather than calling SnapAPI directly, which centralizes API key management, adds a caching layer, and provides a single point for rate limiting and monitoring.

The service typically uses a Redis cache keyed by URL hash with a configurable TTL. On a cache hit, it returns the stored image bytes immediately with a Cache-Hit header. On a cache miss, it calls SnapAPI, stores the result in Redis, and returns it. The cache dramatically reduces API usage and latency for repeated requests. For website monitoring use cases where the same URL is checked hourly, the effective SnapAPI call rate is one per cache TTL period rather than one per check.

Go’s standard library provides everything needed to build this service: net/http for the HTTP server, sync.Map or a Redis client for caching, context for request lifecycle management, and encoding/json for request and response handling. The entire service is typically under two hundred lines of idiomatic Go and compiles to a single binary with no runtime dependencies.

Error Handling and Observability

Production Go services need structured error handling and observability for external API calls. Wrap SnapAPI calls with prometheus metrics to track request duration, error rates, and response size. Use slog or zerolog for structured logging that includes the target URL, response time, HTTP status code, and any error details. This makes debugging slow or failing screenshot requests straightforward without requiring a debug session.

For retry logic, implement exponential backoff with jitter for 5xx responses and network errors. Do not retry 4xx errors from SnapAPI as they indicate a problem with the request parameters. The standard Go pattern uses a ticker-based retry loop with context cancellation so retries respect the parent request deadline. Limit retries to three attempts with delays of one, two, and four seconds to avoid creating thundering herd issues during API degradation events.

Getting Started and Support

SnapAPI’s free tier includes two hundred API calls per month across all four endpoints. Sign up at snapapi.pics, copy your API key from the dashboard, and make your first call in under five minutes. The documentation covers all parameters for each endpoint, response formats, error codes, and code examples in JavaScript, Python, Go, PHP, Swift, and Kotlin. The Playground page lets you test any endpoint directly in the browser without writing any code, which is useful for validating parameters before integrating into your codebase.

Paid plans start at nineteen dollars per month for five thousand calls and seventy-nine dollars per month for fifty thousand calls. Custom plans with higher volume, dedicated infrastructure, and SLA guarantees are available for teams with specific requirements. Support is available via the in-app chat widget and email, with typical response times under one business day. The public status page at status.snapapi.pics shows real-time uptime, historical incident data, and average response times for each endpoint.