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
- Appelle
GET /api/v1/screenshot/createavec la clé et l’URL cible. - Lit
data.idpuis interrogeGET /api/v1/screenshot/infojusqu’àfinishedouerror. - Télécharge l’image via
GET /api/v1/screenshot/thumbnail. - Écrit
screenshot.pnget 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
| Étape | Méthode | Chemin | Notes |
|---|---|---|---|
| Création | GET | /api/v1/screenshot/create | Réponse immédiate processing ou finished. |
| État | GET | /api/v1/screenshot/info | Paramètre id. |
| Fichier | GET | /api/v1/screenshot/thumbnail | Flux 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.