Prodotti Endpoint
Prodotti
Operazioni CRUD complete per i dati dei prodotti.
Panoramica
| Operazione | Metodo | Endpoint | Ambito |
|---|---|---|---|
| Esporta tutto | GET | /xpanel/xoport/export/json/products | products:read |
| Singolo prodotto | GET | /xpanel/xoport/export/json/product/{ID} | products:read |
| Intervallo ID | GET | /xpanel/xoport/export/json/products/{START}:{END} | products:read |
| Importazione | POST | /xpanel/xoport/import/json/products | products:write |
| Aggiorna | PUT | /xpanel/xoport/import/json/products | products:write |
| Elimina | DELETE | /xpanel/xoport/delete/json/products/{ID} | products:delete |
Esportazione (GET)
/product/{ID} (singolare) per un singolo prodotto.Modelli di URL
| URL | Descrizione |
|---|---|
/export/json/product/123 | Singolo prodotto con ID 123; se non vengono trovati risultati, viene restituito un errore HTTP 404 NOT_FOUND v2.122.10 |
/export/json/products | Tutti i prodotti (impaginati) |
/export/json/products/100:200 | Prodotti con ID da 100 a 200 |
/export/json/products/100: | Prodotti a partire dall'ID 100 (intervallo aperto, finora solo il prodotto 100) v2.122.10 |
/export/json/product/123%20 | Lo spazio vuoto alla fine dell’ID non viene conteggiato: prodotto 123 v2.122.12 |
segmento di percorso non valido: /export/json/product/123x, /product/123.0, /product/1e3, /product//123, /products/productlist | HTTP 400 INVALID_RANGE, il messaggio indica il segmento. Finora veniva visualizzato l’elenco dei prodotti o un articolo reinterpretato (1e3 = 1000) v2.122.12 |
A partire dalla v2.122.12, il segmento secondo /product o /products è un numero intero o un intervallo con limiti interi; inviare l’ID senza decimali e senza segno. Tutte le regole: API di esportazione, sezione «Segmento di percorso».
Parametri di query
| Parametri | Tipo | Descrizione |
|---|---|---|
limit | int | Numero massimo (Predefinito: 50, Massimo: 250) |
offset | int | Posizione iniziale per l'impaginazione |
bonus_free_features | 0/1 | Con 1, ogni prodotto include il blocco bonus_free_features (dati bonus e gratuiti) v2.122.10 |
bundles | 0/1 | Con 1, ogni prodotto presenta la chiave bundle (vedi pacchetti) v2.122.11 |
marketplaces | 0/1 | Con 1, ogni prodotto presenta marketplaces[], stato e posizione per canale (vedi marketplace per articolo) v2.122.11 |
fields | Elenco separato da virgola | Solo queste chiavi per ogni prodotto (vedi selezione campi) v2.122.11 |
seo_name | Elenco separato da virgola | Articoli sotto un unico indirizzo del negozio: slug effettivo, esatto, tutte le lingue v2.122.11 |
marketplace, marketplace_status, marketplace_upload_status, marketplace_sync_state | Testo, elenco separato da virgola | Filtri marketplace (vedi filtri marketplace) v2.122.11 |
A partire dalla v2.122.10, valori non validi e valori di elenco come ?limit[]=50 generano un errore HTTP 400 INVALID_FILTER_VALUE con details[].
A partire dalla v2.122.12, gli spazi vuoti ai margini non vengono conteggiati: ?bundles=%201 (così come marketplaces e bonus_free_features) restituisce il blocco; fino alla v2.122.11 mancava silenziosamente in quel punto. Un valore vuoto significa «non impostato».
A partire dalla v2.122.11, un valore composto esclusivamente da virgole su products_model, products_ean o seo_name genera un errore HTTP 400 INVALID_FILTER_VALUE (“holds no value”), così come un filtro di data composto esclusivamente da segni di punteggiatura come ?last_modified=.. In precedenza veniva restituito l’elenco non filtrato o solo le modifiche apportate da quel momento in poi. Gli altri filtri (status, categories_id, ean_prefix, last_modified …) sono disponibili nella sezione Export API, mentre la ricerca tramite l’indirizzo del negozio si trova nella sezione SEO History.
Richieste di esempio
# Einzelprodukt abrufen
curl -X GET "https://shop.de/xpanel/xoport/export/json/product/123" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Alle Produkte paginiert
curl -X GET "https://shop.de/xpanel/xoport/export/json/products?limit=50&offset=0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# ID-Range
curl -X GET "https://shop.de/xpanel/xoport/export/json/products/100:200" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Campi della risposta
Ogni prodotto contiene:
products_id,products_model,products_eanproducts_price,products_weight,products_quantity,products_costmanufacturers_id,products_statuscontract,contract_use_current_price,contract_optionv2.5.0products_inventory_management,products_min_qty,products_max_qty,products_qty_blocks,products_base_pricev2.5.0service,sitemap,non_cart,non_price,products_free_shippingv2.5.0shipping_profile,products_unit_id,base_unit_id,products_sort_orderv2.5.0images[]- Immagini dei prodotti con ordinamento. Gli articoli senza immagine principale non generano più una voce vuota conimages_id0 v2.122.10categories[]- ID delle categoriegroups[]- Prezzi per gruppi di clienticustomers_prices[]- Prezzi personalizzati per clientespecials[]- Prezzi specialiextra_fields[]- Campi aggiuntivibadges[]- Badge dei prodottixsells[]- Prodotti di cross-selling, completi e conformi asort_orderv2.122.9upsells[]- Suggerimenti nel carrello ("Abbinamenti consigliati") v2.18.0+tabs[]- Schede prodottobonus_free_features- solo con?bonus_free_features=1:status_bonus,bonus_points(prezzo in punti per pezzo),max_bonus,status_free,bonus_value(valore della merce per ogni articolo gratuito),max_free,auto_free; solo in letturabonus_date_added,bonus_last_modified,date_status_changed. Senza datinullv2.122.10bundle- solo con?bundles=1: impostazioni e componenti di un bundle, senza bundlenullv2.122.11marketplaces[]- solo con?marketplaces=1: una voce per canale con interruttore e stato v2.122.11videos[]- Video dei prodottisuppliers[]- Fornitori:suppliers_ideproducts_model(codice articolo del fornitore). L’elenco sostituisce tutte le assegnazioni dell’articolo; una voce senzaproducts_modelazzera il numero.storages[]- Stock per magazzino v2.63.0+languages{}- Nome, descrizione, SEO per lingua:products_name,products_description,products_short_descriptionproducts_head_title_tag,products_head_desc_tag,products_head_keywords_tagseo_name,products_url,products_checkout_description,products_image_title
languages.<code>.unit_single,unit_multi,unit_base- Unità per lingua, se specificata; se non specificata, "vuoto" anziché "null" v2.122.9options{}- Caratteristiche senza gruppo di caratteristiche (nome opzione + valori per lingua, dati SKU per valore)option_groups[]- gruppi di caratteristiche combinati (ad es. «colore + taglia» abbinati), dati SKU per ogni combinazione v2.58.0+contenteditor{}- Dati ContentEditor per lingua
Altri campi del prodotto
Oltre ai campi sopra indicati, l’esportazione fornisce le altre colonne dell’articolo; a partire dalla v2.105.0, la maggior parte di esse è anche modificabile. Per questi campi, il nome può trarre in inganno o la loro funzione va oltre quanto ci si aspetterebbe:
| Campo | Significato |
|---|---|
products_status | 0 inattivo, 1 attivo e visibile, 2 attivo, ma senza percorso di categoria sempre attivo: invisibile nel frontend, visualizzato come attivo nel backend. Inviare solo 0 o 1; il valore 2 viene impostato automaticamente dal negozio (a partire dalla v2.122.10 anche durante l’importazione, avviso NO_ACTIVE_CATEGORY_PATH). Se un’integrazione aggiorna le categorie tramite API, il negozio non sincronizza lo stato degli articoli; un’operazionePUT con "products_status": 1 provvede a farlo. |
amazon_allowed | ⚠️ Blocco per Amazon Pay, nonostante il nome: 1 = bloccato. Se nel carrello è presente un articolo di questo tipo, il negozio non mostra il pulsante «Express» di Amazon Pay (carrello, pagina di accesso, carrello laterale). Nella selezione dei metodi di pagamento alla cassa, Amazon Pay rimane selezionabile. Nessun pulsante «Marketplace». |
ebay_allowed | «Articoli per eBay» nel modulo eBay classico: solo 1 offre l’articolo in quella sezione. ⚠️ 0 termina un’offerta in corso al successivo aggiornamento. L’integrazione con eBay tramite i marketplace non valuta questo campo; in quel caso valgono le opzioni presenti in marketplaces[]. |
price_declaration_id | «Nota sugli sconti»: ID del testo che precede il prezzo barrato (impostazione predefinita 1, «Il nostro vecchio prezzo»). Appare solo se è in vigore un prezzo speciale o scontato. L’importazione non verifica se l’ID esiste; un ID sconosciuto non mostra alcun testo. |
products_xoport | Password per feed, marketplace e CSV-Porter, separate da punto e virgola, massimo 64 caratteri. Da un feed di prodotti si escludono gli articoli con &products_xoport=<Kennwort>&blacklist=true nell’indirizzo del feed. ⚠️ I feed e i marketplace effettuano la ricerca per parole parziali: meta corrisponde anche a ohnemeta. Nei marketplace, un’esclusione termina le offerte esistenti al successivo aggiornamento. |
lia_exclude | 1 = non presente nel feed «inventario locale», altrimenti non ha alcun effetto. Se il campo manca nell’esportazione di un negozio, non inviarlo: altrimenti l’importazione si interrompe con codice HTTP 500 e i record precedenti sono già stati scritti. |
importflag | ⚠️ Marchio di origine delle importazioni dal sistema di gestione delle merci. Un importatore considera gli articoli con il proprio marchio come propri e li elimina o li disattiva se mancano nella sua prossima esecuzione. Modificare solo consapevolmente. |
import_mask | Campo obsoleto, di sola lettura (READONLY_FIELD). Né il backend, né CSV-Porter, né questa API lo scrivono; non valutarlo. |
Selezione dei campi (?fields=) v2.122.11
A /products, /product/{ID}, /products/{ID} e nelle sezioni, ?fields= elenca le chiavi per ciascun prodotto come elenco separato da virgole. Il negozio carica solo i dati relativi a queste: una pagina con 250 articoli pesava nel test 4,3 MB senza e 52 KB con fields=products_model,products_price,products_quantity,products_status.
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"https://shop.de/xpanel/xoport/export/json/products?status=1&limit=250&fields=products_model,products_price,products_quantity"
# → "data": [{"products_id": 28, "products_model": 8624000, "products_price": 12.5, "products_quantity": 3}, …]- Sono valide le chiavi del livello superiore del record del prodotto: ogni colonna del prodotto (comprese le colonne personalizzate del negozio),
manufacturers_ide le chiavi dei blocchi dall’elenco sottostante.products_idè sempre incluso; l’ordine corrisponde a quello del record. - Nomi da
a–z,0–9e_, ciascuno fino a 64 caratteri. Massimo 100 elementi nell’elenco; si contano anche quelli vuoti e duplicati; un duplicato conta una sola volta. Unfields=vuoto o composto solo da virgole (fields=,) è considerato non impostato. - Mancanza totale anziché valore vuoto: nella risposta manca una chiave non specificata; non compare mai
[]onull. Allo stesso modo manca una chiave specificata che l’articolo non possiede, ad esempiospecialsenza prezzo speciale. È quindi possibile restituire una selezione tramitePUT, anche con?_strict=true, modificando solo ciò che contiene. - La colonna
shipping_profilefornisce solo l’ID del profilo; la risoluzione va specificata conshipping_profile_effective,shipping_profile_name,shipping_profile_steposhipping_profile_text. bundle,bonus_free_featuresemarketplaces: con?fields=il nome richiede il blocco senza flag. Un flag senza il nome non ha quindi alcun effetto (avvisoSWITCH_IGNORED).- Senza
?fields=la risposta rimane invariata. L'esportazione XML non riconosce il parametro.
Chiave del blocco: un blocco viene caricato solo se viene specificata una delle sue chiavi. A ogni chiave corrisponde un contenuto; se la chiave manca:
groups– Prezzi per gruppo di clienti, gruppo 0 dal prodottomanufacturers– Nomi dei produttori permanufacturers_idlanguages– Testi per lingua, comprese le unità di misuracustomers_prices– Prezzi per cliente; se manca, non viene visualizzato alcun prezzo per il clienterestricted_customer_groups– gruppi di clienti bloccati; se mancanti, senza bloccoimport_blocks– Blocchi di importazione; se mancanti, senza bloccocategories– Assegnazioni alle categorie; se mancanti, non viene visualizzatoimages– Immagini principali e aggiuntive;[]senza immagineextra_fields– Campi aggiuntivi;[]e senza campo solo con sistema di magazzino avanzato, altrimenti mancabadges– Badge; mancanti se non presentixsells– Cross-selling; mancante se non inseritoupsells– Up-selling; mancante se non inseritotabs– Schede in tutte le lingue; assenti senza schedespecial– Prezzo speciale; non disponibile senza prezzo specialeoptions– Caratteristiche senza gruppo; mancante senza caratteristicaoption_groups– Gruppi di caratteristiche combinati; mancanti senza gruppovideos– Video; non disponibile senza videosuppliers– Fornitori; mancante senza assegnazioneshipping_profile_effective,shipping_profile_name,shipping_profile_step,shipping_profile_text– Profilo a semaforo (un unico blocco per tutti e quattro): il profilo in vigore, il suo nome, il livello relativo alle scorte attuali e il testo relativo ai tempi di consegna; i quattro elementi mancano se il negozio non dispone di profili a semaforoquicksearch– Numero di voci di ricerca rapida per lingua ({}= non indicizzato)options_rules– Regole delle opzioni del configuratore; mancanti se non sono presenti regolecontenteditor– Dati del ContentEditor relativi alla descrizione; solo con licenza xoContentEditor e datistorages– Righe di magazzino; solo con sistema di magazzino avanzatobundle– Pacchetti,nullsenza pacchetti; senza?fields=solo con?bundles=1bonus_free_features– Dati relativi a bonus e omaggi,nullsenza voce; senza?fields=solo con?bonus_free_features=1marketplaces– una voce per canale; senza?fields=solo con?marketplaces=1
| Richiesta | Risposta |
|---|---|
?fields=produts_model,products_price | HTTP 200, products_price funziona; avviso UNKNOWN_FIELD con suggestion: "products_model" |
lo stesso con &_strict=true | HTTP 400 UNKNOWN_FIELD, nessun dato |
?fields=Products_Model, ?fields=languages.products_name, ?fields=-extra_fields, ?fields=*, ?fields[]=… o più di 100 elementi nell'elenco | HTTP 400 INVALID_FILTER_VALUE |
?bundles=1&fields=products_model | HTTP 200 senza bundle, avviso SWITCH_IGNORED |
?fields=, fino alla v2.122.10 riceveva l'intero set di dati e l'avviso UNKNOWN_FILTER. Ora vengono fornite solo le chiavi specificate e un elenco non valido genera un errore HTTP 400.Caratteristiche (opzioni)
options{} e option_groups[] (strutture riportate di seguito). A partire dalla v2.96.0, il catalogo delle caratteristiche, indipendentemente dal prodotto, è consultabile su GET /Export/JSON/products_options (elenco senza valori), GET /Export/JSON/products_option/{ID} (una caratteristica con i relativi valori) e GET /Export/JSON/products_options_values (ambito products:read); POST/PUT /Import/JSON/products_options crea e modifica le caratteristiche (ambito products:write). Una chiamata a /Export/JSON/products_attributes restituisce un errore HTTP 501 NOT_IMPLEMENTED; questo endpoint non esiste.A seconda del tipo di caratteristica, l’esportazione fornisce due strutture diverse:
| Campo | Contiene | I dati SKU (model/ean/quantity/price) appartengono al … |
|---|---|---|
options{} | Caratteristiche senza gruppo di caratteristiche | Valore singolo |
option_groups[] v2.58.0+ | gruppi di caratteristiche combinati, ad es. «colore + taglia» abbinati | Combinazione dei valori |
Ci sono volutamente due campi: per ogni gruppo di caratteristiche esiste uno SKU per ogni combinazione (“nero / L” = un EAN), non per ogni singolo valore. Una fusione dei campi genererebbe codici articolo inesistenti. Un prodotto può contenere entrambi i campi.
"option_groups": [
{
"group": 1,
"options": [
{ "id": 27, "languages": { "de": { "name": "Farbe" } } },
{ "id": 26, "languages": { "de": { "name": "Größe" } } }
],
"combinations": [
{
"detail_id": 2344,
"values": [
{ "options_id": 27, "options_values_id": 2483, "languages": { "de": { "name": "schwarz" } } },
{ "options_id": 26, "options_values_id": 304, "languages": { "de": { "name": "L" } } }
],
"model": "MS-01-0040-001-L",
"ean": "4001",
"quantity": "5",
"price": "0.0000",
"sort_id": 1
}
]
}
]
options[]sono nell’ordine di visualizzazione;combinations[].values[]segue lo stesso ordine — la posizione 0 è sempre il primo membro del gruppo.detail_id: 0I campi SKU vuoti indicano che per questa combinazione non è stato inserito nulla (non è un errore).- L’importazione continua a richiedere il formato
options[]non gerarchico (v2.39.0) — convertire lato client per i roundtrip.
Importazione (POST/PUT)
POST crea nuovi prodotti, PUT aggiorna quelli esistenti.
languages(in linea con l’esportazione). Con INSERT (POST), le lingue mancanti vengono automaticamente compilate con i dati della lingua principale (compilazione automatica).contract, contract_use_current_price), la logistica (products_cost, products_min_qty, products_ean) e i flag (service, sitemap, non_cart, non_price).specials[]. Sono supportati customers_group_id, specials_new_products_price o discount_percent, specials_begin/specials_end, specials_price_id/specials_price_name, specials_type e l'eliminazione tramite action: delete.upsells[]. Accetta un array di stringhe products_model o oggetti con {products_model, sort_order}. Semantica di sostituzione — le voci esistenti vengono sostituite. I riferimenti a se stessi e i modelli sconosciuti vengono ignorati implicitamente.warranty_period (una stringa vuota "" sovrascrive il valore predefinito 24m), products_xoport (indicatori, ad es. googleblacklist), images[] (immagini aggiuntive tramite nome file, sostituisce la galleria, inclusi i testi alternativi), storages[] (scorte per magazzino con la funzione «Magazzini multipli» attiva — campi, semantica e insidie nella sezione «Scorte di magazzino e scorte di segnalazione per magazzino») e downloads[] (download dei prodotti, d_src = file in files/). POST /media carica i file tramite entity_type=download su files/.warranty_type è ora importabile – sono ammessi physical e digital, la grafia viene corretta. Si applica la semantica di array_key_exists: se manca la chiave, la colonna rimane invariata; una stringa vuota "" la svuota. ⚠ Un valore sconosciuto viene rifiutato con un avviso in warnings[] e non viene corretto: il campo contribuisce a determinare se una voce è considerata merce o contenuto digitale.groups[]): per ogni prodotto è possibile importare prezzi diversi a seconda del gruppo di clienti (ad es. gruppi di rivenditori). groups è un array di oggetti (non un elenco di ID): ogni voce richiede customers_group_id (> 0) e, tramite customers_group_price, definisce il prezzo netto del gruppo — il prezzo lordo viene calcolato automaticamente in base alla classe fiscale. Prezzi scalari opzionali: products_price1…products_price8 + products_price1_qty…products_price8_qty. Upsert per ogni gruppo; i gruppi non inclusi rimangono invariati. Il gruppo 0 = «Cliente finale» viene impostato tramite products_price. Gli ID dei gruppi si ottengono tramite l’esportazione dei prodotti (campo groups).products_customers_prices) possono essere importati tramite customers_prices[] — inclusi i prezzi scalari e la cancellazione mirata di singoli prezzi per cliente.restricted_customer_groups importa solo i gruppi di clienti esistenti, sotto forma di numero intero. Se l’elenco non contiene alcun gruppo esistente o non è un elenco, i blocchi salvati rimangono invariati (avviso CUSTOMER_GROUP_RESTRICTION_KEPT); la presenza di voci sconosciute accanto a quelle note viene segnalata da CUSTOMER_GROUP. [] continua a rimuovere tutti i blocchi.- Una lista
import_blockssenza voci utilizzabili non modifica più i blocchi. È possibile impostare tutti i tipi di blocco del negozio. Novità:import_blocks_mode(replace,mergeoppureappend,remove) erecords[].import_blocks. I negozi meno recenti ignorano questa modalità, pertanto inviare solo insieme a?_strict=true. - Un
categories_modenon valido, una categoria sconosciuta,categoriesoimagessenza elenco e unimages[]senza voce utilizzabile causano il rifiuto del record prima della scrittura (INVALID_CATEGORIES_MODE,INVALID_CATEGORIES,INVALID_IMAGES). Un POST rifiutato non crea nulla; ogni rifiuto è indicato con un codice inrecords[]. options_replace,options_strict_names,apply_value_defaults,videos_replaceecustomers_prices_replacesi attivano solo tramitetrue,1,"1"o"true".- È possibile inviare la propria esportazione tramite
?_strict=true: solo i campi di sola lettura comequicksearcheoption_groupssegnalanoREADONLY_FIELD.
products_statusaccetta solo 0 o 1 (anche"0"/"1",true/false). Lo stato 2 viene impostato dal negozio stesso se l’articolo non ha un percorso di categoria attivo in tutto il suo percorso (avvisoNO_ACTIVE_CATEGORY_PATH). Un 2 inviato segnalaREADONLY_VALUE, un valore non validoINVALID_FIELD_VALUE; entrambi non vengono scritti, mentre il resto del record sì. Novità:records[].products_status.products_xoportaccetta al massimo 64 caratteri; un valore più lungo fa rifiutare il record.- Dati bonus e gratuiti tramite
bonus_free_features: chiave mancante = invariato,null= rimuovere, oggetto = solo i campi inviati. I valori non validi causano il rifiuto del record (INVALID_BONUS_FREE_FEATURES), viene rispettato il blocco di importazionebonus. Rispostarecords[].bonus_free_features:written,unchanged,removedoblocked. - Un record immagine «nessuna immagine principale» proveniente da esportazioni precedenti (
images_id0,images_filevuoto) viene ignorato; l’immagine principale salvata rimane. Per rimuoverla, utilizzare"products_image": "". - Blocco importazione
costper il prezzo di acquisto. Verificare primaapi_version: i negozi più vecchi non salvano il tipo e, fino alla versione 2.122.8, un elenco rimuove solo i blocchi esistenti dacost.
bundleviene elaborato: finora unbundleinviato veniva ignorato comeUNKNOWN_FIELD. Ora modifica il bundle e unbundlenon valido fa fallire l’intero record (INVALID_BUNDLE). Vedi Bundle.marketplaces[]scrive i pulsanti del marketplace per ciascun canale; finora la chiave eraUNKNOWN_FIELD. Anche un’esportazione rinviata con?marketplaces=1include i pulsanti. Vedi Marketplace per articolo.- È possibile restituire una selezione da
?fields=, modificando solo le chiavi specificate.
Prezzi personalizzati per cliente (customers_prices[]) v2.57.0
Per ogni prodotto è possibile importare prezzi diversi per singoli clienti. Il cliente viene identificato tramite customers_id o customers_id_extern (numero cliente WaWi).
{
"type": "products",
"data": [{
"products_model": "ABC-123",
"customers_prices": [
{"customers_id": 48, "customers_price": "9,99", "products_price1": 9.5, "products_price1_qty": 10},
{"customers_id_extern": "20074", "customers_price": 8.88},
{"customers_id": 55, "_action": "delete"}
]
}]
}- Partial Upsert: vengono scritte solo le colonne inviate. Disponibili:
customers_price(aliasprice),products_price1…products_price12+products_price1_qty…products_price12_qty(prezzi scalari),products_qty_blocks,products_min_qty,products_max_qty. - Indirizzamento dei clienti: tramite
customers_idocustomers_id_extern— il numero cliente del sistema di gestione delle merci deve essere univoco; i numeri ambigui generano un avviso e la voce viene saltata. _action: "delete"rimuove il prezzo del cliente in questione.customers_prices_replace: true= Sincronizzazione completa: prima vengono cancellati tutti i prezzi cliente del prodotto, poi vengono scritti quelli inviati. Secustomers_prices: []è vuoto, vengono rimossi tutti i prezzi cliente del prodotto.- Impostazioni predefinite per l’inserimento: se manca
customers_price, viene utilizzato il prezzo attuale del prodotto; blocchi di quantità e quantità minima = 1. customers_price≤ 0 viene ignorato (avviso) — La rimozione avviene esclusivamente tramite_action: "delete".
customers_prices — i dati esportati possono essere reimportati senza modifiche.Scorte di magazzino e livelli di allerta per magazzino (storages[]) v2.45.0
Quando il sistema multimagazzino è attivo, vengono gestiti le giacenze, le scorte di sicurezza, i livelli di allerta e gli spazi di stoccaggio per ciascun magazzino (tabella products_to_storages). L'array è presente dalla v2.34.0; a partire dalla v2.45.0 vengono scritti solo i campi effettivamente trasmessi — un payload parziale (ad es. solo products_reorder_level) non imposta più le restanti colonne di quel magazzino su 0.
| Campo | Tipo | Descrizione |
|---|---|---|
storages_id (obbligatorio) | int | ID del magazzino aggiuntivo dalla tabella storages (denominazione in storages_info, gestione nel backend alla voce Prodotti → Gestione magazzino). Alias: warehouse_id. |
products_quantity | decimal(16,8) | Stock presente in questo magazzino. |
products_safe_quantity | decimal(16,8) | Stock di sicurezza di questo magazzino. |
products_reorder_level | decimal(16,8) | Livello di segnalazione di questo magazzino. |
products_storage_text | varchar(50) | Posto di magazzino come testo libero, max. 50 caratteri. Non viene troncato automaticamente. |
options_id | int, valore predefinito 0 | Stock per caratteristica/variante. 0 = stock senza riferimento alla caratteristica. |
Impostare solo il livello di segnalazione di un magazzino — tutti gli altri valori di questo magazzino rimangono invariati:
{
"type": "products",
"data": [{
"products_model": "ABC-123",
"storages": [
{"storages_id": 3, "products_reorder_level": 10}
]
}]
}Esempio completo con due magazzini, una giacenza per caratteristica e una giacenza totale impostata separatamente:
{
"type": "products",
"data": [{
"products_model": "ABC-123",
"products_quantity": 42,
"storages": [
{"storages_id": 1, "products_quantity": 30, "products_safe_quantity": 2, "products_reorder_level": 10, "products_storage_text": "Regal A-04"},
{"storages_id": 3, "products_quantity": 12, "products_reorder_level": 5},
{"storages_id": 3, "options_id": 2483, "products_quantity": 4}
]
}]
}- Aggiornamento parziale (a partire dalla v2.45.0): vengono scritti solo i campi presenti nel payload (
INSERT … ON DUPLICATE KEY UPDATE). Fino alla v2.44.0, la riga del magazzino veniva sostituita tramiteREPLACE— le colonne omesse venivano inserite in0. - Chiave per ogni riga di magazzino:
(storages_id, products_id, options_id). Se la tripla è uguale, viene aggiornata la riga esistente; altrimenti viene inserita una nuova riga. - Nessuna replica completa: le righe di magazzino non presenti nell’array rimangono invariate. Non esistono né
storages_replacené_action: "delete"— la rimozione delle righe di magazzino è possibile solo nel backend. - No-Op senza dati utente: una voce che contiene solo la chiave non scrive nulla e, in particolare, non crea una riga con valori pari a zero.
nullNon è “invariato”: un campo con il valorenullè considerato trasmesso e scrive0. Se un valore deve rimanere invariato, omettete il campo.- Nuova riga di magazzino: inviare anche le giacenze. Se per la tripla non esiste ancora una riga, questa viene creata solo dai campi trasmessi. Pertanto, durante il primo popolamento, inviate
products_quantityeproducts_safe_quantitycon — altrimenti il server del database potrebbe rifiutare il record in base alla configurazione, senza che la risposta segnali un errore. options_id: Impostazione predefinita0. I valori > 0 gestiscono le giacenze di una combinazione di caratteristiche/varianti in questo magazzino; le giacenze di prodotto e di caratteristica sono righe separate.- Il separatore decimale è il punto:
"50.5"è valido,"50,5"viene letto come50. - Prerequisito
EXTENDED_STORAGE_SYSTEM = 'true': se il sistema di magazzino esteso è disattivato,storagesviene completamente ignorato; la risposta contiene l’avvisoSTORAGE_SYSTEM_DISABLED. - Magazzino sconosciuto o inattivo: un
storages_idche non esiste o il cui magazzino non è attivo genera l’avvisoSTORAGE_NOT_FOUND; la voce viene saltata, mentre le voci rimanenti vengono elaborate. - Attenzione — l’assenza di
storages_idviene silenziosamente ignorata: le voci prive distorages_id/warehouse_ido con un valore≤ 0vengono saltate senza avviso. L’importazione segnala quindi esito positivo, sebbene non sia stato registrato alcun stock.
storages: il livello di sicurezza «Magazzino principale» dall’editor dei prodotti corrisponde alla colonna del prodotto products.products_reorder_level — un campo diverso da storages[]. A partire dalla v2.63.0 è importabile insieme a products_safe_quantity come campo di primo livello del prodotto; nelle versioni precedenti solo tramite il backend o la colonna CSV/XML reorder_level. storages_id: 1 è già il primo magazzino aggiuntivo — chi si aspetta di trovare lì il livello di sicurezza del magazzino principale, sta inserendo i dati nel magazzino sbagliato.products_quantity non viene sommato dai magazzini: lo stock totale del prodotto è una colonna indipendente. Chi imposta gli stock di magazzino tramite storages deve inviare separatamente products_quantity nella stessa richiesta — altrimenti rimane il vecchio stock totale.storages[] con lo stesso set di campi; i dati di magazzino esportati sono quindi reimportabili senza modifiche. Se il sistema di magazzino esteso è disattivato, la chiave manca del tutto (non come array vuoto). Vengono restituite tutte le righe di magazzino del prodotto, comprese quelle relative ai magazzini inattivi — la loro reimportazione viene poi confermata da STORAGE_NOT_FOUND. Fino alla v2.62.0 l’esportazione non forniva le giacenze di magazzino; in quel caso occorreva utilizzare il CSV-Porter (colonne storage_<ID>_*) o il backend.Richiesta di esempio
curl -X POST "https://shop.de/xpanel/xoport/import/json/products" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "products",
"data": [{
"products_model": "TEST-001",
"products_price": 99.99,
"products_quantity": 50,
"manufacturers_id": 1,
"contract": 1,
"contract_use_current_price": 1,
"products_ean": "4012345678901",
"categories": [5, 12],
"groups": [
{"customers_group_id": 2, "customers_group_price": 1975.00},
{"customers_group_id": 3, "customers_group_price": 1975.00}
],
"suppliers": [{"suppliers_id": 3, "products_model": "LF-4711"}],
"specials": [{
"customers_group_id": 0,
"discount_percent": 10,
"specials_price_name": {"de": "Muttertag", "en": "Mother Day"},
"status": 1,
"specials_begin": "2026-05-05 00:00:00",
"specials_end": "2026-05-11 23:59:59"
}],
"languages": {
"de": {
"products_name": "Testprodukt",
"products_description": "Beschreibung...",
"products_head_title_tag": "SEO Titel",
"products_head_desc_tag": "Meta Description",
"seo_name": "testprodukt"
}
}
}]
}'Pacchetti v2.122.11
Composizione e impostazioni di un bundle sul prodotto, ambiti products:read e products:write. Fino alla v2.122.10 l’API REST non riconosceva il campo: un bundle inviato veniva ignorato come UNKNOWN_FIELD, ?bundles=1 segnalava UNKNOWN_FILTER.
Esportazione
Solo su richiesta: GET /products?bundles=1, anche /product/{ID} e aree (solo 0 o 1, altrimenti HTTP 400 INVALID_FILTER_VALUE). Con questo parametro, ogni prodotto della pagina porta la chiave: null senza bundle, altrimenti le impostazioni e items[] esattamente nella forma in cui vengono lette dall’importazione. Con ?fields=, il nome bundle richiede il blocco; ?bundles=1 senza il nome non funziona (avviso SWITCH_IGNORED).
Importazione
{ "type": "products", "data": [ { "products_model": "SET-100",
"bundle": { "bundle_status": 1, "bundle_cart_listing": 1,
"items": [ { "products_model": "SHIRT-1", "qty": 1, "options_name": "Farbe > Größe", "options_value": "Rot > XL" },
{ "uprid": "4711{3}12{5}40", "qty": 2 } ] } } ] }bundle | Effetto |
|---|---|
| Chiave mancante | Il bundle rimane invariato |
null oppure {"delete": true} | Rimuovere il bundle, le impostazioni e la composizione |
Oggetto con items[] | impostazioni (solo quelle inviate) e componenti come copia completa: i componenti non specificati vengono omessi |
Oggetto senza items o con items | solo le impostazioni |
Tutto o niente per ogni record: la struttura, le impostazioni e ogni componente vengono verificati prima della scrittura. Un componente sconosciuto, un riferimento a se stesso, una quantità non valida, una selezione degli attributi o un’impostazione fanno fallire il record con INVALID_BUNDLE; details[] indica i motivi e non viene scritto nulla, nemmeno il prezzo dello stesso record.
Impostazioni
| Campo | Significato |
|---|---|
bundle_status | Bundle attivo; un nuovo bundle inizia con 1 |
bundle_category_listing, bundle_product_listing, bundle_cart_listing | Mostra i componenti nella categoria, nella pagina del prodotto e nel carrello |
bundle_image_listing, bundle_options_listing | visualizza immagini o caratteristiche dei componenti |
bundle_save_in_pieces, bundle_main_as_product | Componenti singoli nell’ordine, articolo principale come prodotto a sé stante |
bundle_spread_status, bundle_auto_qty | trasferire lo stato ai componenti, creare le scorte a partire dai componenti |
bundle_copy_to_slaves | Nel prodotto principale: le impostazioni e la composizione vengono applicate alle varianti attive |
bundle_auto_price, bundle_auto_price_percent | Non disponibile in Shop 4.9: avviso BUNDLE_SETTING_UNAVAILABLE, nessun testo |
Valori: 1/0, true/false, ja/nein, yes/no, on/off; null = non specificato. Gli altri pulsanti di un nuovo pacchetto sono disattivati.
Componenti (items[])
| Campo | Significato |
|---|---|
products_id oppure products_model | componente per ID (priorità) o codice articolo |
uprid | Selezione di prodotto e selezione degli attributi in un unico campo, ad esempio 4711{3}12{5}40 o solo {3}12{5}40; se abbinato a products_id o products_model, gli ID devono corrispondere; se abbinato a campi di caratteristica, deve risultare la stessa selezione |
qty | Quantità maggiore di 0, vuoto = 1 |
sort | Ordine, vuoto = posizione in items[] |
options_name e options_value | caratteristiche per nome nella lingua predefinita, più voci separate da “>” (Farbe > Größe); più valori di una casella di controllo separati da virgola |
options_id e options_value_id | caratteristiche per ID, più di una separate da “_”; gli ID hanno la precedenza |
Le voci identiche (prodotto e selezione) vengono unite in una riga con la quantità sommata. Un componente non può essere il pacchetto stesso.
Risposta
records[].bundle:written,settings,removedonone.stats.bundlesconprocessed,written,settingseremoved, se la pila recava l’indicazione «bundle».INVALID_BUNDLEcondetails[], ad esempioitem 2: NOCOMPONENT SHIRT-9,item 1: QTY 0oSETTING bundle_cart_listing.
- L'esportazione converte i codici articolo puramente numerici e un
upridsenza selezione in numeri; l'importazione accetta entrambi. Chi ha bisogno di testo, imposta?numeric_check=false. - Negozi che provengono da una versione precedente alla 4.9.155: dopo l’aggiornamento, aprire una volta il backend prima che l’integrazione crei i bundle. In caso contrario, un bundle con due combinazioni di caratteristiche dello stesso prodotto si interrompe.
Marketplace per articolo v2.122.11
La scheda «Piattaforme di vendita» della schermata dell’articolo abilita un articolo per ciascun canale; l’elenco degli articoli multicanale mostra lo stato di caricamento, lo stato di verifica e lo stato «attuale» o «in sospeso». A partire dalla v2.122.11 è possibile leggere, scrivere e filtrare questi dati tramite API. Fino alla v2.122.10 l’API non riconosceva questo blocco.
Lettura
Solo su richiesta: GET /products?marketplaces=1, anche /product/{ID}, /products/{ID} e le sezioni (solo 0 o 1, altrimenti HTTP 400 INVALID_FILTER_VALUE). Ogni articolo presenta una voce per ogni canale del negozio, in ordine crescente secondo marketplace_id, anche se non pubblicato (in tal caso status 0). Con ?fields=, il nome marketplaces richiede il blocco; ?marketplaces=1 senza il nome non funziona (avviso SWITCH_IGNORED).
GET /xpanel/xoport/Export/JSON/product/4711?marketplaces=1
{"products_id": 4711, "products_model": "ART-1", "…": "…",
"marketplaces": [
{"marketplace_id": 2, "code": "idealo", "name": "Idealo", "licensed": true, "adapter": true,
"status": 1, "upload_status": "done", "verify_status": null, "sync_state": "pending",
"last_updated": "2026-10-13 23:00:00", "last_error_at": null,
"external_id": "ART-1", "external_link": null, "asin": null, "live": null, "messages": []},
{"marketplace_id": 9, "code": "amazon", "name": "Amazon", "licensed": true, "adapter": true,
"status": 1, "upload_status": "failed", "verify_status": null, "sync_state": "in_sync",
"last_updated": "2026-10-14 11:00:00", "last_error_at": "2026-10-14 11:00:00",
"external_id": "SKU-4711", "external_link": "https://www.amazon.de/dp/B0C1234567", "asin": "B0C1234567", "live": null,
"messages": [{"code": "90220", "message": "Das Attribut brand fehlt.", "field": null}]}
]}| Campo | Significato |
|---|---|
marketplace_id | ID del canale in questo negozio; può variare da negozio a negozio |
code | Identificativo stabile del canale (elenco in basso); è questo che permette di riconoscere il canale, non il nome |
name | Nome visualizzato nella lingua della richiesta |
licensed | Canale attivato nel negozio: modulo multicanale acquistato e canale attivato |
adapter | false: il negozio non dispone di un collegamento per questa voce; potrebbe essere presente un pulsante, ma non viene trasmesso nulla |
status | Pulsante della scheda «Mercati»: 1 abilitato, 0 no. Modificabile |
upload_status | Stato della trasmissione, come salvato, un elenco aperto: done, pending, failed, error, inactive (Idealo: offerta cancellata, dati del canale conservati) oppure null (mai trasmesso o offerta cancellata). OTTO registra anche gli stati propri delle proprie attività di caricamento; non è possibile filtrare in base a questi. |
verify_status | Stato di verifica del canale, così come salvato (eBay, OTTO) |
sync_state | in_sync, pending, not_listed oppure not_tracked, regola in fondo |
last_updated | Ultimo timbro della voce, non necessariamente una sincronizzazione; 1970-01-01 00:00:00 = contrassegnato per la sincronizzazione |
last_error_at | Momento dell’ultimo errore; successivamente la sincronizzazione rimane inattiva per un giorno. Il messaggio si trova in messages |
external_id, external_link | L'offerta sul canale (SellerSKU, Item-ID, ID offerta …) e il relativo link |
asin | Solo Amazon: l’ASIN dal link |
live | In Shop 4.9 sempre null |
messages[] | messaggi del canale relativi all’ultimo trasferimento come {code, message, field}, mai la risposta grezza |
sync_state | Significato |
|---|---|
in_sync | Offerta presente e l’articolo non è stato modificato dall’ultimo timbro della voce; lo stesso secondo è considerato attuale. Non significa «trasferito»: per sapere se è arrivato qualcosa, consultare upload_status e messages. |
pending | Offerta disponibile, articolo modificato successivamente: «non aggiornato» nell’elenco degli articoli |
not_listed | Nessuna offerta sul canale |
not_tracked | Canale senza stato: Galaxus, Shöpping.at e voci senza collegamento |
I canali Mirakl non necessitano di un ID offerta: in questo caso lo stato confronta solo i momenti temporali e una riga salvata non è mai not_listed. L’interruttore status non viene applicato in sync_state.
0) privi di immagine, senza GTIN (laddove il canale lo richieda), con EAN o MPN duplicati, presenti in una lista di blocco, con blocco ospite o al di fuori dei limiti di categoria del canale. Idealo esclude gli articoli con scorte pari a 0 (0, upload_status inactive ) e riprende proprio queste offerte al momento del rifornimento del magazzino. La sincronizzazione delle inserzioni (Amazon, Kaufland) utilizza 1, mentre su eBay si utilizza “Elimina prodotti” 0; a ciò si aggiungono la maschera articolo, il CSV-Porter e l’importazione XML.Scrivere
POST/PUT /products con la chiave marketplaces imposta l’interruttore per ciascun canale (ambito products:write). Cambiano solo i canali inviati; se la chiave manca, tutto rimane invariato. Una voce identifica il canale tramite code o marketplace_id e contiene status: 1/0, true/false o "1"/"0". I campi di lettura dell’esportazione vengono ignorati: un’esportazione con ?marketplaces=1 viene restituita invariata, così come con ?_strict=true. La scrittura avviene solo per i canali con licenza presenti nello shop.
PUT /xpanel/xoport/Import/JSON/products
{"type": "products", "data": [{"products_id": 4711, "marketplaces": [
{"code": "idealo", "status": 1},
{"marketplace_id": 9, "status": false},
{"code": "manor", "status": "1"}
]}]}
→ "stats": {"…": "…", "marketplaces": {"processed": 1, "written": 1, "unchanged": 0, "skipped": 0}},
"records": [{"index": 1, "products_id": 4711, "status": "updated", "marketplaces": "written"}]| Avviso | Quando |
|---|---|
INVALID_MARKETPLACE | marketplaces non c’è un elenco, una voce non è un oggetto, oppure non indica alcun canale di questo negozio |
INVALID_MARKETPLACE_STATUS | status manca o non è 0/1 |
MARKETPLACE_NOT_LICENSED | canale senza licenza, e la voce modificherebbe l'interruttore salvato |
Gli avvisi non interrompono nulla: il record e le sue restanti voci continuano a funzionare, l’impostazione salvata rimane invariata. Una voce con 0 per un canale senza autorizzazione non crea nulla. La risposta segnala per ogni record records[].marketplaces (written o unchanged) e in batch stats.marketplaces.
- Timbro: un
PUTcontenente solo l'ID articolo (products_id,products_model) emarketplacesnon modifica la data di modifica dell'articolo; non compare nel delta?last_modified. Ogni riga del canale modificata ricevelast_updated1970-01-01 00:00:00e, alla successiva esecuzione, sincronizza proprio quel canale. Se è presente un'offerta (su Mirakl anche senza), mostra quindisync_statepending; in assenza di offerta rimanenot_listed. Se il record contiene altri campi modificati, l'articolo viene contrassegnato come in precedenza. - Effetto sul canale (al successivo allineamento, non in tempo reale): su Amazon, eBay, Kaufland e Idealo,
1crea un’offerta, mentre0la rimuove. I canali Mirakl come Manor o Decathlon trasmettono solo con dati Mirakl aggiornati (brand,category) e non rimuovono l’articolo. OTTO e Galaxus seguono il prossimo export completo; Shöpping.at inserisce l’articolo con il prossimo feed, ma non lo rimuove. Il negozio non trasmette Check24, Etsy e le inserzioni senza collegamento. - Idealo: un ritiro (
0) rimane valido, anche in presenza di scorte. Se il negozio ha disattivato autonomamente un’offerta a causa di scorte pari a 0, la ripristina al rifornimento del magazzino, purché per l’articolo sia attivata l’opzione «Riattiva automaticamente al rifornimento del magazzino»; l’invio di unstatus0non modifica questa impostazione. Un'offerta disattivata dal filtro, ad esempio perché priva di immagine, non viene ripristinata automaticamente una volta risolto il problema: inviare nuovamentestatus1.
PUT riporta i pulsanti allo stato dell’esportazione. Inviare quindi marketplaces solo se si desidera modificare i pulsanti, oppure recuperare l’esportazione senza ?marketplaces=1. Al contrario, una maschera articolo aperta in precedenza e successivamente salvata ripristina l’impostazione dell’interruttore al proprio stato: dopo un’approvazione tramite API, ricaricare le maschere aperte. Inoltre, la sincronizzazione successiva può annullare un valore impostato (filtro, stock Idealo): dopo un PUT, consultare status e upload_status.Filtri del marketplace
«Tutti gli articoli Idealo non aggiornati» in un’unica richiesta anziché in tutto il catalogo: ?marketplace= indica il canale (ID o code, elenco separato da virgole fino a 100 valori), a cui si aggiunge almeno una condizione. ?marketplaces=1 non è necessario in questo caso. Fino alla versione v2.122.10 i nomi venivano ignorati come UNKNOWN_FILTER e l’elenco veniva restituito senza filtri.
| Parametri | Valori |
|---|---|
marketplace_status | 1 approvati, 0 no (anche senza riga salvata) |
marketplace_upload_status | done, pending, failed, error, inactive, none (senza stato); elenco separato da virgole |
marketplace_sync_state | in_sync, pending, not_listed, not_tracked; elenco separato da virgole |
# alle nicht aktuellen Idealo-Artikel
GET /Export/JSON/products?marketplace=idealo&marketplace_sync_state=pending&limit=250
# „ausstehend“ der Artikelliste: freigegeben und aktiv
GET /Export/JSON/products?marketplace=idealo&marketplace_status=1&marketplace_sync_state=pending&status=1
# Amazon-Artikel mit fehlgeschlagenem Upload („fehlgeschlagen“ der Artikelliste)
GET /Export/JSON/products?marketplace=amazon&marketplace_upload_status=error,failed
# freigegeben, aber noch nie übertragen (ohne Upload-Status), auf einem der Mirakl-Kanäle
GET /Export/JSON/products?marketplace=manor,decathlon&marketplace_status=1&marketplace_upload_status=none- Tutte le condizioni si applicano allo stesso canale; se vengono indicati più canali, ne basta uno. I valori di un elenco sono alternativi.
- Il filtro «
sync_state» non è attivo: «pending» individua anche un articolo disattivato la cui offerta è ancora presente. Per «in sospeso», come nell’elenco degli articoli, occorre quindi aggiungere «marketplace_status=1» e «status=1». - Un articolo senza riga salvata per il canale conta come 0 su
status, mentre suupload_status,noneesync_stateconta rispettivamente comenot_listedonot_tracked. miraklnon indica alcun canale; i canali Mirakl hanno codici propri.stats.totalconta i risultati. L’elenco degli articoli nel backend nasconde inoltre gli articoli che il confronto non trasferisce, ad esempio quelli senza immagine o con EAN duplicato; tali articoli sono presenti nella risposta, ma non nell’elenco.
| Caso | Risposta |
|---|---|
Condizione senza ?marketplace= o ?marketplace= senza condizione | HTTP 400 INVALID_FILTER_VALUE |
canale sconosciuto (?marketplace=idelo, ?marketplace=mirakl) | HTTP 400 INVALID_FILTER_VALUE, il messaggio riporta i codici del negozio |
Valore non presente nell'elenco (?marketplace_status=2, ?marketplace_upload_status=DONE) | HTTP 400 INVALID_FILTER_VALUE, il messaggio indica i valori validi |
a /products/{ID}, /product/{ID} o un intervallo | HTTP 400 FILTER_NOT_APPLICABLE |
Codici
code | Canale |
|---|---|
galaxus | Galaxus |
idealo | Idealo |
otto | OTTO |
check24 | Check24 |
ebay | eBay (integrazione multicanale) |
etsy | Etsy |
kaufland | Kaufland |
microspot | Microspot (solo negozi più vecchi, senza integrazione) |
amazon | Amazon |
shoeppingat | Shöpping.at |
manor, decathlon, conrad, mediamarkt, galeria, obi, ottooffice, home24 | Canali Mirakl |
Elimina (DELETE)
Il prodotto viene identificato tramite il proprio ID nel percorso: DELETE /xpanel/xoport/delete/json/products/{ID}. Senza ID, l'API risponde con un codice HTTP 400 MISSING_ID.
Esempio
curl -X DELETE "https://shop.de/xpanel/xoport/delete/json/products/123" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"