Skip to content

Go Screenshot API

Capture pixel-perfect screenshots of any URL with one HTTP request in Go — no chromedp, no Chrome binary next to your binary, no servers to maintain.
Go website screenshot API example

Take a screenshot in Go

No SDK and no chromedp — the Microlink REST API turns any URL into a hosted screenshot with a single HTTP GET. Everything below is standard library: net/http, net/url and encoding/json.
Step 01 · Start a module
There is nothing to go get. net/http sends the request, encoding/json reads it back, and the dependency list stays empty.
go mod init example.com/screenshot
Step 02 · Capture any URL
Point it at a page, ask for a screenshot, and decode the hosted image URL out of the JSON response.
capture.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "time"
)

type response struct {
    Data struct {
        Screenshot struct {
            URL string `json:"url"`
        } `json:"screenshot"`
    } `json:"data"`
}

var client = &http.Client{Timeout: 90 * time.Second} // above the API budget: 30s free, 60s pro

func main() {
    query := url.Values{
        "url":        {"https://example.com"},
        "screenshot": {"true"},
        "meta":       {"false"}, // skip metadata extraction for a faster response
    }

    res, err := client.Get("https://api.microlink.io?" + query.Encode())
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    if res.StatusCode != http.StatusOK {
        panic(fmt.Errorf("microlink: unexpected status %d", res.StatusCode))
    }

    var body response
    if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
        panic(err)
    }

    fmt.Println(body.Data.Screenshot.URL)
}
Step 03 · Customize the capture
Output format, full-page captures, device emulation, and ad blocking — every Headless Chrome option is just a query field, with dot notation for the nested ones.
options.go
query := url.Values{
    "url":                 {"https://example.com"},
    "screenshot.type":     {"jpeg"},          // png (default) | jpeg
    "screenshot.fullPage": {"true"},          // capture the entire scrollable page
    "device":              {"iPhone 15 Pro"}, // emulate any device
    "adblock":             {"true"},          // strip ads & cookie banners (default)
    "meta":                {"false"},
}

res, err := client.Get("https://api.microlink.io?" + query.Encode())
Step 04 · Write it to disk
The response is a hosted image URL on a global CDN. Stream it into a file with a second request, or just hand the URL to the browser.
save.go
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "time"
)

type response struct {
    Data struct {
        Screenshot struct {
            URL string `json:"url"`
        } `json:"screenshot"`
    } `json:"data"`
}

var client = &http.Client{Timeout: 90 * time.Second} // above the API budget: 30s free, 60s pro

func main() {
    query := url.Values{
        "url":        {"https://example.com"},
        "screenshot": {"true"},
    }

    res, err := client.Get("https://api.microlink.io?" + query.Encode())
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    if res.StatusCode != http.StatusOK {
        panic(fmt.Errorf("microlink: unexpected status %d", res.StatusCode))
    }

    var body response
    if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
        panic(err)
    }

    image, err := client.Get(body.Data.Screenshot.URL)
    if err != nil {
        panic(err)
    }
    defer image.Body.Close()

    file, err := os.Create("screenshot.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    if _, err := io.Copy(file, image.Body); err != nil {
        panic(err)
    }
}

Drop it into your router

A handler, a route, or a standalone binary — the same request becomes your own screenshot endpoint, perfect for dynamic Open Graph images on any runtime.
  • Go
  • Gin
  • Echo
  • Fiber
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "time"

    "github.com/gin-gonic/gin"
)

type response struct {
    Data struct {
        Screenshot struct {
            URL string `json:"url"`
        } `json:"screenshot"`
    } `json:"data"`
}

var client = &http.Client{Timeout: 90 * time.Second} // above the API budget: 30s free, 60s pro

func screenshotURL(target string) (string, error) {
    query := url.Values{
        "url":        {target},
        "screenshot": {"true"},
        "meta":       {"false"},
    }

    res, err := client.Get("https://api.microlink.io?" + query.Encode())
    if err != nil {
        return "", err
    }
    defer res.Body.Close()

    if res.StatusCode != http.StatusOK {
        return "", fmt.Errorf("microlink: unexpected status %d", res.StatusCode)
    }

    var body response
    if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
        return "", err
    }

    if body.Data.Screenshot.URL == "" {
        return "", fmt.Errorf("microlink: no screenshot for %s", target)
    }

    return body.Data.Screenshot.URL, nil
}

func main() {
    router := gin.Default()

    // GET /screenshot?url=https://example.com
    router.GET("/screenshot", func(c *gin.Context) {
        image, err := screenshotURL(c.Query("url"))
        if err != nil {
            c.AbortWithError(http.StatusBadGateway, err)
            return
        }
        c.Redirect(http.StatusFound, image)
    })

    router.Run(":3000")
}

Skip the chromedp maintenance

Driving Headless Chrome from Go means shipping a browser next to a statically linked binary, keeping it pinned to a Chrome release, and owning the pool. The API gives you the same control without any of the infrastructure.

Self-hosted chromedp

  • Install Chrome or Chromium on every host that runs your binary
  • Ship headless Chrome (~300 MB) beside a Go binary built for scratch
  • Each browser eats hundreds of MB of RAM; workers crash under load
  • Launching Chrome adds seconds of cold-start latency
  • You build the pooling, queueing, retries and autoscaling
  • Write your own cookie-banner & ad dismissal scripts

Microlink for Go

  • One HTTP request — net/http and encoding/json, nothing to go get
  • Runs anywhere: scratch containers, Cloud Run, Lambda, your laptop
  • Autoscaled managed browser fleet with a 99.95% uptime SLA
  • Sub-second cached responses from 340+ edge locations
  • Built-in adblock removes ads & cookie banners automatically
  • Full-page, device emulation, overlays & DOM interaction included

Built for the way you write Go.

A REST API that feels native in Go — one HTTP call, JSON back, and at home in anything from a Gin service to a Cloud Run container. Read the screenshot guide to go deeper.

  • No Browser to Install

    Skip chromedp and go-rod entirely. There is no Chromium to download, pin, or keep in sync with Chrome releases.
  • Standard Library Only

    net/http sends the request, net/url builds the query, encoding/json decodes the response. Your go.mod stays empty.
  • Router Friendly

    Drop it into net/http, Gin, Echo, or Fiber as a handler in a few lines. The same request works everywhere.
  • Scratch Images & Serverless

    No browser to bundle, so a static binary in a scratch or distroless image still works. Deploy to Cloud Run, AWS Lambda, or Fly.
  • Goroutine Friendly

    Fan out captures across goroutines. Parallelism is bounded by your plan quota and its concurrency, not by a browser pool you have to build.
  • Simple JSON Response

    A single request returns JSON with the hosted image URL. No contexts to cancel, no explicit waits, no browser lifecycle.
  • Zero Infrastructure

    Managed Headless Chrome, autoscaled and load-balanced. No browser pool, no servers, no patching to maintain.
  • Built-in Adblock

    Captures arrive clean — GDPR cookie banners, newsletter popups, and injected ads are removed before the shot.
  • Generous Free Tier

    Start with 25 requests per day — no account, no credit card. Add an API key when you are ready to scale.
  • No Browser to Install

    Skip chromedp and go-rod entirely. There is no Chromium to download, pin, or keep in sync with Chrome releases.
  • Standard Library Only

    net/http sends the request, net/url builds the query, encoding/json decodes the response. Your go.mod stays empty.
  • Router Friendly

    Drop it into net/http, Gin, Echo, or Fiber as a handler in a few lines. The same request works everywhere.
  • Scratch Images & Serverless

    No browser to bundle, so a static binary in a scratch or distroless image still works. Deploy to Cloud Run, AWS Lambda, or Fly.
  • Goroutine Friendly

    Fan out captures across goroutines. Parallelism is bounded by your plan quota and its concurrency, not by a browser pool you have to build.
  • Simple JSON Response

    A single request returns JSON with the hosted image URL. No contexts to cancel, no explicit waits, no browser lifecycle.
  • Zero Infrastructure

    Managed Headless Chrome, autoscaled and load-balanced. No browser pool, no servers, no patching to maintain.
  • Built-in Adblock

    Captures arrive clean — GDPR cookie banners, newsletter popups, and injected ads are removed before the shot.
  • Generous Free Tier

    Start with 25 requests per day — no account, no credit card. Add an API key when you are ready to scale.

Try it live in the playground

Paste a URL and see the exact API request before you write a line of Go.

Go Screenshot FAQ

Everything Go developers ask before integrating the Microlink screenshot API.

Do I need chromedp or a headless browser?

No. It is a plain HTTP request to the Microlink API — there is no Chromium binary to install next to your service and no DevTools protocol to speak. The Headless Chrome fleet runs on Microlink's side.
That is what makes it deploy cleanly to scratch and distroless images, where shipping a browser alongside a static Go binary is painful.

Which HTTP client should I use?

The standard library. net/http sends the GET, net/url encodes the query, and encoding/json decodes the response — no third-party client required.
Give your http.Client a timeout above the API budget: 30 seconds on free, 60 seconds on Pro.

Does it work with Gin, Echo, and Fiber?

Yes. Because it is just an HTTP call, it drops into any handler in a few lines — see the tabs above for net/http, Gin, Echo, and Fiber, or the screenshot guide.

Can I fan out requests across goroutines?

Yes. There is no browser pool to size, so a sync.WaitGroup over a batch of URLs is fine — just keep the fan-out within your plan quota and its concurrency.
The exact limits are on the rate limit page.

Does it run on Cloud Run, Lambda, or a scratch image?

Yes. Because there is no Chrome binary to bundle, a static Go binary in a scratch or distroless image works as-is — on Cloud Run, AWS Lambda, or any container.
See the API overview for request details.

Is there a free tier or do I need an API key?

The free tier gives you 25 requests per day with no account, no credit card, and no API key. Just call the endpoint and start capturing.
When you need more throughput or caching control, add an apiKey header and requests route to the Pro tier. See pricing for the limits.

How fast is it and how does it scale?

Cached captures return sub-second from a global edge network, and the browser fleet autoscales behind a 99.95% uptime SLA — so a traffic spike does not mean provisioning more workers.
Compare the numbers on the screenshot API benchmarks.

Start capturing in Go

Get 25 requests/day with zero commitment — no account and no credit card. Send your first request and ship a screenshot in minutes.
No login needed
25 reqs/day free
No credit card