Skip to content

API Screenshot en Go : exemple de bout en bout

Programme Go autonome avec net/http : créer une capture, interroger /screenshot/info, télécharger le PNG via /screenshot/thumbnail et gérer les erreurs proprement.

Pourquoi Go et l’API REST ?

Go reste un choix naturel pour les outils en ligne de commande, les workers et les microservices qui exigent du HTTP fiable, des erreurs explicites et un binaire statique unique. L’API ScreenshotCenter se résume à du JSON sur HTTPS et à des téléchargements binaires : net/http et encoding/json suffisent, sans framework web.

Pour des paramètres typés et des retries intégrés, utilisez le SDK Go. Cet article montre le protocole brut pour que chaque étape soit limpide. La liste complète des options se trouve dans la documentation développeur.

Comportement du programme

  1. Appelle GET /api/v1/screenshot/create avec la clé et l’URL cible.
  2. Lit data.id puis interroge GET /api/v1/screenshot/info jusqu’à finished ou error.
  3. Télécharge l’image via GET /api/v1/screenshot/thumbnail.
  4. Écrit screenshot.png et quitte avec un code ≠ 0 en cas d’échec.

Prérequis

Go 1.21+, variable SCREENSHOTCENTER_API_KEY, trafic HTTPS sortant autorisé. Les constantes de polling (2 s) et de délai max (5 min) sont ajustables selon vos pages les plus lentes.

Points de terminaison

ÉtapeMéthodeCheminNotes
CréationGET/api/v1/screenshot/createRéponse immédiate processing ou finished.
ÉtatGET/api/v1/screenshot/infoParamètre id.
FichierGET/api/v1/screenshot/thumbnailFlux PNG brut ; width optionnel.

Programme complet

Enregistrez sous main.go, puis go run . https://exemple.com. Les messages d’erreur utilisateur sont en français ; la logique est identique à la version anglaise.

package main

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

const apiBase = "https://api.screenshotcenter.com/api/v1"

type envelope struct {
	Success bool            `json:"success"`
	Data    json.RawMessage `json:"data"`
	Message string          `json:"message"`
}

type screenshotData struct {
	ID     int64  `json:"id"`
	Status string `json:"status"`
	Error  string `json:"error"`
}

func mustEnv(key string) string {
	v := os.Getenv(key)
	if v == "" {
		fmt.Fprintf(os.Stderr, "variable d'environnement absente : %s\n", key)
		os.Exit(1)
	}
	return v
}

func decodeData(raw json.RawMessage, into any) error {
	if len(raw) == 0 {
		return fmt.Errorf("bloc data vide")
	}
	return json.Unmarshal(raw, into)
}

func createScreenshot(client *http.Client, key, page string) (int64, error) {
	q := url.Values{}
	q.Set("key", key)
	q.Set("url", page)
	q.Set("country", "us")
	req, err := http.NewRequest(http.MethodGet, apiBase+"/screenshot/create?"+q.Encode(), nil)
	if err != nil {
		return 0, err
	}
	resp, err := client.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("création : HTTP %d : %s", resp.StatusCode, string(body))
	}
	var env envelope
	if err := json.Unmarshal(body, &env); err != nil {
		return 0, err
	}
	if !env.Success {
		return 0, fmt.Errorf("création : success=false : %s", string(body))
	}
	var data screenshotData
	if err := decodeData(env.Data, &data); err != nil {
		return 0, err
	}
	if data.ID == 0 {
		return 0, fmt.Errorf("création : id manquant")
	}
	return data.ID, nil
}

func screenshotInfo(client *http.Client, key string, id int64) (*screenshotData, error) {
	q := url.Values{}
	q.Set("key", key)
	q.Set("id", strconv.FormatInt(id, 10))
	req, err := http.NewRequest(http.MethodGet, apiBase+"/screenshot/info?"+q.Encode(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("info : HTTP %d : %s", resp.StatusCode, string(body))
	}
	var env envelope
	if err := json.Unmarshal(body, &env); err != nil {
		return nil, err
	}
	if !env.Success {
		return nil, fmt.Errorf("info : success=false : %s", string(body))
	}
	var data screenshotData
	if err := decodeData(env.Data, &data); err != nil {
		return nil, err
	}
	return &data, nil
}

func waitFinished(client *http.Client, key string, id int64, poll time.Duration, max time.Duration) (*screenshotData, error) {
	deadline := time.Now().Add(max)
	for time.Now().Before(deadline) {
		info, err := screenshotInfo(client, key, id)
		if err != nil {
			return nil, err
		}
		switch info.Status {
		case "finished":
			return info, nil
		case "error":
			if info.Error != "" {
				return nil, fmt.Errorf("erreur capture : %s", info.Error)
			}
			return nil, fmt.Errorf("erreur capture")
		case "processing":
			time.Sleep(poll)
		default:
			time.Sleep(poll)
		}
	}
	return nil, fmt.Errorf("délai dépassé pour la capture %d", id)
}

func downloadThumbnail(client *http.Client, key string, id int64, dest string) error {
	q := url.Values{}
	q.Set("key", key)
	q.Set("id", strconv.FormatInt(id, 10))
	req, err := http.NewRequest(http.MethodGet, apiBase+"/screenshot/thumbnail?"+q.Encode(), nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		b, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("vignette : HTTP %d : %s", resp.StatusCode, string(b))
	}
	f, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer f.Close()
	if _, err := io.Copy(f, resp.Body); err != nil {
		return err
	}
	return nil
}

func main() {
	key := mustEnv("SCREENSHOTCENTER_API_KEY")
	target := "https://example.com"
	if len(os.Args) > 1 {
		target = os.Args[1]
	}
	client := &http.Client{Timeout: 120 * time.Second}
	id, err := createScreenshot(client, key, target)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Printf("capture demandée, id=%d\n", id)
	_, err = waitFinished(client, key, id, 2*time.Second, 5*time.Minute)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := downloadThumbnail(client, key, id, "screenshot.png"); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Println("fichier screenshot.png écrit")
}

Notes de fiabilité

Vérifiez toujours le code HTTP avant le Unmarshal. Traitez success:false comme une erreur métier. En production, journalisez les IDs plutôt que les clés, limitez le parallélisme et appliquez un backoff sur les 5xx.