Lecture
Autorise les routes GET. Choisissez cette permission si l’intégration doit seulement afficher ou exporter les données du compte.
Connectez un service serveur aux données déjà synchronisées avec un compte Lunidex. Créez une clé et envoyez une première requête, puis explorez les cartes, les produits scellés, la pagination et les écritures.
Ne placez jamais une clé API dans le code d’un navigateur, une application mobile, un dépôt public, une URL, une capture d’écran ou un journal partagé. Gardez-la dans un gestionnaire de secrets côté serveur et transmettez-la uniquement dans l’en-tête Authorization.
Une intégration a besoin d’une clé, d’un secret stocké côté serveur et d’une requête HTTPS vers l’URL de base de l’API.
Ouvrez la carte des clés API dans le tableau de bord Lunidex. Nommez la clé et choisissez lecture seule ou lecture et écriture. Le secret complet n’apparaît qu’une fois : copiez-le avant de fermer le message.
Enregistrez la clé sous LUNIDEX_API_KEY dans le gestionnaire de secrets de votre hébergeur. Ne la commitez pas et ne l’envoyez pas au navigateur.
Appelez GET /me depuis votre serveur. Les routes privées exigent l’en-tête Authorization: Bearer suivi de la clé.
curl --fail-with-body \
-H "Authorization: Bearer $LUNIDEX_API_KEY" \
https://lunidex.app/api/v1/meconst API_BASE_URL = "https://lunidex.app/api/v1";
const apiKey = process.env.LUNIDEX_API_KEY;
if (!apiKey) throw new Error("Missing LUNIDEX_API_KEY");
const response = await fetch(API_BASE_URL + "/me", {
headers: { Authorization: "Bearer " + apiKey },
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error?.code ?? "REQUEST_FAILED");
}
console.log(result.data);Envoyez la clé dans l’en-tête HTTP Authorization pour chaque requête privée /api/v1. L’API renvoie du JSON dans une enveloppe data ; les réponses privées utilisent Cache-Control: private, no-store.
Autorise les routes GET. Choisissez cette permission si l’intégration doit seulement afficher ou exporter les données du compte.
Autorise les routes GET et les routes d’écriture. Réservez-la à une intégration serveur de confiance qui doit modifier la collection ou le journal des scellés.
L’API ne voit que les données synchronisées avec le compte Lunidex dans Neon. Les données présentes uniquement sur un appareil ne sont pas accessibles. L’identité du compte ne contient pas l’adresse e-mail.
Les listes renvoient data et meta.nextCursor. Transmettez ce curseur sans le modifier pour obtenir la page suivante, puis arrêtez-vous quand il vaut null.
const API_BASE_URL = "https://lunidex.app/api/v1";
const apiKey = process.env.LUNIDEX_API_KEY;
let cursor = null;
if (!apiKey) throw new Error("Missing LUNIDEX_API_KEY");
do {
const query = new URLSearchParams({ limit: "100" });
if (cursor) query.set("cursor", cursor);
const response = await fetch(API_BASE_URL + "/cards?" + query, {
headers: { Authorization: "Bearer " + apiKey },
});
const result = await response.json();
if (!response.ok) throw new Error(result.error?.code ?? "REQUEST_FAILED");
console.log(result.data);
cursor = result.meta?.nextCursor ?? null;
} while (cursor);Les chemins sont relatifs à l’URL de base de l’API. Chaque ligne indique le niveau d’accès et les paramètres du contrat OpenAPI. La spécification lisible par machine contient les schémas complets, des exemples de requête et de réponse, ainsi que les erreurs par opération ; les sections ci-dessous expliquent les principaux usages.
/openapi.jsonContrat lisible par machine, public et sans clé API.
/meIdentité limitée du compte, sans adresse e-mail.
/summaryStatistiques principales, nombre physique et distinct de cartes, et totaux des scellés en centimes EUR.
/cardsPossessions paginées, avec filtres facultatifs de langue et de set.
cursor(requête)limit(requête)language(requête)set(requête)/cards/{cardId}Métadonnées TCGdex et possessions du compte, sans valorisation des cartes.
cardId(chemin)· obligatoirelanguage(requête)/cards/{cardId}Définit la quantité absolue pour une langue et une variante.
cardId(chemin)· obligatoire/sealed/catalogueRecherche dans le catalogue existant de produits scellés Cardmarket.
q(requête)cursor(requête)/sealed/positionsPositions actuelles paginées, coût et valeur de marché stockée.
cursor(requête)limit(requête)language(requête)productId(requête)/sealed/positions/{productId}Positions par langue pour un produit possédé.
productId(chemin)· obligatoire/sealed/transactionsJournal privé avec révisions et filtres.
cursor(requête)limit(requête)language(requête)productId(requête)type(requête)includeVoided(requête)voided(requête)/sealed/transactionsCrée un achat, une vente ou un échange ; clé d’idempotence requise.
Idempotency-Key(en-tête)· obligatoire/sealed/transactions/{id}Lit une transaction appartenant à ce compte.
id(chemin)· obligatoire/sealed/transactions/{id}Modifie une transaction avec contrôle de révision.
id(chemin)· obligatoire/sealed/transactions/{id}/voidAnnule une transaction avec contrôle de révision.
id(chemin)· obligatoireGET /cards liste les variantes possédées sans charger tout le catalogue TCGdex. Chaque possession contient cardId, setId, language, variant et quantity ; les filtres facultatifs sont language et set.
GET /cards/{cardId} renvoie les métadonnées TCGdex utiles et les possessions de cette carte dans le compte. Ajoutez le paramètre language pour limiter les possessions.
PUT /cards/{cardId} reçoit language, variant (unspecified, normal, reverse ou holo) et une quantité absolue comprise entre 0 et 10 000. La valeur 0 retire cette variante.
{
"language": "en",
"variant": "reverse",
"quantity": 2
}Les anciennes possessions sans langue restent visibles ; leur langue peut valoir null et elles peuvent être marquées legacy.
Cette API ne renvoie jamais la valeur des cartes.
Les positions indiquent les quantités par langue, le coût et la valeur actuelle calculée à partir des prix déjà stockés dans Lunidex. Les montants sont des centimes entiers d’EUR. Si un prix manque, valueCents vaut null et missingPrice l’indique ; le résumé compte aussi missingPrices.
Dans GET /summary, la valeur des scellés vaut null si le prix manque pour au moins un produit possédé ; missingPrices donne le nombre de prix manquants.
Recherchez les produits déjà présents au catalogue Cardmarket avec q (150 caractères maximum) et cursor. Chaque page contient au plus 24 produits ; utilisez leur ID dans les requêtes suivantes.
GET /sealed/positions accepte language et productId. GET /sealed/positions/{productId} renvoie la position actuelle d’un produit. Ces routes exposent les totaux actuels, sans série complète d’historique des prix.
Le journal privé accepte les filtres language, productId, type (buy, sell, exchange), includeVoided et voided. Il renvoie des révisions pour permettre des modifications sûres.
PUT /cards/{cardId} définit une quantité au lieu d’ajouter un delta. Répéter la même requête conserve la même quantité ; envoyez 0 pour retirer la variante. Avant l’écriture, l’API vérifie la carte, le set, la langue et la variante disponible.
POST /sealed/transactions accepte les mouvements buy, sell et exchange. Il exige l’en-tête Idempotency-Key (8 à 200 caractères) et expectedRevision. Réutilisez la même clé pour réessayer la même requête afin de ne pas créer un second mouvement. La première requête crée la transaction ; une répétition renvoie le résultat d’origine.
curl --request POST "https://lunidex.app/api/v1/sealed/transactions" \
-H "Authorization: Bearer $LUNIDEX_API_KEY" \
-H "Idempotency-Key: lunidex-import-example-001" \
-H "Content-Type: application/json" \
--data '{
"expectedRevision": 0,
"kind": "buy",
"cardmarketProductId": 12345,
"language": "en",
"date": "2026-09-27",
"quantity": 1,
"unitPriceCents": 2500
}'Avant une écriture, lisez la dernière révision du journal dans les métadonnées de GET /sealed/transactions et utilisez-la comme expectedRevision. PATCH /sealed/transactions/{id} exige aussi la revision de cette transaction. Une révision obsolète renvoie 409 ; rechargez la transaction et le journal avant de décider de réessayer.
PATCH /sealed/transactions/{id}
POST /sealed/transactions/{id}/void
POST /sealed/transactions/{id}/void exige revision et expectedRevision. Création, modification et annulation réutilisent les validations, allocations et l’audit des transactions Lunidex.
{
"revision": 1,
"expectedRevision": 0
}Une réponse réussie utilise { "data": ..., "meta": ... } (meta est facultatif). Une erreur utilise { "error": { "code": ..., "message": ..., "details": ... } } (details est facultatif).
Réponses courantes : 400 données de transaction invalides ; 401 clé invalide ; 403 permission insuffisante ; 404 ressource absente ou non détenue ; 409 curseur ou révision obsolète ; 410 compte en cours de suppression ; 422 paramètre de requête ou corps invalide ; 429 quota atteint ; 500 erreur serveur inattendue ; 502 données de carte indisponibles ; 503 API indisponible.
Après un 409, récupérez la ressource à jour ou recommencez la liste sans curseur. Ne transformez pas aveuglément un conflit en nouvelle transaction.
Une réponse 429 inclut Retry-After. Attendez au moins le nombre de secondes indiqué avant de réessayer.
Toutes les réponses privées utilisent Cache-Control: private, no-store. La spécification OpenAPI publique est disponible séparément sur /api/v1/openapi.json.
Les limites s’appliquent par compte Lunidex et sont partagées entre ses clés API.
| Lectures par minute | 60 |
|---|---|
| Écritures par minute | 10 |
| Lectures par jour | 1 000 |
| Écritures par jour | 100 |
| Détails de cartes par compte et par jour | 100 |
| Calculs de scellés par compte et par jour | 100 |
| Plafond global quotidien par catégorie coûteuse | 5 000 |
Les détails de cartes et les calculs de scellés ont aussi chacun un plafond global distinct de 5 000 opérations par jour. Une réponse 429 inclut Retry-After.
Ouvrir la spécification OpenAPI