Sviluppatori

API 4pricing per sviluppatori

Collega il tuo sistema (CRM, ERP, negozio online) a 4pricing e interroga i prezzi della concorrenza, i gruppi di confronto e gli avvisi direttamente dal tuo codice.

URL di base: https://4pricing.net/api/v1/pricing · Specifica OpenAPI (JSON)

Integrazione rapida in 3 passaggi

Questo è il modo più breve per vedere i tuoi dati 4pricing sul tuo sito. Copierai un file, ci incollerai la tua chiave e lo caricherai sul tuo hosting — nessun framework, nessuna installazione.

Cosa ti serve
  • Un abbonamento 4pricing attivo (l'API è inclusa in ogni piano).
  • Un hosting con PHP — quasi tutti gli hosting condivisi ce l'hanno (cPanel, Plesk e simili).
  • Un modo per caricare un file: il Gestore file del tuo hosting o un programma FTP.
  1. Passaggio 1: Crea la tua chiave API

    Accedi a panel.4pricing.net e apri Price Monitoring → API & keys.

    Fai clic su “Generate key”, dagli un nome (per esempio “il mio negozio”) e copia la chiave che inizia con prc_live_. Viene mostrata una sola volta: se la perdi, creane un'altra e revoca quella vecchia.

    Apri API & keys nel pannello →

  2. Passaggio 2: Copia il file pronto e incolla la tua chiave

    Copia qui sotto il file PHP in un editor di testo (Blocco note, TextEdit o VS Code) e salvalo come 4pricing-prezzi.php.

    php4pricing-prezzi.php
    <?php
    // =====================================================================
    // INCOLLA QUI LA TUA CHIAVE API (panel.4pricing.net → API & keys), tra le virgolette
    $apiKey = 'INCOLLA_QUI_LA_TUA_CHIAVE';
    // =====================================================================
    
    header('Content-Type: text/html; charset=utf-8');
    
    function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }
    function money($cents) {
        if ($cents === null) { return '—'; }
        return number_format($cents / 100, 2, ',', '.');
    }
    
    if (strpos($apiKey, 'prc_') !== 0) {
        exit('<p>' . h('La chiave API manca: apri il file e incolla la tua chiave sulla riga indicata.') . '</p>');
    }
    
    $ch = curl_init('https://4pricing.net/api/v1/pricing/groups/');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_USERAGENT => 'my-shop-integration/1.0',
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Accept: application/json'],
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($body === false) {
        exit('<p>' . h('Impossibile connettersi a 4pricing:' . ' ' . curl_error($ch)) . '</p>');
    }
    if ($status !== 200) {
        $hints = [401 => 'la chiave API è sbagliata o è stata revocata', 402 => 'l\'account non ha un abbonamento 4pricing attivo (oppure i crediti non bastano)', 429 => 'troppe richieste in un minuto, riprova tra un minuto'];
        $hint = isset($hints[$status]) ? ' — ' . $hints[$status] : '';
        exit('<p>' . h('4pricing ha risposto con un errore' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>');
    }
    
    $groups = json_decode($body, true);
    $verdicts = [
        'cheapest' => 'Sei il più economico',
        'too_cheap' => 'Molto più economico del mercato — puoi alzare il prezzo',
        'competitive' => 'Competitivo',
        'too_expensive' => 'Più caro della maggior parte dei concorrenti',
        'no_competitors' => 'Ancora nessun prezzo della concorrenza',
        'no_own_price' => 'Manca il tuo prezzo',
    ];
    
    echo '<!doctype html><html><head><meta charset="utf-8"><title>' . h('I miei prodotti monitorati') . '</title>';
    echo '<style>body{font-family:Arial,sans-serif;margin:24px}table{border-collapse:collapse}';
    echo 'th,td{border:1px solid #ddd;padding:6px 10px;text-align:left}th{background:#f5f5f5}</style>';
    echo '</head><body><h1>' . h('I miei prodotti monitorati') . '</h1>';
    
    if (!$groups) {
        echo '<p>' . h('Non hai ancora prodotti confrontati.') . '</p>';
    } else {
        echo '<table><tr><th>' . h('Prodotto') . '</th><th>' . h('SKU') . '</th>';
        echo '<th>' . h('Il tuo prezzo') . '</th><th>' . h('Concorrente più economico') . '</th>';
        echo '<th>' . h('Differenza') . '</th><th>' . h('Verdetto') . '</th></tr>';
        foreach ($groups as $g) {
            $yours = $g['own_price_cents_effective'];
            $cheapest = $g['min_competitor_cents'];
            $diff = '—';
            if ($yours !== null && $cheapest !== null) {
                $diff = ($yours > $cheapest ? '+' : '') . money($yours - $cheapest);
            }
            $verdict = isset($verdicts[$g['verdict']]) ? $verdicts[$g['verdict']] : $g['verdict'];
            echo '<tr><td>' . h($g['name']) . '</td><td>' . h($g['sku']) . '</td>';
            echo '<td>' . money($yours) . '</td><td>' . money($cheapest) . '</td>';
            echo '<td>' . $diff . '</td><td>' . h($verdict) . '</td></tr>';
        }
        echo '</table>';
    }
    echo '</body></html>';
    
    Sostituisci INCOLLA_QUI_LA_TUA_CHIAVE con la tua chiave, mantenendo le virgolette attorno. È l'unica riga che devi modificare.
  3. Passaggio 3: Carica il file e aprilo nel browser

    Carica 4pricing-prezzi.php sul tuo sito con il Gestore file o via FTP, nella stessa cartella del sito (di solito public_html).

    Apri https://il-tuo-sito/4pricing-prezzi.php nel browser. Vedrai una tabella con ogni prodotto confrontato: il tuo prezzo, il prezzo più basso della concorrenza, la differenza e il verdetto.

Il file: elenca i tuoi prodotti con il prezzo più basso della concorrenza

Il file chiede a 4pricing i tuoi prodotti confrontati (GET /groups/) e li mostra in una tabella. La lettura dei dati è gratuita e non consuma crediti.

  • “Il tuo prezzo” è il prezzo che 4pricing conosce per te; “Concorrente più economico” è il prezzo più basso trovato tra i concorrenti che monitori.
  • I prezzi sono mostrati in unità (12999 centesimi diventano 129,99); un trattino (—) significa che 4pricing non ha ancora un prezzo, non che il prezzo è zero.
  • Se al posto della tabella vedi un errore, il messaggio ti dice cosa correggere: chiave sbagliata, nessun abbonamento attivo o troppe richieste in un minuto.

Seconda ricetta: invia il tuo prezzo

Usala quando 4pricing deve conoscere il tuo prezzo senza leggerlo dalla pagina del tuo negozio — per esempio direttamente dal database del negozio. L'invio dei tuoi prezzi è gratuito (nessun credito) e non viene conteggiato nel limite di URL monitorati del tuo piano.

CampoSignificato
skuil codice del tuo prodotto; 4pricing trova il prodotto tramite questo codice, oppure lo crea la prima volta
nameil nome del prodotto, come vuoi vederlo in 4pricing
urlfacoltativo: l'indirizzo della pagina del prodotto nel tuo negozio (non viene mai letto)
price_centsil prezzo moltiplicato per 100, senza decimali: 49,99 → 4999
currencyil codice della valuta, per esempio EUR, GBP o RON (RON se manca)
php4pricing-invia-prezzo.php
<?php
// =====================================================================
// INCOLLA QUI LA TUA CHIAVE API (panel.4pricing.net → API & keys), tra le virgolette
$apiKey = 'INCOLLA_QUI_LA_TUA_CHIAVE';
// =====================================================================

// I TUOI PRODOTTI: un blocco per ogni prodotto. price_cents = prezzo x 100 (49,99 diventa 4999).
$products = [
    [
        'sku' => 'MAGLIETTA-BIANCA-M',
        'name' => 'Maglietta bianca, taglia M',
        'url' => 'https://il-tuo-negozio.example/maglietta-bianca-m',
        'price_cents' => 4999,
        'currency' => 'EUR',
    ],
];

header('Content-Type: text/html; charset=utf-8');

function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }

if (strpos($apiKey, 'prc_') !== 0) {
    exit('<p>' . h('La chiave API manca: apri il file e incolla la tua chiave sulla riga indicata.') . '</p>');
}

$ch = curl_init('https://4pricing.net/api/v1/pricing/own-products/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT => 'my-shop-integration/1.0',
    CURLOPT_TIMEOUT => 60,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json', 'Accept: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['items' => $products], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($body === false) {
    exit('<p>' . h('Impossibile connettersi a 4pricing:' . ' ' . curl_error($ch)) . '</p>');
}
if ($status !== 200) {
    $hints = [401 => 'la chiave API è sbagliata o è stata revocata', 402 => 'l\'account non ha un abbonamento 4pricing attivo (oppure i crediti non bastano)', 429 => 'troppe richieste in un minuto, riprova tra un minuto'];
    $hint = isset($hints[$status]) ? ' — ' . $hints[$status] : '';
    exit('<p>' . h('4pricing ha risposto con un errore' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>');
}

$result = json_decode($body, true);
echo '<h1>' . h('Prezzi inviati a 4pricing') . '</h1>';
echo '<p>' . h('Nuovi prodotti creati:') . ' ' . (int) $result['created'] . '</p>';
echo '<p>' . h('Prodotti aggiornati:') . ' ' . (int) $result['updated'] . '</p>';
if (!empty($result['errors'])) {
    echo '<p>' . h('Righe con problemi:') . '</p><ul>';
    foreach ($result['errors'] as $error) {
        echo '<li>' . h($error) . '</li>';
    }
    echo '</ul>';
}
  • Salvalo come 4pricing-invia-prezzo.php, incolla la tua chiave, modifica le righe dei prodotti, caricalo e aprilo una volta: la pagina mostra quanti prodotti sono stati creati e quanti aggiornati.
  • Ogni richiesta può contenere fino a 2.000 prodotti; una riga errata viene segnalata e le altre vengono comunque salvate. Inviando di nuovo lo stesso sku, il prezzo viene aggiornato.

Le stesse chiamate con curl

Se preferisci il terminale, questi due comandi fanno esattamente ciò che fanno i due file PHP:

bash
curl https://4pricing.net/api/v1/pricing/groups/ \
  -H "X-API-Key: INCOLLA_QUI_LA_TUA_CHIAVE"
bash
curl -X POST https://4pricing.net/api/v1/pricing/own-products/bulk \
  -H "X-API-Key: INCOLLA_QUI_LA_TUA_CHIAVE" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"sku":"MAGLIETTA-BIANCA-M","name":"Maglietta bianca, taglia M","url":"https://il-tuo-negozio.example/maglietta-bianca-m","price_cents":4999,"currency":"EUR"}]}'
Tieni al sicuro la chiave
  • La chiave dà accesso a tutto il tuo account 4pricing. Conservala solo in file lato server (PHP, il tuo backend) — mai nel JavaScript del browser né in un repository pubblico.
  • Chiunque conosca l'indirizzo della pagina caricata può vedere la tabella dei prezzi (non la chiave). Dai al file un nome difficile da indovinare, proteggi la cartella con una password oppure eliminalo quando non ti serve più.
  • Se una chiave viene divulgata, revocala in API & keys — smette di funzionare subito — e creane una nuova.

Autenticazione

  • Ogni richiesta porta la tua chiave nell'header X-API-Key. Funziona anche Authorization: Bearer prc_live_… (solo per le chiavi che iniziano con prc_).
  • Non esistono scope: una chiave può leggere e modificare tutto nel tuo account 4pricing. Crea una chiave per ogni integrazione (negozio, ERP, report), così puoi revocarne una senza fermare le altre.
  • Una chiave mancante, errata o revocata riceve 401. La revoca ha effetto immediato. Troppe chiavi non valide in un minuto ricevono 429.
  • Le chiavi si creano, si elencano e si revocano solo nel pannello (Price Monitoring → API & keys); l'API stessa non può gestire le chiavi. Per creare una chiave serve un abbonamento 4pricing attivo.
http
X-API-Key: prc_live_…
# or
Authorization: Bearer prc_live_…

URL di base e convenzioni

  • Ogni percorso in questa documentazione è relativo all'URL di base — /groups/ significa https://4pricing.net/api/v1/pricing/groups/.
  • Richieste e risposte sono in JSON (Content-Type: application/json).
  • Gli importi sono numeri interi nelle unità minori (centesimi): price_cents = 12999 significa 129,99. Le percentuali sono in punti base (100 = 1%).
  • Un valore sconosciuto è null, mai 0 — mostralo come “—”.
  • Le collezioni si chiamano con la barra finale (/products/, /groups/, /insights/).
  • Le liste paginate restituiscono il totale nell'header X-Total-Count (per esempio GET /products/?skip=0&limit=200).
  • Invia un header User-Agent che indichi la tua integrazione (per esempio my-shop-integration/1.0). Il firewall davanti all'API risponde 403 ad alcuni valori predefiniti generici delle librerie, come Python-urllib.

Limite di richieste, abbonamento e limiti del piano

Limite di richieste

  • 60 richieste al minuto per ACCOUNT, in una finestra scorrevole — tutte le chiavi dello stesso account condividono gli stessi 60.
  • Ogni risposta riporta X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (la durata della finestra, in secondi).
  • Oltre il limite ricevi 429 con un header Retry-After (secondi). Attendi per quel tempo, poi riprova — gli esempi qui sotto lo fanno per te.
http
HTTP/2 200
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
x-ratelimit-reset: 60

HTTP/2 429
retry-after: 60
{"detail":"Rate limit exceeded"}

Abbonamento e limiti del piano

  • L'API richiede un abbonamento 4pricing attivo; senza di esso ogni richiesta riceve 402 con il codice plan_required.
  • Ogni piano include l'API con gli stessi limiti del pannello. Superare un limite quando aggiungi o riattivi qualcosa restituisce 402 con il codice plan_limit e un messaggio che indica quale limite.
LimiteSignificato
URL monitoratiOgni indirizzo di pagina prodotto che 4pricing legge viene contato separatamente: la tua pagina e ogni pagina della concorrenza. I prezzi che invii con /own-products non vengono contati.
Negozi concorrentiIl numero di siti web concorrenti distinti (domini) che monitori.
Intervallo minimo di verificaOgni quanto lo stesso URL può essere controllato. Gli intervalli più brevi vengono portati automaticamente al minimo del tuo piano; aggiornare un URL troppo presto restituisce 402 plan_limit.

Quali chiamate consumano crediti

Leggere la pagina di un negozio costa crediti dal portafoglio del tuo account. Le chiamate qui sotto controllano il saldo PRIMA di leggere qualsiasi cosa: se non basta ricevi 402 insufficient_credits, e se in questo momento il portafoglio non è leggibile ricevi 503 credit_wallet_unavailable (riprova più tardi). In entrambi i casi non viene letto nulla e non viene addebitato nulla.

Chiamate che consumano crediti

MetodoEndpointCosa faCosto
POST/groups/{group_id}/membersAggiungi un URL al gruppo, tramite product_id o da un nuovo indirizzo (un nuovo indirizzo viene letto subito)Crediti
POST/groups/{group_id}/refreshControlla subito ogni URL del gruppoCrediti
POST/groups/{group_id}/suggestions“Trova concorrenti”: URL di concorrenti proposti, con un punteggio e il motivoCrediti
POST/products/Inizia a monitorare un URL (url, name, role own o competitor) — viene letto subitoCrediti
POST/products/{product_id}/refreshControlla subito il prezzo di questo URL (rispettando l'intervallo minimo del tuo piano)Crediti
POST/search/Cerca il prezzo in tempo reale di un prodotto presso i rivenditori (crediti per ogni rivenditore cercato)Crediti

Chiamate gratuite

  • Ogni lettura (GET): prodotti, gruppi, confronti, storico, avvisi, insight, briefing, crediti, statistiche.
  • POST /own-products/ e POST /own-products/bulk — i tuoi prezzi.
  • POST /sales/bulk — le tue vendite.
  • Avvisi, impostazioni, conferme e tutto il resto che non legge la pagina di un negozio.
  • Anche il monitoraggio programmato degli URL che segui consuma crediti, a ogni verifica; GET /credits/ mostra il tuo saldo e la previsione.
  • POST /products/import aggiunge molti URL in una volta senza leggerli subito: vengono controllati, e addebitati, dal monitoraggio programmato.

Riferimento degli endpoint

Ogni operazione raggiungibile con una chiave, raggruppata per scopo. La specifica OpenAPI descrive ciascuna campo per campo, con i corpi esatti di richiesta e risposta.

I tuoi prezzi

Comunica a 4pricing il tuo prezzo direttamente, senza leggere la pagina del tuo negozio. Gratis.

MetodoEndpointCosa faCosto
POST/own-products/Invia il tuo prezzo attuale per un prodotto (trovato tramite sku, altrimenti per nome; creato se manca)Gratis
POST/own-products/bulkInvia i tuoi prezzi per fino a 2.000 prodotti in una sola richiesta; le righe errate vengono segnalate, le altre vengono salvateGratis

Prodotti confrontati (gruppi)

Un gruppo è un tuo prodotto insieme alle pagine dei concorrenti che vendono la stessa cosa. Contiene il verdetto e il prezzo consigliato.

MetodoEndpointCosa faCosto
GET/groups/Tutti i prodotti confrontati, ciascuno con il tuo prezzo, il prezzo più basso della concorrenza, il verdetto e il prezzo consigliatoGratis
POST/groups/Crea un prodotto confrontato (nome, sku, il tuo prezzo, costo, strategia di prezzo)Gratis
GET/groups/{group_id}Un prodotto confrontatoGratis
PUT/groups/{group_id}Aggiorna prezzo, costo, strategia, intervallo di verifica o il collegamento al negozio (store_kind)Gratis
DELETE/groups/{group_id}Elimina il gruppo; i suoi URL restano monitoratiGratis
GET/groups/{group_id}/comparisonLa tua posizione rispetto a ogni concorrente, con il prezzo consigliato e come è stato calcolatoGratis
GET/groups/{group_id}/historyStorico dei prezzi di ogni URL del gruppoGratis
GET/groups/seriesLa tua posizione sul mercato, giorno per giorno, su tutti i prodottiGratis
POST/groups/{group_id}/membersAggiungi un URL al gruppo, tramite product_id o da un nuovo indirizzo (un nuovo indirizzo viene letto subito)Crediti
DELETE/groups/{group_id}/members/{product_id}Rimuovi un URL dal gruppo (resta monitorato)Gratis
POST/groups/{group_id}/refreshControlla subito ogni URL del gruppoCrediti
POST/groups/{group_id}/apply-recommendedRendi il prezzo consigliato il tuo prezzo in 4pricing (invia il prezzo che hai visto; 409 se è cambiato)Gratis
GET/groups/suggestions/sourcesFonti disponibili per “Trova concorrenti”Gratis
POST/groups/{group_id}/suggestions“Trova concorrenti”: URL di concorrenti proposti, con un punteggio e il motivoCrediti

URL monitorati

Una riga per ogni indirizzo di pagina che 4pricing legge, tuo o di un concorrente (role own o competitor).

MetodoEndpointCosa faCosto
GET/products/URL monitorati, paginati (skip, limit fino a 500; totale in X-Total-Count)Gratis
POST/products/Inizia a monitorare un URL (url, name, role own o competitor) — viene letto subitoCrediti
POST/products/importAggiungi molti URL in una volta; vengono letti più tardi dal monitoraggio programmatoGratis
GET/products/exportEsportazione CSV degli URL attiviGratis
GET/products/{product_id}Un URL monitorato con il suo ultimo prezzoGratis
PATCH/products/{product_id}Modifica il nome, la categoria o l'intervallo di verificaGratis
DELETE/products/{product_id}Smetti di monitorare un URL (viene conservato come inattivo)Gratis
POST/products/{product_id}/refreshControlla subito il prezzo di questo URL (rispettando l'intervallo minimo del tuo piano)Crediti
GET/products/{product_id}/historyStorico dei prezzi di un URLGratis
GET/products/sparklinesUn breve storico dei prezzi per più URL insiemeGratis
GET/products/price-changesURL il cui prezzo è cambiato all'ultima verificaGratis

Cerca subito il prezzo di un prodotto presso i rivenditori.

MetodoEndpointCosa faCosto
POST/search/Cerca il prezzo in tempo reale di un prodotto presso i rivenditori (crediti per ogni rivenditore cercato)Crediti

Avvisi

Le regole di prezzo e la casella degli avvisi scattati.

MetodoEndpointCosa faCosto
GET/alerts/I tuoi avvisi di prezzoGratis
POST/alerts/Crea un avviso (prezzo sotto/sopra, calo in %, di nuovo disponibile, battuto da un concorrente…)Gratis
DELETE/alerts/{alert_id}Elimina un avvisoGratis
POST/alerts/{alert_id}/rearmRiattiva un avviso già scattatoGratis
GET/alerts/eventsCasella degli avvisi scattatiGratis
GET/alerts/events/unread-countQuanti avvisi non letti haiGratis
POST/alerts/events/read-allSegna gli avvisi come letti (facoltativamente solo quelli precedenti a un certo momento)Gratis
POST/alerts/events/{event_id}/readSegna un avviso come lettoGratis

Crediti

Saldo, previsione e ogni movimento di crediti.

MetodoEndpointCosa faCosto
GET/credits/Saldo dei crediti e previsione di consumoGratis
GET/credits/ledgerOgni movimento di creditiGratis
GET/credits/by-productCrediti usati per URL (totale e ultimi 30 giorni)Gratis

Le tue vendite

Vendite giornaliere per prodotto, usate per calcolare il valore di ogni opportunità. Gratis.

MetodoEndpointCosa faCosto
POST/sales/bulkVendite giornaliere per prodotto (sku o group_id, day, units_sold, revenue_cents; facoltativi stock_units, cost_cents), fino a 2.000 righe; inviando di nuovo lo stesso prodotto e lo stesso giorno, il dato viene sovrascrittoGratis

Insight, opportunità e briefing

Cosa è cambiato, perché conta e cosa fare — più il briefing giornaliero.

MetodoEndpointCosa faCosto
GET/insights/Insight con prove e proposte di prezzo (X-Total-Count)Gratis
GET/insights/{insight_id}Un insight nel dettaglioGratis
POST/insights/{insight_id}/dismissScarta un insightGratis
GET/opportunities/Riepilogo delle opportunità: valore calcolato dalle tue vendite, tenuto separato dalla stima di mercatoGratis
GET/briefings/Storico dei briefing giornalieriGratis
GET/briefings/latestL'ultimo briefing giornalieroGratis
POST/briefings/generateRicostruisce il briefing di oggi dagli insight aperti (non invia nulla)Gratis

Azioni sui prezzi

Modifiche di prezzo proposte, inviate, applicate o annullate. Tramite l'API puoi leggerle e confermare quelle ricevute via webhook.

MetodoEndpointCosa faCosto
GET/actions/Azioni sui prezzi: proposte, inviate, applicate, annullateGratis
POST/actions/{action_id}/confirmConferma che il tuo sistema ha applicato un prezzo ricevuto tramite price.apply o price.revertGratis

Impostazioni

Impostazioni dell'account, compresi l'indirizzo e il secret del webhook.

MetodoEndpointCosa faCosto
GET/settings/Impostazioni dell'account (rivenditori, strategia di repricing, invio degli avvisi, webhook)Gratis
PUT/settings/Modifica le impostazioni, compresi alert_webhook_url, alert_webhook_secret e alert_webhook_enabledGratis

Statistiche

Panoramica dell'account e stato di salute delle verifiche di prezzo.

MetodoEndpointCosa faCosto
GET/stats/Statistiche dell'account: URL, avvisi, variazioni nelle 24 ore, gruppiGratis
GET/stats/scrapersStato di salute delle verifiche di prezzo per rivenditoreGratis

53 endpoints · https://4pricing.net/api/v1/pricing/openapi.json

Cosa non può fare l'API

Alcune cose restano nel pannello di proposito, dove decide una persona autenticata. Tramite l'API questi percorsi rispondono 404:

PercorsoPerché
/keys/…Creazione e revoca delle chiavi — una chiave non può creare altre chiavi.
/autopilot/…La policy dell'autopilot, che decide cosa può cambiare senza una persona; una chiave trapelata non deve poterla ampliare.
/watchlists/…Le watchlist, che consumano crediti a ogni esecuzione.
/actions/{action_id}/approve · reject · revertApprovare, rifiutare o annullare una modifica di prezzo è una decisione che una persona prende nel pannello. L'API può solo leggere le azioni e confermarle.
/insights/{insight_id}/act · /insights/runAgire su un insight (scrive prezzi) e ricalcolare gli insight.
/admin/… · /internal/…Amministrazione interna della piattaforma.

Webhook

4pricing può inviare eventi a un tuo indirizzo (un webhook): fa un POST JSON ogni volta che succede qualcosa, così il tuo sistema non deve continuare a chiedere.

C'è un solo indirizzo webhook per account; ogni tipo di evento arriva lì. Distinguili dal campo event — l'avviso è l'unico evento che non lo ha.

Come attivarlo

  • Imposta alert_webhook_url (un indirizzo http/https pubblico sulla porta 80 o 443), alert_webhook_secret e alert_webhook_enabled: true — nel pannello o con PUT /settings/.
  • Un indirizzo che risolve verso una rete privata o interna viene rifiutato con 422. I reindirizzamenti non vengono seguiti.
  • Per briefing.ready attiva anche briefing_webhook_enabled. price.apply e price.revert vengono inviati solo per i gruppi collegati al tuo negozio con store_kind = webhook, e solo se è impostato un secret.
bash
curl -X PUT https://4pricing.net/api/v1/pricing/settings/ \
  -H "X-API-Key: INCOLLA_QUI_LA_TUA_CHIAVE" \
  -H "Content-Type: application/json" \
  -d '{"alert_webhook_url":"https://your-shop.example/4pricing-webhook.php","alert_webhook_secret":"IL_TUO_SECRET_WEBHOOK","alert_webhook_enabled":true,"briefing_webhook_enabled":true}'

Eventi

EventoSignificato
alertÈ scattato un avviso di prezzo. Corpo: event_id (deduplica su di esso), alert_id, alert_type, message, message_code, message_params, price_cents, currency, triggered_at, product, group, panel_url. Nessun campo event.
price.applyUn prezzo approvato da applicare nel tuo negozio: action_id, group (id, name, sku, ean, store_product_id), old_price_cents, new_price_cents, currency, reason_code, mode e confirm.path.
price.revertTorna al prezzo precedente. Stesso corpo di price.apply; applicalo e confermalo allo stesso modo.
briefing.readyIl briefing giornaliero è pronto: day, headline, summary, sections, total_value_cents (solo dalle tue vendite), currency, panel_url.

Header di ogni richiesta webhook

HeaderSignificato
X-4Pricing-TimestampQuando è stata inviata la richiesta, in secondi Unix. Rifiuta le richieste troppo vecchie (per esempio oltre 5 minuti).
X-4Pricing-Signaturesha256= seguito dall'HMAC-SHA256 esadecimale del testo “<timestamp>.<raw body>”, calcolato con il tuo secret del webhook. Presente quando è impostato un secret (sempre su price.apply e price.revert).
Content-Typeapplication/json; charset=utf-8
User-Agent4pricing-alerts/1.0

Verifica della firma

Ricalcola la firma a partire dall'header del timestamp e dal corpo GREZZO (RAW) che hai ricevuto, confrontala in tempo costante e controlla il timestamp. Solo allora elabora l'evento. Rispondi con un qualsiasi 2xx quando l'hai accettato.

php
<?php
$secret = 'IL_TUO_SECRET_WEBHOOK';

// Usa il corpo GREZZO (RAW), esattamente come ricevuto — non un JSON ricodificato
$body = file_get_contents('php://input');
$timestamp = isset($_SERVER['HTTP_X_4PRICING_TIMESTAMP']) ? $_SERVER['HTTP_X_4PRICING_TIMESTAMP'] : '';
$signature = isset($_SERVER['HTTP_X_4PRICING_SIGNATURE']) ? $_SERVER['HTTP_X_4PRICING_SIGNATURE'] : '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);

// Firma errata o più vecchia di 5 minuti: rifiuta
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
if (isset($event['event']) && ($event['event'] === 'price.apply' || $event['event'] === 'price.revert')) {
    // Applica new_price_cents al prodotto nel tuo negozio (abbinalo tramite sku o store_product_id)
    // $event['group']['sku'], $event['new_price_cents'], $event['currency']
    // Poi conferma: POST /actions/{action_id}/confirm con la tua chiave API
}
http_response_code(200);
echo 'ok';
javascript
// Node.js, no package needed
const http = require("http");
const crypto = require("crypto");
const SECRET = process.env.WEBHOOK_SECRET; // IL_TUO_SECRET_WEBHOOK

http.createServer((req, res) => {
  const chunks = [];
  req.on("data", (c) => chunks.push(c));
  req.on("end", () => {
    // Usa il corpo GREZZO (RAW), esattamente come ricevuto — non un JSON ricodificato
    const body = Buffer.concat(chunks);
    const ts = String(req.headers["x-4pricing-timestamp"] || "");
    const sig = String(req.headers["x-4pricing-signature"] || "");
    const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(ts + ".").update(body).digest("hex");
    const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
    const same = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
    if (!same || !fresh) {
      // Firma errata o più vecchia di 5 minuti: rifiuta
      res.writeHead(401);
      return res.end();
    }
    const event = JSON.parse(body.toString("utf8"));
    if (event.event === "price.apply" || event.event === "price.revert") {
      // Applica new_price_cents al prodotto nel tuo negozio (abbinalo tramite sku o store_product_id)
      // Poi conferma: POST /actions/{action_id}/confirm con la tua chiave API
    }
    res.writeHead(200);
    res.end("ok");
  });
}).listen(3000);
python
# Python 3, standard library only
import hashlib
import hmac
import time

SECRET = "IL_TUO_SECRET_WEBHOOK"


def is_valid(raw_body: bytes, timestamp: str, signature: str) -> bool:
    # Usa il corpo GREZZO (RAW), esattamente come ricevuto — non un JSON ricodificato
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return False
    expected = "sha256=" + hmac.new(
        SECRET.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Consegna e nuovi tentativi

  • Avvisi: in caso di errore di connessione o DNS, 429 o 5xx, la richiesta viene ritentata con backoff esponenziale, al massimo 5 tentativi. Qualsiasi altro 4xx è definitivo.
  • Se la richiesta è partita ma non è arrivata alcuna risposta entro 10 secondi, non viene rinviata (per evitare un duplicato) — raramente potresti comunque ricevere un duplicato, quindi deduplica su event_id.
  • price.apply e price.revert vengono inviati una sola volta: se la consegna fallisce, l'azione diventa failed. Deduplica su action_id.
  • briefing.ready viene inviato una volta al giorno.

Conferma di un prezzo applicato

  • Un price.apply non è considerato applicato finché il tuo sistema non lo dice. Dopo aver cambiato il prezzo nel tuo negozio, chiama POST /actions/{action_id}/confirm con la tua chiave API: l'azione passa da “inviata” ad “applicata”.
  • Confermare due volte non crea problemi — un'azione già applicata viene restituita così com'è.
bash
curl -X POST https://4pricing.net/api/v1/pricing/actions/ACTION_ID/confirm \
  -H "X-API-Key: INCOLLA_QUI_LA_TUA_CHIAVE"

Esempi di corpo

json · price.apply
{
  "event": "price.apply",
  "action_id": "8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55",
  "tenant_id": "…",
  "group": {
    "id": "2fa912df-440c-4ddf-896a-5d558d56085f",
    "name": "Canon RF 50mm F1.8 STM",
    "sku": "4515C005AA",
    "ean": "4549292181623",
    "store_product_id": "1042"
  },
  "old_price_cents": 124900,
  "new_price_cents": 118699,
  "currency": "RON",
  "reason_code": "undercut",
  "mode": "manual",
  "confirm": { "method": "POST", "path": "/api/v1/pricing/actions/8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55/confirm" }
}
json · alert
{
  "event_id": 18342,
  "alert_id": "c1b5…",
  "alert_type": "competitor_undercut",
  "tenant_id": "…",
  "message_code": "competitor_undercut",
  "message": "…",
  "message_params": { "…": "…" },
  "price_cents": 118990,
  "currency": "RON",
  "triggered_at": "2026-10-13T07:05:12",
  "product": { "id": "…", "name": "…", "url": "https://…", "retailer": "…", "current_price_cents": 118990, "in_stock": true },
  "group": { "id": "…", "name": "Canon RF 50mm F1.8 STM", "sku": "4515C005AA" },
  "panel_url": "https://panel.4pricing.net/…"
}

Errori

Gli errori arrivano con lo stato HTTP corretto e un corpo JSON {"detail": …}. detail è un testo, un oggetto con un codice che puoi gestire nel tuo programma o — su 422 — l'elenco dei campi non validi.

StatoCodiceSignificato
401—La chiave API manca, è errata o è stata revocata.
402plan_requiredL'account non ha un abbonamento 4pricing attivo.
402plan_limitSi supererebbe un limite del piano (URL monitorati, negozi concorrenti) oppure un aggiornamento è arrivato prima dell'intervallo minimo. Il messaggio indica quale.
402insufficient_creditsCrediti insufficienti per una chiamata che legge la pagina di un negozio. Non è stato letto né addebitato nulla.
403—Bloccato dal firewall prima di raggiungere l'API — di solito un User-Agent generico come Python-urllib. Invia il tuo User-Agent.
404—La risorsa non esiste, non appartiene al tuo account oppure il percorso non è disponibile tramite l'API.
409recommendation_moved · own_from_listing · invalid_state · webhook_secret_requiredConflitto: il prezzo consigliato è cambiato (recommendation_moved), l'azione si trova in un altro stato (invalid_state), serve un secret del webhook, oppure l'operazione non si applica a questo elemento.
422—Dati non validi, per esempio un URL senza http/https o un campo obbligatorio mancante. detail elenca i campi.
429—Più di 60 richieste al minuto per l'account (o troppe chiavi non valide). Attendi i secondi indicati da Retry-After.
503credit_wallet_unavailableIn questo momento il portafoglio crediti non è leggibile. Riprova più tardi; non è stato addebitato nulla.

Corpi degli errori

json
{"detail": "Invalid or missing API key"}
{"detail": {"code": "plan_required", "message": "…"}}
{"detail": {"code": "insufficient_credits", "message": "…"}}
{"detail": [{"loc": ["body", "price_cents"], "msg": "…", "type": "…"}]}

Esempi di codice

La stessa lettura in quattro linguaggi, con pausa e nuovo tentativo su 429, poi l'invio delle vendite. Sostituisci INCOLLA_QUI_LA_TUA_CHIAVE con la tua chiave (oppure imposta la variabile d'ambiente PRICING_API_KEY).

Node.js: leggi i tuoi prodotti confrontati

javascript
// Node.js 18+ (fetch is built in, no package needed)
const API_KEY = process.env.PRICING_API_KEY; // INCOLLA_QUI_LA_TUA_CHIAVE
const BASE = "https://4pricing.net/api/v1/pricing";

async function call(path, options = {}) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const res = await fetch(BASE + path, {
      ...options,
      headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", ...(options.headers || {}) },
    });
    if (res.status === 429) {
      // Oltre 60 richieste/minuto: attendi i secondi indicati da Retry-After, poi riprova
      const wait = Number(res.headers.get("Retry-After") || 60);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!res.ok) throw new Error(res.status + " " + (await res.text()));
    return res.status === 204 ? null : res.json();
  }
  throw new Error("rate limited");
}

async function main() {
  // I tuoi prodotti confrontati: il tuo prezzo, il concorrente più economico, il verdetto
  const groups = await call("/groups/");
  for (const g of groups) {
    console.log(g.name, g.own_price_cents_effective, g.min_competitor_cents, g.verdict);
  }
}

main().catch((err) => {
  console.error(err.message);
  process.exit(1);
});

Python: leggi i tuoi prodotti confrontati

python
# Python 3, standard library only
import json
import os
import time
import urllib.error
import urllib.request

API_KEY = os.environ["PRICING_API_KEY"]  # INCOLLA_QUI_LA_TUA_CHIAVE
BASE = "https://4pricing.net/api/v1/pricing"


def call(path, method="GET", payload=None):
    data = json.dumps(payload).encode() if payload is not None else None
    for _ in range(3):
        req = urllib.request.Request(BASE + path, data=data, method=method, headers={
            "X-API-Key": API_KEY, "Content-Type": "application/json", "Accept": "application/json",
            # Imposta il tuo User-Agent: quello predefinito di alcune librerie HTTP (Python urllib) viene bloccato con 403
            "User-Agent": "my-shop-integration/1.0",
        })
        try:
            with urllib.request.urlopen(req, timeout=30) as res:
                body = res.read()
                return json.loads(body) if body else None
        except urllib.error.HTTPError as err:
            if err.code == 429:
                # Oltre 60 richieste/minuto: attendi i secondi indicati da Retry-After, poi riprova
                time.sleep(int(err.headers.get("Retry-After", "60")))
                continue
            raise
    raise RuntimeError("rate limited")


# I tuoi prodotti confrontati: il tuo prezzo, il concorrente più economico, il verdetto
for g in call("/groups/"):
    print(g["name"], g["own_price_cents_effective"], g["min_competitor_cents"], g["verdict"])

curl: invia le tue vendite giornaliere

bash
curl -X POST https://4pricing.net/api/v1/pricing/sales/bulk \
  -H "X-API-Key: INCOLLA_QUI_LA_TUA_CHIAVE" \
  -H "Content-Type: application/json" \
  -d '{"rows":[{"sku":"MAGLIETTA-BIANCA-M","day":"2026-10-12","units_sold":7,"revenue_cents":34993}]}'

PHP: invia le tue vendite giornaliere

php
<?php
$apiKey = 'INCOLLA_QUI_LA_TUA_CHIAVE';

// Vendite giornaliere per prodotto (per sku o group_id), fino a 2.000 righe per richiesta
$rows = [
    ['sku' => 'MAGLIETTA-BIANCA-M', 'day' => '2026-10-12', 'units_sold' => 7, 'revenue_cents' => 34993],
];

$ch = curl_init('https://4pricing.net/api/v1/pricing/sales/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT => 'my-shop-integration/1.0',
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['rows' => $rows]),
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . ' ' . $body;   // 200 {"upserted": 1, "skipped": []}

Domande frequenti

Devo essere un programmatore per usare l'API 4pricing?

No. La guida rapida ti dà un file PHP già pronto: crei una chiave nel pannello, la incolli nel file, carichi il file sul tuo hosting e lo apri nel browser. Per integrazioni più avanzate (un ERP, una piattaforma di e-commerce) uno sviluppatore può usare il riferimento degli endpoint e la specifica OpenAPI.

Quanto costa l'API 4pricing?

L'API è inclusa in ogni abbonamento 4pricing, con gli stessi limiti del pannello. La lettura dei tuoi dati è gratuita; consumano crediti solo le chiamate che leggono la pagina di un negozio (aggiunta di un nuovo URL, aggiornamento, ricerca in tempo reale, ricerca di concorrenti), come nel pannello.

Quali chiamate consumano crediti?

POST /products/, POST /products/{product_id}/refresh, POST /groups/{group_id}/refresh, POST /groups/{group_id}/members con un nuovo URL, POST /groups/{group_id}/suggestions e POST /search/. Se il saldo non basta ricevi 402 insufficient_credits e non viene letto nulla.

I prezzi che invio tramite l'API contano nel limite di URL del mio piano?

No. Contano solo gli indirizzi che 4pricing legge da una pagina — le tue pagine prodotto e ogni pagina della concorrenza, separatamente. I prezzi inviati con POST /own-products/ o /own-products/bulk non contano né come URL né come crediti.

Posso limitare a cosa può accedere una chiave?

No, non esistono scope: una chiave vede e può modificare tutto il tuo account 4pricing. Usa una chiave separata per ogni integrazione e revoca ogni chiave che non usi più; la revoca ha effetto immediato.

Cosa succede se invio più di 60 richieste al minuto?

Ricevi 429 con un header Retry-After. Il limite è per account, condiviso da tutte le tue chiavi. Attendi il numero di secondi indicato in Retry-After e riprova; per molti prodotti usa le chiamate bulk (fino a 2.000 righe per richiesta).

Come faccio ad avere il prezzo consigliato nel mio negozio in automatico?

Collega il gruppo al tuo negozio con store_kind = webhook, imposta un indirizzo e un secret del webhook e gestisci l'evento price.apply: cambia il prezzo nel tuo negozio, poi chiama POST /actions/{action_id}/confirm. L'approvazione di una modifica di prezzo resta una decisione presa nel pannello.

Esiste un SDK o un plugin già pronto?

Non c'è un SDK o un plugin ufficiale. L'API è un semplice REST con JSON, quindi funziona qualsiasi linguaggio in grado di fare richieste HTTPS; la specifica OpenAPI può essere caricata negli strumenti che generano un client in automatico.