API NinjaBuy Business
URL di base
Ogni route di questo sito vive sotto un unico URL di base. L’app Shopify e tutto ciò che costruisci chiamano le stesse route, quindi ciò che fa l’app può farlo anche il tuo codice.
https://www.ninjabuy.com/api/v1
Preferisci chiedere a un assistente IA? Lo stesso account funziona via MCP su https://www.ninjabuy.com/mcp: Assistenti IA (MCP).
- ImportazioniUn link in entrata, il costo completo della vendita in uscita.
- ProdottiCosa vendi, e dove è pubblicato.
- VariantiOgni opzione, legata allo SKU del venditore.
- Livelli di stockCosa puoi vendere adesso.
- Regole di sincronizzazioneCosa succede quando un venditore cambia un prezzo.
- OrdiniUna vendita che acquistiamo, controlliamo e spediamo.
- Ordini di evasioneLe righe che partono in un pacco.
- SpedizioniPacchi in viaggio, con tracciamento.
- SaldoIl denaro con cui si pagano gli ordini.
- Endpoint webhookDove inviamo gli eventi.
curl "https://www.ninjabuy.com/api/v1/balance" \
-H "Authorization: Bearer $NINJABUY_API_KEY" \
-H "NinjaBuy-Version: 2026-11"Autenticazione
Invia una chiave API come bearer token in ogni richiesta. Le chiavi iniziano con nb_. Durante l’anteprima le emette il nostro team: una volta configurato il tuo account Business, inviamo ogni chiave una sola volta, sul canale che scegli, con gli scope richiesti. Chiedici di revocarne una e smette di funzionare dalla richiesta successiva.
Tieni le chiavi sul tuo server. Una chiave in un browser, in un’app o in un repository pubblico può essere letta da chiunque e può spendere il tuo saldo.
Un’app che agisce per altri venditori NinjaBuy usa invece OAuth 2.1: registrazione dinamica del client, flusso authorization code con PKCE (S256) e la schermata di approvazione del venditore stesso. Il suo token di accesso va nella stessa intestazione e funziona sulle stesse route. I token di accesso durano un’ora; i token di aggiornamento li rinnovano per sette giorni. Gli assistenti IA si collegano allo stesso modo (MCP).
products:readscope- Leggi importazioni, prodotti, varianti e regole di sincronizzazione.
products:writescope- Importa, modifica, invia ed elimina prodotti; cambia le regole di sincronizzazione.
inventory:readscope- Leggi i livelli di stock.
orders:readscope- Leggi ordini, ordini di evasione e spedizioni.
orders:writescope- Crea, metti in attesa, sblocca e annulla ordini. Spende il tuo saldo.
balance:readscope- Leggi il saldo e le sue transazioni.
webhooks:writescope- Gestisci gli endpoint webhook.
Una chiamata fuori dagli scope di una chiave risponde 403 insufficient-scope e indica lo scope necessario.
Authorization: Bearer nb_7Qk2...vX9eHTTP/2 401
{
"type": "https://www.ninjabuy.com/api/v1/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "The API key is missing, wrong, expired or revoked.",
"request_id": "req_7Hq2Lw9c"
}Versioni
Il percorso contiene la versione principale, /v1. Al suo interno, versioni datate cambiano campi e comportamento. Fissa quella su cui hai costruito con l’intestazione NinjaBuy-Version; ogni risposta indica la versione usata nella stessa intestazione.
2026-11 è la versione attuale ed è un’anteprima: nomi e campi possono ancora cambiare prima che sia dichiarata stabile. Ogni modifica è nel changelog e avvisiamo via email ogni titolare di chiave prima di una modifica che potrebbe creargli problemi.
All’interno di una versione aggiungiamo soltanto: nuove route, nuovi parametri facoltativi, nuovi campi nelle risposte e nuovi tipi di evento. Costruisci il tuo client in modo che ignori i campi che non conosce.
GET /api/v1/orders
NinjaBuy-Version: 2026-11
HTTP/2 200
NinjaBuy-Version: 2026-11Richieste e risposte
- Invia JSON con
Content-Type: application/json; le risposte sono JSON in UTF-8. Gli errori sonoapplication/problem+json. - Un importo è un oggetto con una stringa decimale e una valuta:
{ "amount": "264.20", "currency": "CNY" }. Ciò che NinjaBuy addebita è in CN¥; i tuoi prezzi di vendita sono nella valuta del tuo negozio. - Gli ID sono stringhe con un prefisso che ne indica il tipo (
prd_,ord_,shp_). Trattali come opachi. - Le date sono ISO 8601 in UTC. I paesi sono codici ISO a due lettere.
- Ogni risposta ha un’intestazione
X-Request-Ide ogni errore riporta lo stesso ID comerequest_id. Citalo quando ci scrivi.
{
"id": "ord_3n7q",
"created_at": "2026-11-01T09:14:02Z",
"total": { "amount": "264.20", "currency": "CNY" },
"suggested_price": { "amount": "49.99", "currency": "USD" }
}Paginazione
Gli elenchi restituiscono i più recenti per primi, come data, has_more e cursor. Rimanda quel cursore per la pagina successiva finché has_more è true. Un cursore funziona solo con i filtri da cui proviene.
limitinteger- Da 1 a 100. 25 se omesso.
cursorstring- Il cursore della pagina precedente. Omettilo per la prima pagina.
curl "https://www.ninjabuy.com/api/v1/orders?limit=25&cursor=c_2v9kq" \
-H "Authorization: Bearer $NINJABUY_API_KEY" \
-H "NinjaBuy-Version: 2026-11"HTTP/2 200 OK
{
"data": [
{ "id": "ord_3n7q", "status": "checked" },
{ "id": "ord_3n6z", "status": "shipped" }
],
"has_more": true,
"cursor": "c_1q4ea"
}Limiti di frequenza
I limiti sono per account, condivisi da tutte le sue chiavi, i token OAuth e il collegamento MCP. Ogni risposta indica a che punto sei nelle intestazioni IETF RateLimit-Policy e RateLimit; un rifiuto è 429 rate-limited con Retry-After in secondi.
| Finestra | Cosa conta |
|---|---|
| "create-links";q=30;w=60 | Chiamate che leggono un’inserzione dal marketplace: creare o aggiornare un’importazione, inviare un prodotto. 30 al minuto, condivise con l’API dei link e MCP. |
| "default";q=120;w=60 | Tutte le altre chiamate. 120 al minuto. |
HTTP/2 429
Content-Type: application/problem+json
Retry-After: 12
RateLimit-Policy: "create-links";q=30;w=60
RateLimit: "create-links";r=0;t=12
{
"type": "https://www.ninjabuy.com/api/v1/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Try again in 12 seconds.",
"request_id": "req_7Hq2Lw9c"
}Idempotenza
Invia una Idempotency-Key (da 1 a 255 caratteri ASCII stampabili; un UUID è l’ideale) in ogni POST, e un nuovo tentativo dopo un timeout non può creare un secondo ordine. Per 24 ore la stessa chiave, sulla stessa route con lo stesso corpo, restituisce di nuovo la prima risposta byte per byte, con Idempotent-Replayed: true, senza rieseguire nulla né contare per un limite.
- La stessa chiave con un corpo o una route diversi:
422 idempotency-key-reused. - La prima richiesta è ancora in corso:
409 idempotency-key-in-progress, conRetry-After. - Un rifiuto per limite di frequenza non è una risposta: la chiave resta libera, quindi riprovare dopo
Retry-Afteresegue la richiesta.
Gli ordini portano anche un tuo external_id: un secondo ordine con lo stesso viene rifiutato con 409 duplicate-order, qualunque sia la chiave usata.
POST /api/v1/orders
Idempotency-Key: 4f0c2b4e-9d1a-4e0f-b0a8-5d1d3c7e2f61
HTTP/2 201
Idempotent-Replayed: true
X-Request-Id: req_7Hq2Lw9cErrori
Gli errori sono problem JSON (RFC 9457): type è un URL stabile su cui ramificare, title lo nomina, detail spiega questo caso con parole che puoi mostrare e request_id lo ritrova nei nostri log. Un corpo che non supera la validazione aggiunge errors, ognuno con un pointer JSON al campo.
| Stato | Tipo | Quando |
|---|---|---|
| 400 | bad-json | Il corpo non è JSON. |
| 400 | bad-body | Un campo manca o è errato; errors[] lo indica. |
| 400 | bad-idempotency-key | La Idempotency-Key è vuota, troppo lunga o non è ASCII stampabile. |
| 401 | unauthorized | La chiave o il token manca, è errato, scaduto o revocato. |
| 403 | insufficient-scope | Alla chiave manca lo scope richiesto dalla route. |
| 404 | not-found | Nessun oggetto simile su questo account. Gli ID di un altro account danno la stessa risposta. |
| 405 | method-not-allowed | La route esiste, ma non con questo metodo; Allow elenca quelli accettati. |
| 409 | duplicate-order | Esiste un ordine con questo external_id; order lo indica. |
| 409 | idempotency-key-in-progress | La prima richiesta con questa chiave è ancora in corso. |
| 409 | invalid-state | L’oggetto non può farlo ora, per esempio annullare un ordine spedito. |
| 415 | unsupported-media-type | Il corpo non è application/json. |
| 422 | idempotency-key-reused | La chiave è stata usata per una richiesta diversa. |
| 422 | unprocessable | JSON valido su cui non possiamo agire: un link non letto, una variante esaurita. detail indica quale. |
| 429 | rate-limited | Troppe chiamate nella finestra. Attendi Retry-After secondi. |
| 500 | internal-error | Colpa nostra. Riprova con la stessa Idempotency-Key. |
| 503 | service-unavailable | Al momento non siamo riusciti a verificare la chiave o a raggiungere un negozio. Riprova dopo Retry-After. |
HTTP/2 400
{
"type": "https://www.ninjabuy.com/api/v1/problems/bad-body",
"title": "Invalid request body",
"status": 400,
"detail": "1 field is invalid.",
"request_id": "req_7Hq2Lw9c",
"errors": [
{
"pointer": "/ship_to/country",
"detail": "Must be a two-letter country code."
}
]
}