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 / BLite | SQLite | Tipo | Significato |
|---|---|---|---|
admCode | adm_code | intero, chiave | Codice AAMS (ADM) del prodotto. Stabile nel tempo; è la chiave per i codici a barre. |
name | name | testo | Denominazione commerciale come riportata nella tariffa. |
category | category | testo | Categoria della tariffa (ad esempio «Sigarette», «Sigari», «Trinciati per sigarette»). |
brand | brand | testo | Marchio, 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. |
packaging | packaging | testo | Descrizione originale della confezione, ad esempio «Confezione da 20 pezzi». |
packSize | pack_size | decimale o nullo | Quantità ricavata da packaging; nulla se il testo non si riconosce. |
packUnit | pack_unit | pieces, grams o nullo | Unità di packSize. |
pricePerKg | price_per_kg | decimale (euro) | Prezzo al chilogrammo convenzionale della tariffa. |
priceCents | price_cents | intero (centesimi) | Prezzo di vendita al pubblico della confezione in vigore oggi. |
nextPriceCents | next_price_cents | intero o nullo | Prezzo che entrerà in vigore a nextEffectiveFrom. |
nextPricePerKg | next_price_per_kg | decimale o nullo | Prezzo al chilogrammo futuro. |
nextEffectiveFrom | next_effective_from | data ISO o nulla | Decorrenza del prezzo futuro (giorno, ora italiana). |
status | status | active o delisted | delisted: il prodotto non è più in tariffa ma resta nel dataset per le giacenze. |
Codici a barre
| JSON / BLite | SQLite | Tipo | Significato |
|---|---|---|---|
barcode | barcode | testo, chiave | Codice GS1 a 8, 12, 13 o 14 cifre, con cifra di controllo valida. |
admCode | adm_code | intero | Prodotto abbinato. |
kind | kind | pack o carton | Pacchetto singolo oppure stecca o cartone. |
unitsPerBarcode | units_per_barcode | intero | Pezzi 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
| Campo | Significato |
|---|---|
schemaVersion | Versione della forma dei dati, oggi 2. Uguale in tutti i formati e nel manifest. |
version | Versione della release, AAAAMMGG.n. |
tariffDate | Data di decorrenza della tariffa in vigore. |
generatedAt | Data 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 perpackSizequandopackUnitèpieces.- Se una tariffa ha decorrenza futura, il prezzo nuovo è in
nextPriceCentscon la data innextEffectiveFrom: applicalo da quel giorno (ora italiana). Il giorno della decorrenza viene comunque pubblicata una release con il prezzo già in vigore e i campinext*azzerati. packSizeepackUnitderivano dal testo dipackaging; 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).
| Collezione | Documento | Chiave | Indici |
|---|---|---|---|
tobacco_products | TobaccoProductDoc: Id, AdmCode, Name, Category, Brand, Packaging, PackSize, PackUnit, PricePerKg, PriceCents, NextPriceCents, NextPricePerKg, NextEffectiveFrom, Status | Id = codice AAMS come stringa | AdmCode, Name |
tobacco_barcodes | TobaccoBarcodeDoc: Id, Barcode, AdmCode, Kind, UnitsPerBarcode | Id = codice a barre | AdmCode |
tobacco_meta | TobaccoMetaDoc: Id, SchemaVersion, BliteVersion, Version, TariffDate, GeneratedAt, Source | Id = "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.