OpenTabacchi

Formati e schema dei dati

Ogni release produce tre file con lo stesso contenuto: JSON, SQLite e BLite, tutti compressi con gzip. Scegli quello più comodo per il tuo stack; i campi e il loro significato sono identici. Tutti gli esempi usano dati di fantasia.

I campi e il loro significato

Prodotti

JSON / BLiteSQLiteTipoSignificato
admCodeadm_codeintero, chiaveCodice AAMS (ADM) del prodotto. Stabile nel tempo; è la chiave per i codici a barre.
namenametestoDenominazione commerciale come riportata nella tariffa.
categorycategorytestoCategoria della tariffa (ad esempio «Sigarette», «Sigari», «Trinciati per sigarette»).
brandbrandtestoMarchio, ricavato dall'inizio della denominazione (di norma la prima parola, ma anche «LA GALERA» o «H. UPMANN») e corretto a mano quando serve. Non è un campo della tariffa ufficiale: per gli stessi prodotti può cambiare tra una release e l'altra se un marchio viene corretto. In maiuscolo, mai vuoto.
packagingpackagingtestoDescrizione originale della confezione, ad esempio «Confezione da 20 pezzi».
packSizepack_sizedecimale o nulloQuantità ricavata da packaging; nulla se il testo non si riconosce.
packUnitpack_unitpieces, grams o nulloUnità di packSize.
pricePerKgprice_per_kgdecimale (euro)Prezzo al chilogrammo convenzionale della tariffa.
priceCentsprice_centsintero (centesimi)Prezzo di vendita al pubblico della confezione in vigore oggi.
nextPriceCentsnext_price_centsintero o nulloPrezzo che entrerà in vigore a nextEffectiveFrom.
nextPricePerKgnext_price_per_kgdecimale o nulloPrezzo al chilogrammo futuro.
nextEffectiveFromnext_effective_fromdata ISO o nullaDecorrenza del prezzo futuro (giorno, ora italiana).
statusstatusactive o delisteddelisted: il prodotto non è più in tariffa ma resta nel dataset per le giacenze.

Codici a barre

JSON / BLiteSQLiteTipoSignificato
barcodebarcodetesto, chiaveCodice GS1 a 8, 12, 13 o 14 cifre, con cifra di controllo valida.
admCodeadm_codeinteroProdotto abbinato.
kindkindpack o cartonPacchetto singolo oppure stecca o cartone.
unitsPerBarcodeunits_per_barcodeinteroPezzi contenuti: 1 per pack, da 2 a 1000 per carton.

Nel dataset compaiono solo i codici a barre approvati; quelli da confermare, in conflitto o revocati non vengono pubblicati. Vedi Codici a barre.

Metadati

CampoSignificato
schemaVersionVersione della forma dei dati, oggi 2. Uguale in tutti i formati e nel manifest.
versionVersione della release, AAAAMMGG.n.
tariffDateData di decorrenza della tariffa in vigore.
generatedAtData e ora di costruzione della release (ISO 8601).
source«Agenzia delle Dogane e dei Monopoli (ADM)».

Prezzi e decorrenza

  • priceCents è il prezzo di vendita al pubblico di una confezione, in centesimi di euro, valido oggi. Per il prezzo unitario di un pezzo dividi per packSize quando packUnit è pieces.
  • Se una tariffa ha decorrenza futura, il prezzo nuovo è in nextPriceCents con la data in nextEffectiveFrom: applicalo da quel giorno (ora italiana). Il giorno della decorrenza viene comunque pubblicata una release con il prezzo già in vigore e i campi next* azzerati.
  • packSize e packUnit derivano dal testo di packaging; se il formato non si riconosce restano nulli e va usato il testo.
  • Un prodotto che esce dalla tariffa passa a status: "delisted" con l'ultimo prezzo noto, invece di sparire: così puoi continuare a gestire giacenze e resi. Non venderlo come se fosse in tariffa.

JSON

GET /v1/dataset.json.gz. Un unico oggetto con tre proprietà: meta, products (ordinati per codice AAMS) e barcodes (ordinati per codice). Nomi in camelCase, valori nulli presenti esplicitamente, numeri decimali come numeri JSON.

{
  "meta": {
    "version": "20261014.2", "tariffDate": "2026-10-14", "generatedAt": "2026-10-14T09:00:00+00:00",
    "source": "Agenzia delle Dogane e dei Monopoli (ADM)", "schemaVersion": 2
  },
  "products": [
    { "admCode": 12345, "name": "PRODOTTO DI ESEMPIO", "category": "Sigarette", "brand": "PRODOTTO",
      "packaging": "Confezione da 20 pezzi", "packSize": 20, "packUnit": "pieces",
      "pricePerKg": 270.00, "priceCents": 540,
      "nextPriceCents": 560, "nextPricePerKg": 280.00, "nextEffectiveFrom": "2026-11-01",
      "status": "active" },
    { "admCode": 12346, "name": "ALTRO PRODOTTO DI ESEMPIO", "category": "Trinciati per sigarette", "brand": "ALTRO",
      "packaging": "Busta da 30 grammi", "packSize": 30, "packUnit": "grams",
      "pricePerKg": 210.00, "priceCents": 630,
      "nextPriceCents": null, "nextPricePerKg": null, "nextEffectiveFrom": null,
      "status": "delisted" }
  ],
  "barcodes": [
    { "barcode": "4006381333931", "admCode": 12345, "kind": "pack", "unitsPerBarcode": 1 },
    { "barcode": "4006381333948", "admCode": 12345, "kind": "carton", "unitsPerBarcode": 10 }
  ]
}

SQLite

GET /v1/dataset.sqlite.gz. Un database SQLite 3 con tre tabelle; i nomi delle colonne sono in snake_case. PRAGMA user_version vale schemaVersion. Le date sono testo ISO (AAAA-MM-GG).

CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
CREATE TABLE products (
    adm_code INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    category TEXT NOT NULL,
    brand TEXT NOT NULL,
    packaging TEXT NOT NULL,
    pack_size REAL,
    pack_unit TEXT,
    price_per_kg REAL NOT NULL,
    price_cents INTEGER NOT NULL,
    next_price_cents INTEGER,
    next_price_per_kg REAL,
    next_effective_from TEXT,
    status TEXT NOT NULL);
CREATE INDEX ix_products_name ON products (name);
CREATE INDEX ix_products_brand ON products (brand);
CREATE TABLE barcodes (
    barcode TEXT PRIMARY KEY,
    adm_code INTEGER NOT NULL,
    kind TEXT NOT NULL,
    units_per_barcode INTEGER NOT NULL);
CREATE INDEX ix_barcodes_adm_code ON barcodes (adm_code);

La tabella meta contiene le chiavi schemaVersion, version, tariffDate, generatedAt e source. Esempio d'uso alla cassa:

SELECT p.adm_code, p.name, p.price_cents, b.kind, b.units_per_barcode
FROM barcodes b JOIN products p ON p.adm_code = b.adm_code
WHERE b.barcode = '4006381333931';

BLite

GET /v1/dataset.blite.gz. Un file per chi usa il database embedded BLite in .NET: si apre direttamente, senza importazione, con un DocumentDbContext che dichiara le stesse collezioni, gli stessi indici e la stessa dimensione di pagina (32 KB, PageFileConfig.Large).

CollezioneDocumentoChiaveIndici
tobacco_productsTobaccoProductDoc: Id, AdmCode, Name, Category, Brand, Packaging, PackSize, PackUnit, PricePerKg, PriceCents, NextPriceCents, NextPricePerKg, NextEffectiveFrom, StatusId = codice AAMS come stringaAdmCode, Name
tobacco_barcodesTobaccoBarcodeDoc: Id, Barcode, AdmCode, Kind, UnitsPerBarcodeId = codice a barreAdmCode
tobacco_metaTobaccoMetaDoc: Id, SchemaVersion, BliteVersion, Version, TariffDate, GeneratedAt, SourceId = "meta"—

I tipi dei campi sono quelli del JSON (decimal? per PackSize, string? per NextEffectiveFrom in formato AAAA-MM-GG, int per i centesimi).

Controlla le versioni prima di scaricare. BLite non verifica il formato del file in apertura: un file scritto con una versione diversa del pacchetto può aprirsi senza errori e restituire dati sbagliati. Nel manifest di /v1/version il blocco files.blite riporta bliteVersion (versione del pacchetto BLite usata per scrivere il file) e schemaVersion: scarica il file BLite solo se entrambe coincidono con quelle del tuo software; altrimenti usa il JSON o lo SQLite, che non hanno questo vincolo.

var manifest = await GetVersionAsync();                       // GET /v1/version
var blite = manifest.Files.Blite;
if (blite.BliteVersion != MyBliteVersion || blite.SchemaVersion != MySchemaVersion)
    return await DownloadAsync(manifest.Files.Json.Url);         // ripiego senza vincoli
return await DownloadAsync(blite.Url);

Versioni dello schema

schemaVersion cambia solo se cambiano i campi o il loro significato; oggi vale 2. L'aggiunta di un campo facoltativo non è esclusa anche a schema invariato: un importatore robusto ignora i campi che non conosce. La versione della release (version) cambia invece a ogni variazione dei dati ed è quella da confrontare ogni giorno.