API NinjaBuy Business
URL de base
Chaque route de ce site se trouve sous une même URL de base. L’app Shopify et tout ce que vous construisez appellent les mêmes routes : ce que fait l’app, votre code peut le faire aussi.
https://www.ninjabuy.com/api/v1
Vous préférez demander à un assistant IA ? Le même compte fonctionne via MCP sur https://www.ninjabuy.com/mcp : Assistants IA (MCP).
- ImportsUn lien en entrée, le coût complet de la vente en sortie.
- ProduitsCe que vous vendez, et où c’est publié.
- VariantesChaque option, liée au SKU du vendeur.
- Niveaux de stockCe que vous pouvez vendre maintenant.
- Règles de synchronisationCe qui se passe quand un vendeur change un prix.
- CommandesUne vente que nous achetons, vérifions et expédions.
- Commandes d’exécutionLes lignes qui partent dans un colis.
- EnvoisColis en route, avec suivi.
- SoldeL’argent avec lequel les commandes sont payées.
- Endpoints de webhookOù nous envoyons les événements.
curl "https://www.ninjabuy.com/api/v1/balance" \
-H "Authorization: Bearer $NINJABUY_API_KEY" \
-H "NinjaBuy-Version: 2026-11"Authentification
Envoyez une clé API comme jeton bearer à chaque requête. Les clés commencent par nb_. Pendant la préversion, notre équipe les émet : une fois votre compte Business configuré, nous envoyons chaque clé une seule fois, par le canal de votre choix, avec les scopes demandés. Demandez-nous d’en révoquer une et elle cesse de fonctionner dès la requête suivante.
Gardez les clés sur votre serveur. Une clé dans un navigateur, une app ou un dépôt public peut être lue par n’importe qui, et elle peut dépenser votre solde.
Une app qui agit pour d’autres vendeurs NinjaBuy utilise plutôt OAuth 2.1 : enregistrement dynamique du client, flux authorization code avec PKCE (S256), et l’écran d’approbation du vendeur lui-même. Son jeton d’accès va dans le même en-tête et fonctionne sur les mêmes routes. Les jetons d’accès durent une heure ; les jetons de rafraîchissement les renouvellent pendant sept jours. Les assistants IA se connectent de la même façon (MCP).
products:readscope- Lire les imports, produits, variantes et règles de synchronisation.
products:writescope- Importer, modifier, envoyer et supprimer des produits ; modifier les règles de synchronisation.
inventory:readscope- Lire les niveaux de stock.
orders:readscope- Lire les commandes, commandes d’exécution et envois.
orders:writescope- Créer, mettre en attente, débloquer et annuler des commandes. Dépense votre solde.
balance:readscope- Lire le solde et ses transactions.
webhooks:writescope- Gérer les endpoints de webhook.
Un appel hors des scopes d’une clé répond 403 insufficient-scope et indique le scope requis.
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"
}Versions
Le chemin porte la version majeure, /v1. À l’intérieur, des versions datées changent les champs et le comportement. Fixez celle sur laquelle vous avez construit avec l’en-tête NinjaBuy-Version ; chaque réponse indique la version utilisée dans le même en-tête.
2026-11 est la version actuelle, en préversion : les noms et les champs peuvent encore changer avant qu’elle soit déclarée stable. Chaque changement figure dans le journal des modifications, et nous prévenons par e-mail chaque détenteur de clé avant tout changement susceptible de le casser.
Dans une version, nous ne faisons qu’ajouter : nouvelles routes, nouveaux paramètres facultatifs, nouveaux champs dans les réponses et nouveaux types d’événements. Construisez votre client pour ignorer les champs qu’il ne connaît pas.
GET /api/v1/orders
NinjaBuy-Version: 2026-11
HTTP/2 200
NinjaBuy-Version: 2026-11Requêtes et réponses
- Envoyez du JSON avec
Content-Type: application/json; les réponses sont du JSON en UTF-8. Les erreurs sont enapplication/problem+json. - Un montant est un objet avec une chaîne décimale et une devise :
{ "amount": "264.20", "currency": "CNY" }. Ce que NinjaBuy facture est en CN¥ ; vos prix de vente sont dans la devise de votre boutique. - Les identifiants sont des chaînes avec un préfixe qui indique leur type (
prd_,ord_,shp_). Traitez-les comme opaques. - Les dates sont en ISO 8601, en UTC. Les pays sont des codes ISO à deux lettres.
- Chaque réponse porte un en-tête
X-Request-Id, et chaque erreur porte le même identifiant enrequest_id. Citez-le quand vous nous écrivez.
{
"id": "ord_3n7q",
"created_at": "2026-11-01T09:14:02Z",
"total": { "amount": "264.20", "currency": "CNY" },
"suggested_price": { "amount": "49.99", "currency": "USD" }
}Pagination
Les listes renvoient les plus récents d’abord, sous forme de data, has_more et cursor. Renvoyez ce curseur pour la page suivante tant que has_more vaut true. Un curseur ne fonctionne qu’avec les filtres d’où il vient.
limitinteger- 1 à 100. 25 si omis.
cursorstring- Le curseur de la page précédente. Omettez-le pour la première page.
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"
}Limites de débit
Les limites sont par compte, partagées par toutes ses clés, ses jetons OAuth et sa connexion MCP. Chaque réponse indique où vous en êtes dans les en-têtes IETF RateLimit-Policy et RateLimit ; un refus est un 429 rate-limited avec Retry-After en secondes.
| Fenêtre | Ce qui compte |
|---|---|
| "create-links";q=30;w=60 | Appels qui lisent une annonce sur la marketplace : créer ou rafraîchir un import, envoyer un produit. 30 par minute, partagés avec l’API des liens et MCP. |
| "default";q=120;w=60 | Tous les autres appels. 120 par minute. |
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"
}Idempotence
Envoyez une Idempotency-Key (1 à 255 caractères ASCII imprimables ; un UUID est idéal) sur tout POST, et une nouvelle tentative après un délai dépassé ne peut pas créer une deuxième commande. Pendant 24 heures, la même clé, sur la même route avec le même corps, renvoie la première réponse à l’octet près, avec Idempotent-Replayed: true, sans rien exécuter ni compter dans une limite.
- La même clé avec un corps ou une route différente :
422 idempotency-key-reused. - La première requête est encore en cours :
409 idempotency-key-in-progress, avecRetry-After. - Un refus pour limite de débit n’est pas une réponse : la clé reste libre, donc réessayer après
Retry-Afterexécute la requête.
Les commandes portent aussi votre propre external_id : une deuxième commande avec le même est refusée avec 409 duplicate-order, quelle que soit la clé utilisée.
POST /api/v1/orders
Idempotency-Key: 4f0c2b4e-9d1a-4e0f-b0a8-5d1d3c7e2f61
HTTP/2 201
Idempotent-Replayed: true
X-Request-Id: req_7Hq2Lw9cErreurs
Les erreurs sont au format problem JSON (RFC 9457) : type est une URL stable sur laquelle brancher, title la nomme, detail explique ce cas en mots affichables, et request_id la retrouve dans nos journaux. Un corps qui échoue à la validation ajoute errors, chacun avec un pointer JSON vers le champ.
| Statut | Type | Quand |
|---|---|---|
| 400 | bad-json | Le corps n’est pas du JSON. |
| 400 | bad-body | Un champ manque ou est incorrect ; errors[] l’indique. |
| 400 | bad-idempotency-key | L’Idempotency-Key est vide, trop longue ou pas en ASCII imprimable. |
| 401 | unauthorized | La clé ou le jeton est absent, incorrect, expiré ou révoqué. |
| 403 | insufficient-scope | La clé n’a pas le scope requis par la route. |
| 404 | not-found | Aucun objet de ce type sur ce compte. Les identifiants d’un autre compte donnent la même réponse. |
| 405 | method-not-allowed | La route existe, mais pas avec cette méthode ; Allow liste celles qu’elle accepte. |
| 409 | duplicate-order | Une commande avec cet external_id existe ; order l’indique. |
| 409 | idempotency-key-in-progress | La première requête avec cette clé est encore en cours. |
| 409 | invalid-state | L’objet ne peut pas faire cela maintenant, par exemple annuler une commande expédiée. |
| 415 | unsupported-media-type | Le corps n’est pas en application/json. |
| 422 | idempotency-key-reused | La clé a servi pour une autre requête. |
| 422 | unprocessable | Du JSON valide que nous ne pouvons pas traiter : un lien illisible, une variante épuisée. detail indique lequel. |
| 429 | rate-limited | Trop d’appels dans la fenêtre. Attendez Retry-After secondes. |
| 500 | internal-error | Notre erreur. Réessayez avec la même Idempotency-Key. |
| 503 | service-unavailable | Nous n’avons pas pu vérifier la clé ou joindre une boutique pour le moment. Réessayez après 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."
}
]
}