API REST · Documentazione

Tutto quello che serve per collegare l'API.

Una chiave, un indirizzo e le stesse regole per tutti i servizi. Qui trovi la guida comune e l'endpoint dei QR statici; ogni servizio ha la sua scheda tecnica.

Risposte in JSONErrori con status, code e messageEsempi in 14 linguaggi
Il primo QR · cURL
curl "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static?content=https://www.tuosito.it&size=300" \
  -H "x-api-key: LA_TUA_API_KEY" \
  --output qrcode.png

Chiave e indirizzo.

Indirizzo di base

Tutte le richieste partono da qui.

https://www.qrcodegenerate.it/api/v1
La chiave

Va nell'header di ogni richiesta. Tienila sul tuo server: mai in un'app o in una pagina pubblica. La convalida dei biglietti usa una chiave dedicata.

x-api-key: LA_TUA_API_KEY
Gli errori

Hanno sempre la forma { "status", "code", "message" }: usa code nel tuo codice.

Tutti i codici →

Guida comune.

Vale per tutti i servizi: QR statici e dinamici, biglietti e buoni, etichette digitali, anti-contraffazione e webhook. I numeri sono quelli in vigore adesso.

Elenchi e paginazione

Gli elenchi (QR, eventi, biglietti, chiavi, schede) accettano limit e offset nell'indirizzo. limit è il numero di elementi per pagina: da 1 a 100, predefinito 50. offset è quanti elementi saltare.

La risposta contiene items, total (tutti gli elementi), limit e offset usati. Per leggere tutto, continua ad aumentare offset di limit finché offset + limit non raggiunge total.

GET /api/v1/qrcode/dynamic?limit=100&offset=100
Limiti di frequenza

Le chiamate fatte con la tua chiave API hanno un limite di richieste al minuto che dipende dal piano: lo trovi nella tabella «Cosa include ogni piano». Ogni risposta riporta X-RateLimit-Limit e X-RateLimit-Remaining. Superato il limite ricevi 429 TROPPE_RICHIESTE_AL_MINUTO con l'intestazione Retry-After: i secondi da aspettare. Una richiesta respinta non consuma la quota mensile.

In più, contro le raffiche, ogni indirizzo di rete può fare al massimo 6.000 richieste al minuto, qualunque sia il piano.

La convalida ai varchi ha limiti propri, pensati per il personale: 300 scansioni al minuto per ogni chiave di convalida e 100 richieste non riuscite (chiave sbagliata, richiesta malformata) ogni 10 minuti per indirizzo. Le scansioni dei clienti sul QR non contano nelle richieste del piano.

Oltre al limite di frequenza, il piano fissa una quota mensile di richieste e un numero massimo di QR, eventi, biglietti, chiavi e schede: la tabella «Cosa include ogni piano» di ogni prodotto mostra i valori in vigore.

Webhook

Un webhook è un indirizzo https del tuo server che riceve una chiamata POST appena succede qualcosa nel tuo account. Si gestiscono dalla dashboard (scheda «Webhook») o con /api/v1/webhooks. Sono inclusi secondo il piano: la tabella «Cosa include ogni piano» dice quanti.

Eventi: qr.scanned, qr.expired, ticket.validated, voucher.redeemed, authenticity.checked. Il corpo è JSON: { "id", "type", "apiVersion", "createdAt", "data" }; id è unico per ogni invio e si ripete nei nuovi tentativi, quindi puoi usarlo per non elaborare due volte lo stesso evento.

Ogni invio porta X-QRG-Event, X-QRG-Delivery, X-QRG-Webhook e X-QRG-Signature: t=<secondi>,v1=<firma>. La firma è HMAC-SHA256, in esadecimale, del testo «t.corpo» con il segreto del webhook (whsec_…). Il segreto si vede solo alla creazione e quando lo rinnovi.

Il tuo server deve rispondere con un codice 2xx entro 10 secondi. Se non succede si riprova dopo 1 minuto, 5 minuti, 30 minuti, 2 ore, 8 ore. Dopo 20 invii non consegnati di fila il webhook si sospende e lo riattivi dalla dashboard. Il registro degli invii resta 30 giorni.

Si invia solo a indirizzi https pubblici. I redirect non vengono seguiti.

Esempio
// Node.js: verifica della firma
const crypto = require("crypto");
function firmaValida(corpoGrezzo, intestazione, segreto) {
  const p = Object.fromEntries(intestazione.split(",").map((x) => x.split("=")));
  const attesa = crypto.createHmac("sha256", segreto).update(p.t + "." + corpoGrezzo).digest();
  const ricevuta = Buffer.from(String(p.v1 || ""), "hex");
  if (ricevuta.length !== attesa.length) return false; // firma assente o di lunghezza sbagliata
  return crypto.timingSafeEqual(attesa, ricevuta) && Math.abs(Date.now() / 1000 - Number(p.t)) < 300; // al massimo 5 minuti fa
}
Date, orari e fusi

Le date e gli orari che invii vanno nel formato ISO 8601 con il fuso orario, per esempio 2026-10-04T21:00:00+02:00. Le risposte li restituiscono sempre in UTC, con la Z finale.

Gli intervalli di date delle statistiche (from e to) sono giorni nel formato AAAA-MM-GG e si contano sul fuso dell'Italia (Europe/Rome).

Errori

Ogni errore ha la forma { "status", "code", "message" }: status è il codice HTTP, code è un testo stabile che puoi usare nel codice, message è una spiegazione in italiano. Non basare la logica sul testo di message. Alcune risposte hanno anche error, con lo stesso testo di message: c'è per compatibilità, usa code.

Chiave e quota valgono per tutti i servizi: 401 CHIAVE_MANCANTE (manca l'header x-api-key), 403 CHIAVE_NON_VALIDA, 403 PIANO_NON_ATTIVO, 429 TROPPE_RICHIESTE_AL_MINUTO (limite al minuto del piano), 429 QUOTA_MENSILE_ESAURITA (richieste del periodo finite), 429 TROPPE_RICHIESTE (troppe richieste dallo stesso indirizzo di rete).

Una richiesta con campi che non esistono viene rifiutata con 422 CAMPO_NON_CONSENTITO: i nomi vanno scritti come nelle tabelle dei parametri. 401 e 403 riguardano la chiave, 404 un elemento che non esiste o non è tuo, 409 una modifica in conflitto, 429 troppe richieste.

QR statici.

Un QR statico contiene direttamente il testo o l'indirizzo: non si può cambiare dopo la stampa e non ha statistiche. Se ti serve cambiarlo, usa i QR dinamici.

Indirizzo e metodi POST e GET
  • POST/qrcode/generate-static

    Corpo JSON. Consigliato: supporta tutti i parametri.

  • GET/qrcode/generate-static

    Parametri nell'indirizzo, utile per richieste semplici. I colori si passano come color[dark] e color[light].

Parametri 6 campi
Parametro Tipo Obbligatorio Descrizione
content stringa Sì Testo o URL da codificare nel QR Code, al massimo 2.000 caratteri.
format stringa No Formato output: png, jpeg, webp, svg, base64url. Default: png.
size numero No Lato del QR Code in pixel: numero intero da 100 a 2000. Default: 200.
color oggetto No Solo POST: colori dark e light nel formato #RRGGBB o #RRGGBBAA, diversi tra loro. Esempio: { "dark": "#000000", "light": "#ffffff" }.
color[dark] / color[light] query string No Solo GET: colori di primo piano e sfondo passati come parametri URL.
logoUrl stringa URL No URL pubblico di un logo da inserire al centro del QR (PNG, JPEG o WebP, non SVG; al massimo 2 MB, scaricato entro 5 secondi). Funziona con png, jpeg, webp e base64url. Se il logo non si scarica il QR esce lo stesso, senza logo: l'intestazione X-QR-Logo vale applied o skipped.

Nota: l'svg viene generato senza logo. Per un QR con logo usa png, jpeg, webp o base64url.

Esempi 14 linguaggi

Gli esempi usano l'endpoint reale e salvano il risultato come file quando il formato restituisce un'immagine. Sostituisci LA_TUA_API_KEY con la tua chiave o usa una variabile d'ambiente.

cURL — POST consigliato

curl -X POST "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static" \
  -H "Content-Type: application/json" \
  -H "x-api-key: LA_TUA_API_KEY" \
  --output qrcode.png \
  -d '{
    "content": "https://www.tuosito.it",
    "format": "png",
    "size": 300,
    "color": {
      "dark": "#000000",
      "light": "#ffffff"
    }
  }'

Node.js 18+ — POST

import { writeFile } from "node:fs/promises";

const apiKey = process.env.QRCODEGENERATE_API_KEY;
const endpoint = "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static";

if (!apiKey) {
  throw new Error("Variabile QRCODEGENERATE_API_KEY mancante");
}

const response = await fetch(endpoint, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": apiKey
  },
  body: JSON.stringify({
    content: "https://www.tuosito.it",
    format: "png",
    size: 300,
    color: {
      dark: "#000000",
      light: "#ffffff"
    }
  })
});

if (!response.ok) {
  const errorBody = await response.text();
  throw new Error(`Errore API ${response.status}: ${errorBody}`);
}

const buffer = Buffer.from(await response.arrayBuffer());
await writeFile("qrcode.png", buffer);
console.log("QR Code salvato in qrcode.png");

Node.js 18+ — GET semplice

import { writeFile } from "node:fs/promises";

const apiKey = process.env.QRCODEGENERATE_API_KEY;
const endpoint = new URL("https://www.qrcodegenerate.it/api/v1/qrcode/generate-static");

endpoint.searchParams.set("content", "https://www.tuosito.it");
endpoint.searchParams.set("format", "svg");
endpoint.searchParams.set("size", "300");
endpoint.searchParams.set("color[dark]", "#000000");
endpoint.searchParams.set("color[light]", "#ffffff");

const response = await fetch(endpoint, {
  method: "GET",
  headers: {
    "x-api-key": apiKey
  }
});

if (!response.ok) {
  const errorBody = await response.text();
  throw new Error(`Errore API ${response.status}: ${errorBody}`);
}

const svg = await response.text();
await writeFile("qrcode.svg", svg, "utf8");
console.log("QR Code SVG salvato in qrcode.svg");

Python — POST

import os
import requests

api_key = os.environ.get("QRCODEGENERATE_API_KEY")
endpoint = "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static"

if not api_key:
    raise RuntimeError("Variabile QRCODEGENERATE_API_KEY mancante")

payload = {
    "content": "https://www.tuosito.it",
    "format": "png",
    "size": 300,
    "color": {
        "dark": "#000000",
        "light": "#ffffff"
    }
}

response = requests.post(
    endpoint,
    headers={"x-api-key": api_key},
    json=payload,
    timeout=30
)

if not response.ok:
    raise RuntimeError(f"Errore API {response.status_code}: {response.text}")

with open("qrcode.png", "wb") as file:
    file.write(response.content)

print("QR Code salvato in qrcode.png")

PHP — POST con cURL

<?php
$apiKey = getenv('QRCODEGENERATE_API_KEY');
$endpoint = 'https://www.qrcodegenerate.it/api/v1/qrcode/generate-static';

if (!$apiKey) {
    throw new RuntimeException('Variabile QRCODEGENERATE_API_KEY mancante');
}

$payload = json_encode([
    'content' => 'https://www.tuosito.it',
    'format' => 'png',
    'size' => 300,
    'color' => [
        'dark' => '#000000',
        'light' => '#ffffff'
    ]
]);

$ch = curl_init($endpoint);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'x-api-key: ' . $apiKey
    ],
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30
]);

$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}

curl_close($ch);

if ($statusCode < 200 || $statusCode >= 300) {
    throw new RuntimeException('Errore API ' . $statusCode . ': ' . $response);
}

file_put_contents('qrcode.png', $response);
echo 'QR Code salvato in qrcode.png';

TypeScript — POST

import { writeFile } from "node:fs/promises";

const apiKey = process.env.QRCODEGENERATE_API_KEY;
const endpoint = "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static";

if (!apiKey) {
  throw new Error("Variabile QRCODEGENERATE_API_KEY mancante");
}

const payload = {
  content: "https://www.tuosito.it",
  format: "png",
  size: 300,
  color: {
    dark: "#000000",
    light: "#ffffff"
  }
};

const response = await fetch(endpoint, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": apiKey
  },
  body: JSON.stringify(payload)
});

if (!response.ok) {
  const errorBody = await response.text();
  throw new Error(`Errore API ${response.status}: ${errorBody}`);
}

const fileBuffer = Buffer.from(await response.arrayBuffer());
await writeFile("qrcode.png", fileBuffer);

Ruby — POST

require "net/http"
require "json"
require "uri"

api_key = ENV["QRCODEGENERATE_API_KEY"]
endpoint = URI("https://www.qrcodegenerate.it/api/v1/qrcode/generate-static")

raise "Variabile QRCODEGENERATE_API_KEY mancante" if api_key.to_s.empty?

request = Net::HTTP::Post.new(endpoint)
request["Content-Type"] = "application/json"
request["x-api-key"] = api_key
request.body = {
  content: "https://www.tuosito.it",
  format: "png",
  size: 300,
  color: {
    dark: "#000000",
    light: "#ffffff"
  }
}.to_json

response = Net::HTTP.start(endpoint.hostname, endpoint.port, use_ssl: endpoint.scheme == "https") do |http|
  http.request(request)
end

unless response.is_a?(Net::HTTPSuccess)
  raise "Errore API #{response.code}: #{response.body}"
end

File.binwrite("qrcode.png", response.body)
puts "QR Code salvato in qrcode.png"

Swift — POST

import Foundation

@main
struct GenerateQrCode {
    static func main() async throws {
        let apiKey = ProcessInfo.processInfo.environment["QRCODEGENERATE_API_KEY"] ?? ""
        guard !apiKey.isEmpty else {
            fatalError("Variabile QRCODEGENERATE_API_KEY mancante")
        }

        let endpoint = URL(string: "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static")!
        var request = URLRequest(url: endpoint)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue(apiKey, forHTTPHeaderField: "x-api-key")

        let payload: [String: Any] = [
            "content": "https://www.tuosito.it",
            "format": "png",
            "size": 300,
            "color": [
                "dark": "#000000",
                "light": "#ffffff"
            ]
        ]

        request.httpBody = try JSONSerialization.data(withJSONObject: payload)

        let (data, response) = try await URLSession.shared.data(for: request)
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            let body = String(data: data, encoding: .utf8) ?? ""
            throw NSError(domain: "QRCodeGenerate", code: 1, userInfo: [NSLocalizedDescriptionKey: body])
        }

        try data.write(to: URL(fileURLWithPath: "qrcode.png"))
        print("QR Code salvato in qrcode.png")
    }
}

Go — POST

package main

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

func main() {
    apiKey := os.Getenv("QRCODEGENERATE_API_KEY")
    if apiKey == "" {
        panic("Variabile QRCODEGENERATE_API_KEY mancante")
    }

    payload := map[string]interface{}{
        "content": "https://www.tuosito.it",
        "format":  "png",
        "size":    300,
        "color": map[string]string{
            "dark":  "#000000",
            "light": "#ffffff",
        },
    }

    body, err := json.Marshal(payload)
    if err != nil {
        panic(err)
    }

    req, err := http.NewRequest("POST", "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static", bytes.NewReader(body))
    if err != nil {
        panic(err)
    }

    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-api-key", apiKey)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        errorBody, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("Errore API %d: %s", resp.StatusCode, string(errorBody)))
    }

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

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

    fmt.Println("QR Code salvato in qrcode.png")
}

C++ — POST con libcurl

#include <curl/curl.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>

int main() {
    const char* apiKeyEnv = std::getenv("QRCODEGENERATE_API_KEY");
    if (apiKeyEnv == nullptr) {
        std::cerr << "Variabile QRCODEGENERATE_API_KEY mancante" << std::endl;
        return 1;
    }

    FILE* output = std::fopen("qrcode.png", "wb");
    if (!output) {
        std::cerr << "Impossibile creare qrcode.png" << std::endl;
        return 1;
    }

    CURL* curl = curl_easy_init();
    if (!curl) {
        std::fclose(output);
        return 1;
    }

    std::string apiHeader = std::string("x-api-key: ") + apiKeyEnv;
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, apiHeader.c_str());

    const char* payload = R"json({
        "content": "https://www.tuosito.it",
        "format": "png",
        "size": 300,
        "color": {
            "dark": "#000000",
            "light": "#ffffff"
        }
    })json";

    curl_easy_setopt(curl, CURLOPT_URL, "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, output);

    CURLcode result = curl_easy_perform(curl);
    if (result != CURLE_OK) {
        std::cerr << curl_easy_strerror(result) << std::endl;
    }

    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    std::fclose(output);

    return result == CURLE_OK ? 0 : 1;
}

C# — POST

using System.Text;

var apiKey = Environment.GetEnvironmentVariable("QRCODEGENERATE_API_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
{
    throw new InvalidOperationException("Variabile QRCODEGENERATE_API_KEY mancante");
}

var endpoint = "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static";
var json = """
{
  "content": "https://www.tuosito.it",
  "format": "png",
  "size": 300,
  "color": {
    "dark": "#000000",
    "light": "#ffffff"
  }
}
""";

using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
request.Headers.Add("x-api-key", apiKey);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsByteArrayAsync();

if (!response.IsSuccessStatusCode)
{
    var errorText = Encoding.UTF8.GetString(body);
    throw new Exception($"Errore API {(int)response.StatusCode}: {errorText}");
}

await File.WriteAllBytesAsync("qrcode.png", body);
Console.WriteLine("QR Code salvato in qrcode.png");

Java 11+ — POST

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;

public class GenerateQrCode {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("QRCODEGENERATE_API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalStateException("Variabile QRCODEGENERATE_API_KEY mancante");
        }

        String json = """
        {
          "content": "https://www.tuosito.it",
          "format": "png",
          "size": 300,
          "color": {
            "dark": "#000000",
            "light": "#ffffff"
          }
        }
        """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://www.qrcodegenerate.it/api/v1/qrcode/generate-static"))
            .header("Content-Type", "application/json")
            .header("x-api-key", apiKey)
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();

        HttpResponse<byte[]> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofByteArray());

        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new RuntimeException("Errore API " + response.statusCode() + ": " + new String(response.body()));
        }

        Files.write(Path.of("qrcode.png"), response.body());
        System.out.println("QR Code salvato in qrcode.png");
    }
}

VB.NET — POST

Imports System
Imports System.IO
Imports System.Net.Http
Imports System.Text
Imports System.Threading.Tasks

Module Program
    Sub Main()
        MainAsync().GetAwaiter().GetResult()
    End Sub

    Private Async Function MainAsync() As Task
        Dim apiKey = Environment.GetEnvironmentVariable("QRCODEGENERATE_API_KEY")
        If String.IsNullOrWhiteSpace(apiKey) Then
            Throw New InvalidOperationException("Variabile QRCODEGENERATE_API_KEY mancante")
        End If

        Dim endpoint = "https://www.qrcodegenerate.it/api/v1/qrcode/generate-static"
        Dim json = "{" &
            """content"":""https://www.tuosito.it""," &
            """format"":""png""," &
            """size"":300," &
            """color"":{""dark"":""#000000"",""light"":""#ffffff""}" &
        "}"

        Using client As New HttpClient()
            Using request As New HttpRequestMessage(HttpMethod.Post, endpoint)
                request.Headers.Add("x-api-key", apiKey)
                request.Content = New StringContent(json, Encoding.UTF8, "application/json")

                Using response = Await client.SendAsync(request)
                    Dim body = Await response.Content.ReadAsByteArrayAsync()

                    If Not response.IsSuccessStatusCode Then
                        Dim errorText = Encoding.UTF8.GetString(body)
                        Throw New Exception("Errore API " & CInt(response.StatusCode) & ": " & errorText)
                    End If

                    File.WriteAllBytes("qrcode.png", body)
                    Console.WriteLine("QR Code salvato in qrcode.png")
                End Using
            End Using
        End Using
    End Function
End Module

Frontend — esempio sicuro tramite tuo backend

<form id="qrForm">
  <input id="qrContent" type="url" placeholder="https://www.tuosito.it" required>
  <button type="submit">Genera QR</button>
</form>

<img id="qrPreview" alt="QR Code generato" hidden>

<script>
document.getElementById("qrForm").addEventListener("submit", async (event) => {
  event.preventDefault();

  const content = document.getElementById("qrContent").value;
  const response = await fetch("/api/qr-proxy", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ content })
  });

  if (!response.ok) {
    throw new Error(await response.text());
  }

  const blob = await response.blob();
  const imageUrl = URL.createObjectURL(blob);
  const preview = document.getElementById("qrPreview");
  preview.src = imageUrl;
  preview.hidden = false;
});
</script>
Risposte ed errori 200, 401, 403, 422, 429, 500

Per png, jpeg, webp e svg, una richiesta valida restituisce direttamente il file generato. Per base64url, la risposta è JSON con il valore base64.

Status Quando accade Esempio risposta
200 QR Code generato correttamente. image/png, image/jpeg, image/webp, image/svg+xml oppure JSON per base64url.
422 content mancante o troppo lungo, formato, dimensione o colori non validi. { "status": 422, "code": "DIMENSIONE_NON_VALIDA", "message": "Dimensione non valida: numero intero tra 100 e 2000." }
401 Header x-api-key mancante. { "status": 401, "code": "CHIAVE_MANCANTE", "message": "Chiave API mancante. Includi l'header x-api-key." }
403 Chiave non valida (CHIAVE_NON_VALIDA) o piano API non attivo (PIANO_NON_ATTIVO). { "status": 403, "code": "PIANO_NON_ATTIVO", "message": "Il piano API di questa chiave non è attivo." }
429 Limite al minuto del piano (TROPPE_RICHIESTE_AL_MINUTO, con Retry-After in secondi), richieste del periodo finite (QUOTA_MENSILE_ESAURITA) o troppe richieste dallo stesso indirizzo (TROPPE_RICHIESTE). { "status": 429, "code": "TROPPE_RICHIESTE_AL_MINUTO", "message": "Hai superato le … richieste al minuto del tuo piano. Riprova tra … secondi." }
500 Errore interno durante la verifica della chiave, il conteggio delle richieste o la generazione del QR. { "status": 500, "code": "ERRORE_GENERAZIONE", "message": "Errore interno durante la generazione del QR Code. Riprova." }
Prima di andare in produzione

Conserva la chiave sul server, gestisci gli errori con code, rispetta Retry-After quando ricevi 429 e controlla i limiti del piano prima di automatizzare grandi volumi.