Un endpoint REST che riceve il PDF del listino di un fornitore e restituisce le righe con le stesse chiavi per ogni fornitore — codice, descrizione, prezzo, um — più il payload già pronto per products/batch del WooCommerce del tuo cliente. L’import lo scrivi una volta sola, e la mappatura verso il negozio non la scrivi affatto.
Questo strumento non è ancora disponibile: lo stiamo preparando con la stessa cura del resto del motore.
Guarda un esempio di risultato
Stato di oggi, senza giri di parole
Il contratto qui sotto è quello vero e il motore dietro esiste (è lo stesso che serve lo strumento web Listino → JSON, in produzione), ma l’accesso programmatico della piattaforma è ancora chiuso: le chiamate con chiave rispondono 401 e la sezione « Chiavi API » di /app/ non compare finché non viene aperto. Non è un dettaglio da scoprire dopo: se ti serve convertire un listino adesso, usa lo strumento web — stesso motore, stesso prezzo, un caricamento a mano. Questa pagina esiste perché tu possa preventivare l’integrazione prima che l’interruttore si apra, e sapere esattamente cosa riceverai.
Per chi è
Per chi deve importare listini molte volte, senza un umano che carichi il file: software house e freelance che fanno integrazioni per PMI con decine di fornitori, chi gestisce cataloghi WooCommerce o PrestaShop di terzi, chi ha un flusso automatico (n8n, Make, un cron) che pesca gli allegati dalla casella. Se hai un listino da convertire, lo strumento web costa uguale e non richiede codice.
Il contratto
Modello asincrono: si crea un lavoro, si legge l’esito quando è pronto. Base REST https://megaengine.it/wp-json/mega-engine/v1/, autenticazione con chiave nell’header X-ME-API-Key. Il file va in multipart/form-data nel campo file: non base64, non un URL da scaricare.
# 1. crea il lavoro
curl -X POST https://megaengine.it/wp-json/mega-engine/v1/jobs
-H "X-ME-API-Key: $ME_KEY"
-F "product=parse-price-list-api"
-F "file=@listino-fornitore.pdf"
-F "idempotency_key=listino-acme-2026-09"
# 201 Created
{ "job_id": "a34ea62c-87eb-4e4d-9d0e-8fafe42a3cd0", "status": "queued" }
# 2. leggi l’esito (polling)
curl https://megaengine.it/wp-json/mega-engine/v1/jobs/a34ea62c-…
-H "X-ME-API-Key: $ME_KEY"
idempotency_key è la difesa contro il doppio addebito: se il tuo retry ripete la stessa chiave, il lavoro non viene creato due volte e i crediti si spendono una volta sola. Gli scope della chiave sono jobs:write per creare e jobs:read per leggere.
Una precisazione che altrove troveresti scritta al contrario: gli scope sono per azione, non per prodotto. Una chiave con jobs:write può creare lavori su qualunque strumento del tuo account, compresi quelli che costano più crediti di questo. Non consegnare una chiave a un sistema di terzi come se fosse limitata a questo endpoint: oggi non lo è.
Codici di errore: 401 chiave assente, non valida o accesso programmatico chiuso · 402 crediti insufficienti · 403 scope mancante · 400 prodotto o file non accettabile (estensione diversa da .pdf, file oltre 15 MB) · 429 oltre il limite di richieste · 503 motore non disponibile. Il corpo dell’errore porta un codice leggibile a macchina e una descrizione in italiano.
Che cosa torna
La risposta del lavoro concluso porta summary (le voci del riepilogo: righe estratte, colonne, pagine elaborate), preview con le prime dieci righe come stanno nel documento, quality e download con nome, dimensione, checksum SHA-256 e scadenza del file. Il JSON completo — con il blocco import e il payload per il negozio — sta nel file che download indica, non nella risposta del job: sono due chiamate, e questa è la seconda.
Dentro il file, in cima, c’è il blocco import. I nomi dei campi sono sempre questi, qualunque intestazione porti il PDF, e mappatura dice quale colonna del documento è diventata quale campo:
"import": {
"verdetto": "da_controllare",
"riepilogo": "Un codice articolo è ripetuto su 2 righe…",
"articoli_totali": 4,
"chiavi": ["codice","ean","descrizione","categoria","marca",
"um","prezzo","sconto_percento","prezzo_lordo","iva_percento"],
"mappatura": { "codice": {"key":"cod_art","intestazione":"Cod. Art."},
"prezzo": {"key":"prz_netto","intestazione":"Netto"} },
"campi_non_presenti": ["quantita_minima"],
"colonne_non_mappate": [],
"controlli": { "codici_ripetuti": [{"codice":"FX-9","volte":2}],
"righe_senza_codice": [3], "prezzi_non_numerici": [4] },
"per_piattaforma": { … },
"articoli": [ { "codice": "FX-9", "descrizione": "Tubo rame 15mm",
"prezzo": 8.4, "um": "mt", "iva_percento": 22 } ]
}
Il verdetto è la prima cosa da leggere e vale per il tuo import, non per la nostra lettura: importabile oppure da_controllare sui tre difetti che fanno cadere un import a gestionale — codici ripetuti, righe senza codice, prezzi che numero non sono. Sono conteggi fatti sulle righe lette, non stime.
Il pezzo che lo strumento web non fa: il payload per il tuo negozio
Chiavi sempre uguali risolvono metà del problema: l’import lo scrivi una volta sola, ma quella volta la scrivi. Il blocco per_piattaforma.woocommerce.batch è già il corpo di POST /wp-json/wc/v3/products/batch del negozio, da mandare così com’è. Una chiamata a noi, una al WooCommerce del cliente, zero righe di mappatura:
"per_piattaforma": { "woocommerce": {
"endpoint": "POST /wp-json/wc/v3/products/batch",
"articoli_nel_payload": 3,
"batch": { "create": [
{ "sku": "FX-9",
"name": "Tubo rame 15mm",
"regular_price": "8.4",
"categories": [ { "name": "Idraulica" } ],
"meta_data": [ { "key": "_me_unita_misura", "value": "mt" },
{ "key": "_me_ean", "value": "8012345678901" },
{ "key": "_me_iva_percento", "value": "22" },
{ "key": "_me_sconto_percento","value": "30" } ] } ] },
"campi_nativi": { "codice": "sku", "descrizione": "name",
"prezzo": "regular_price", "categoria": "categories" },
"csv_intestazioni": ["SKU","Name","Regular price","Categories",
"Meta: _me_unita_misura","Meta: _me_ean"]
} }
Questo blocco non è un mock: è uscito dal motore su due listini con intestazioni senza un byte in comune (Codice/Cod. Art., Prezzo EUR/Netto), e l’uscita è la stessa. Tre scelte dentro, che valgono più del blocco:
- I nomi dei campi sono verificati sul sorgente di WooCommerce, non ricordati:
sku,name,regular_priceecategoriesstanno nello schema diWC_REST_Products_Controller, eregular_priceè dichiarato di tipo stringa — per questo i prezzi nel payload sono stringhe col punto decimale. Le intestazioni CSV sono quelle diwc_importer_default_english_mappings(), che WooCommerce registra come fallback: l’importer le riconosce da solo anche su un negozio in italiano. - Due dati diversi non si travestono da uno. La quantità minima d’ordine non è la giacenza: su
stock_quantitymetterebbe in vendita centinaia di articoli con uno stock che non esiste. L’aliquota IVA non è unatax_class, che in WooCommerce è il nome di una classe d’imposta. Vanno inmeta_data, dove non fanno danno, ecampi_in_metadice perché ce li abbiamo messi. - Un articolo senza codice non entra nel payload (senza
skul’import non sa se creare o aggiornare, e alla seconda passata il negozio si riempie di doppioni), e un prezzo che numero non è — « a richiesta », « n.d. » — non diventa mai0: l’articolo arriva senza prezzo invece di finire in vendita a zero euro. Quelle righe restano tutte inarticoli, e i contatori del verdetto le nominano.
products/batch di WooCommerce accetta cento articoli per chiamata: il payload porta i primi cento e lo dichiara in nota_taglio, con gli altri tutti presenti in articoli da mandare a blocchi dello stesso formato. PrestaShop e Shopify non sono ancora in questo blocco.
Quanto costa
2 crediti per chiamata, cioè da 0,98 € (pacchetto da 100 crediti a 49 €) a 1,96 € (ricarica minima: 4 crediti a 3,90 €). Il prezzo è per documento, non per pagina: un listino di 4 pagine e uno di 80 costano uguale. I 3 crediti di benvenuto bastano per una prova.
Il confronto onesto, misurato il 27/09/2026. Sul costo puro non vinciamo: un servizio cloud di layout sta intorno a 0,26 € per un listino di 30 pagine, e dare lo stesso PDF a un modello linguistico a token costa fra 0,02 € e 0,50 €. Se hai un fornitore, tempo e pazienza, quella strada costa meno: scritto qui perché è vero. Vinciamo su due cose diverse. La prima è il rischio che quella strada nasconde: su un elenco lungo un modello estrae le prime decine di voci e poi si ferma — LlamaIndex ha documentato perdite fino all’ 80% delle voci di un catalogo, con un caso di 18 voci lette su 153 — e lo fa in silenzio, restituendo un JSON perfettamente valido. Qui le righe si contano: il riepilogo dice quante sono, il verdetto dice se si importano, e le righe che a perdere siamo stati noi non le paghi. La seconda è che nessuno dei diciotto fornitori censiti ha un modello pre-addestrato per i listini (hanno fatture, ricevute, documenti d’identità): lo schema te lo definisci tu, fornitore per fornitore. E nessuno consegna il payload per l’importer di WooCommerce. Sul mercato italiano il servizio che oggi occupa il primo risultato per « api estrarre dati da pdf » è a 0,49 € per pagina: lo stesso listino di 30 pagine, lì, costa 14,70 €.
Input supportati
Listini in PDF con testo selezionabile (generati digitalmente) fino a 15 MB e circa 80 pagine per elaborazione, anche con sezioni di categoria intercalate tra gli articoli: le loro intestazioni non spezzano l’elenco e non diventano articoli, ma non compaiono fra i dati del JSON — il riepilogo dell’elaborazione le elenca una per una, con il testo che il documento riporta. Quando in un listino ce ne sono troppe per stare in una nota leggibile, il riepilogo ne elenca quante ce ne stanno e ti dice quante ne restano fuori dall’elenco: il numero complessivo non viene mai taciuto. I listini scansionati (immagini) non sono ancora supportati: in quel caso l’elaborazione viene segnalata subito come non riuscita e non ti viene addebitato nulla. Un listino in parte digitale e in parte scansionato (una pagina fotografata, un allegato acquisito con lo scanner) viene invece elaborato e addebitato, perché il testo delle altre pagine c’è: in quel caso il risultato ti dice quali pagine non contenevano testo selezionabile e che il loro contenuto non è in questo risultato, invece di dichiararsi completo. Se invece la parte digitale non contiene tabelle — per esempio è generata al computer solo la copertina, e il resto è tutto scansionato — non resta niente da consegnare: il lavoro viene segnalato come non riuscito, ti diciamo quante pagine su quante erano senza testo selezionabile e non ti viene addebitato nulla. I PDF protetti da password vengono rifiutati allo stesso modo: senza la password le pagine non si aprono, l’elaborazione si ferma subito e non ti viene addebitato nulla. Apri il file con la sua password, salvane una copia senza protezione e carica quella. Le 80 pagine sono il tetto massimo, non la regola: quando le righe non stanno in una griglia netta la lettura passa dall’intelligenza artificiale, che riceve al massimo circa 280.000 caratteri di testo — quanto ottanta pagine di media. I due tetti sono tarati per mordere nello stesso punto: misurato l’8 settembre 2026 su dieci documenti di gara italiani veri, è esattamente quanto testo portano ottanta pagine. Solo su un documento molto più fitto della media quel limite arriva prima dell’ottantesima pagina. Il risultato dichiara sempre a quale pagina la lettura si è fermata, e il messaggio ti dice da quale pagina ripartire. Quando il documento prosegue oltre il taglio la parte letta ti viene consegnata lo stesso, con il taglio dichiarato: il lavoro si conclude e i crediti restano spesi, anche se i dati che cercavi stavano oltre. Puoi mandare come chiamata separata un PDF con le sole pagine che ti servono.
C’è una seconda perdita possibile, indipendente da questi due tetti: dentro le pagine lette l’estrazione assistita dall’intelligenza artificiale può smettere di elencare prima della fine, restituendo articoli giusti ma pochi. Ce ne accorgiamo confrontando il risultato consegnato con due letture indipendenti dello stesso documento: la griglia che legge le tabelle riga per riga e, quando la griglia non riconosce la struttura del documento, il conteggio delle righe del testo estratto che hanno la forma di una riga di tabella. Quando una delle due si scosta dal risultato, il file lo dichiara con entrambi i conteggi e si considera incompleto. Quando una delle due lo dice, la perdita è nostra e non del tuo documento: il file ti arriva lo stesso e non lo paghi — il riepilogo lo dice con le parole «non te lo facciamo pagare» e i crediti ti tornano indietro da soli. Non è però una garanzia: su un documento fatto in prevalenza di prosa, con qualche tabella dentro, nessuna delle due letture sa dire quante ne dovevano tornare, e una perdita lì non te la segnala nessuno. Il controllo che costa dieci secondi è questo: import.articoli_totali dice quanti articoli sono entrati nel file, e l’ultima pagina di un listino di solito scrive quanti ne ha. Rimandare il PDF a blocchi di poche pagine per volta è il modo più efficace per recuperare gli articoli mancanti.
C’è infine un tetto che non riguarda il documento ma la nostra risposta: gli articoli tornano indietro in un’unica risposta del modello, e quella risposta ha uno spazio finito. Un documento che ne contiene moltissime lo esaurisce. Se lo esaurisce prima che ne arrivi una parte utilizzabile, il lavoro viene segnalato come non riuscito, il messaggio ti dice che il documento contiene più voci di quante riusciamo a restituirne in una volta sola e non ti viene addebitato nulla; se lo esaurisce dopo, il risultato ti viene consegnato con il taglio dichiarato fra le note, e non lo paghi: i crediti ti tornano indietro. In entrambi i casi il gesto è lo stesso: mandare il PDF a blocchi di poche pagine per volta.
Tetti che non riguardano il documento ma il tuo account, e su un’integrazione contano quanto i primi: 50 lavori al giorno per account (circa 1.500 al mese) e, per ogni chiave, 60 richieste al minuto come valore predefinito. Esistono inoltre tetti di spesa giornaliera lato piattaforma che possono sospendere le elaborazioni per tutti fino a mezzanotte UTC: non dipendono dal tuo traffico, e per questo oggi non c’è un SLA. Il file del risultato resta disponibile 14 giorni, quello di un lavoro fallito viene cancellato dopo 2 giorni, e il PDF che invii serve all’elaborazione e non viene conservato dopo.
Chi paga quando qualcosa va storto
Se l’elaborazione fallisce, i crediti tornano automaticamente sul tuo saldo. E se il documento era leggibile ma a perdere delle righe siamo stati noi, quelle righe non le paghi: il lavoro si chiude, il riepilogo dichiara quante e dove, e la quota corrispondente viene riaccreditata. È la stessa regola dello strumento web. Non promettiamo invece un rimborso quando il limite dichiarato qui sopra è il tuo documento a superarlo — le 80 pagine, le pagine scansionate di un PDF misto: in quel caso ricevi la parte letta e la paghi, e il riepilogo lo dice.
Trattamento dati e AI
La lettura parte da codice deterministico. Quando la struttura della tabella è incerta, il testo delle pagine viene inviato a OpenAI per ricostruire le colonne, e sui listini capita spesso: un listino fornitore contiene prezzi e sconti riservati, quindi devi saperlo prima di mandarci i documenti dei tuoi clienti. Il trattamento può avvenire anche negli Stati Uniti e i log anti-abuso del fornitore possono conservarne una copia per un periodo limitato, indicato nella nostra informativa; non vengono usati per addestrare modelli. Il dettaglio sta in Trattamento dati & AI e in Privacy. Nessun dato inventato: ciò che l’AI ricostruisce sono le colonne di celle già lette, e una cella che non c’è resta vuota invece di essere indovinata.
Se integri per conto di terzi, leggi questo prima: oggi non firmiamo un accordo di nomina a responsabile del trattamento (art. 28 GDPR) e le nostre pagine legali si dichiarano da verificare prima dell’apertura commerciale continuativa. Se il tuo cliente ti chiede un DPA sulla catena dei fornitori, oggi non possiamo dartelo. Scritto qui perché lo scopra ora e non a integrazione fatta.
Domande frequenti
Posso usare l’endpoint oggi?
No: l’accesso programmatico della piattaforma è chiuso e le chiamate con chiave rispondono 401. Il motore e il formato della risposta descritti qui sono però quelli veri, già in produzione sullo strumento web: quando l’interruttore si apre, l’integrazione che scrivi contro questa pagina funziona senza modifiche.
Perché l’API se esiste già lo strumento web?
Per due motivi, e se non sono i tuoi conviene lo strumento web. Primo: nessuno deve caricare il file a mano, quindi « ogni listino che arriva, mentre arriva, dentro il catalogo » diventa possibile. Secondo: solo qui la risposta porta per_piattaforma, cioè il payload già pronto per products/batch di WooCommerce. Il prezzo è lo stesso.
Come mi autentico, e dove prendo la chiave?
Con una chiave nell’header X-ME-API-Key, generata da /app/. La sezione esiste nel codice ma compare solo quando l’accesso programmatico è attivo: oggi, se la cerchi, non la trovi — e non è un errore tuo. Le chiavi sono conservate solo come hash: se ne perdi una va rigenerata, non recuperata.
Accetta CSV o XLSX? E un URL da cui scaricare il file?
No, e fino a oggi questa pagina diceva il contrario: accetta solo PDF, inviato in multipart/form-data. Né base64 né URL remoti. Se il fornitore ti manda già un CSV o un XLSX hai in mano un file strutturato: non ti serviamo noi.
C’è un campo « valuta »?
No, e nemmeno questo lo promettiamo più: i campi canonici sono codice, ean, descrizione, categoria, marca, um, quantita_minima, prezzo, sconto_percento, prezzo_lordo, iva_percento. Un campo entra in articoli solo se una colonna del documento lo nomina; gli altri stanno in campi_non_presenti, così sai che mancano prima dell’import invece di scoprirlo dopo.
Mi date fattura?
No. Il gestore è una persona fisica senza partita IVA e non emette fattura tramite la piattaforma: per un’azienda la spesa non è deducibile e l’IVA non è recuperabile. Su un prodotto rivolto a professionisti e software house è un limite grosso, e va detto prima del codice, non dopo.
Che succede se mando due volte la stessa richiesta?
Se ripeti lo stesso idempotency_key, il lavoro non viene creato due volte e i crediti si spendono una volta sola: è la difesa da usare nei retry automatici.