# PNJ

> Les PNJ : boss et rencontres, butin, objets vendus, quêtes, réputation, emplacements sur les cartes.

Documentation de l’API de WoWDB des Défias : https://docs.wowdb.assemblee-defias.fr/npcs

## PNJ

Tous les PNJ du jeu : monstres, boss, marchands, donneurs de quêtes. Les PNJ techniques et les variantes de difficulté (« VanCleef (1) ») sont absents des listes. La fiche d’un boss donne sa **rencontre** (`encounter`) : son donjon, sa place dans l’ordre du jeu, le boss précédent et le suivant, et l’adresse de son butin par difficulté.

**Routes**

- `GET /v1/{version}/npcs` : Lister les PNJ
- `GET /v1/{version}/npcs/{id}` : Obtenir un PNJ
- `GET /v1/{version}/npcs/{id}/loot` : Lister le butin d’un PNJ
- `GET /v1/{version}/npcs/{id}/sells` : Lister les objets vendus par un PNJ
- `GET /v1/{version}/npcs/{id}/quests` : Lister les quêtes d’un PNJ
- `GET /v1/{version}/npcs/{id}/locations` : Lister les emplacements d’un PNJ

## L’objet npc

Un PNJ. `loot` et `sells` sont des listes incluses (les premiers éléments, puis leur adresse).

- `object` (chaîne) : Type d’objet, toujours « npc ». Valeurs : `npc`.
- `id` (entier) : Identifiant de le PNJ : son numéro dans le jeu (entrée de la base du jeu), le même dans les trois versions quand le PNJ y existe.
- `key` (chaîne) : Clé WoWDB, unique dans toute la base : « <version>:<type>:<id> » (classic:npc:639).
- `version` (code) : Version du jeu de cette fiche. Valeurs : `classic`, `tbc`, `wotlk`.
- `name` (chaîne) : Nom français, comme en jeu.
- `icon` (chaîne ou null) : Adresse absolue de l’icône du jeu (WebP, JPEG ou PNG), servie par WoWDB ; null si la fiche n’en a pas.
- `subtitle` (chaîne ou null) : Sous-titre (« Caïd défias »).
- `level_min` (entier ou null) : Niveau minimum ; null si inconnu (« ?? » des boss).
- `level_max` (entier ou null) : Niveau maximum.
- `type` (code ou null) : Type de créature. Valeurs : voir les codes (https://docs.wowdb.assemblee-defias.fr/filtres#codes).
- `type_name` (chaîne ou null) : Type, libellé français.
- `family` (code ou null) : Famille des bêtes et des démons. Valeurs : voir les codes (https://docs.wowdb.assemblee-defias.fr/filtres#codes).
- `family_name` (chaîne ou null) : Famille, libellé français.
- `rank` (code) : Rang : normal, élite, élite rare, boss, rare. Valeurs : `normal`, `elite`, `rare_elite`, `boss`, `rare`.
- `rank_name` (chaîne) : Rang, libellé français.
- `boss` (booléen) : Vrai pour un boss (rang boss).
- `internal` (booléen) : PNJ technique ou variante de difficulté : absent des listes et de la recherche.
- `url` (chaîne) : Adresse publique de la fiche sur WoWDB (page HTML, à donner à vos lecteurs).
- `api_url` (chaîne) : Adresse de le PNJ dans l’API.

Champs de la fiche seulement (`GET …/{id}`) :

- `english_name` (chaîne ou null) : Nom anglais.
- `faction` (objet ou null) : Faction du PNJ.
  - `name` (chaîne) : Nom de la faction.
  - `faction` (Ref ou null) : Réputation liée, quand elle a une fiche.
- `base_npc` (Ref ou null) : PNJ de base, pour une variante de difficulté.
- `zones` (tableau d’objets) : Lieux où il apparaît, du plus fréquent au plus rare.
  - `name` (chaîne ou null) : Nom de la zone ou du donjon.
  - `positions` (entier) : Nombre d’emplacements connus dans ce lieu.
  - `instance` (InstanceRef ou null) : Le donjon ou raid, quand le lieu en est un.
- `encounter` (Encounter ou null) : Rencontre de boss : place du PNJ dans son donjon ou raid ; null hors des rencontres.
- `loot` (liste) : Butin : les 10 premiers objets (Loot), puis /npcs/{id}/loot.
- `sells` (liste) : Objets vendus : les 10 premiers (VendorItem), puis /npcs/{id}/sells.
- `quests` (objet) : Quêtes qu’il donne et reprend.
  - `starts` (tableau de Ref) : Quêtes données.
  - `ends` (tableau de Ref) : Quêtes reprises.
- `reputation` (tableau d’objets) : Réputation gagnée en le tuant.
  - `faction` (Ref ou null) : La réputation.
  - `name` (chaîne ou null) : Nom de la faction.
  - `value` (entier) : Points gagnés.
  - `until_rank_name` (chaîne ou null) : Jusqu’à quel rang.
- `summoned_by` (tableau d’objets) : Objets qui l’invoquent (monture, compagnon).
  - `item` (ItemRef) : L’objet.
  - `type` (chaîne) : mount, companion ou other.
- `locations_url` (chaîne) : Ses emplacements sur les cartes : /npcs/{id}/locations.
- `versions` (tableau de Ref) : La même fiche dans chaque version du jeu où elle existe (elle comprise).
- `guides` (tableau d’objets) : Guides de L’Assemblée des Défias qui citent cette fiche.
  - `title` (chaîne) : Titre du guide.
  - `url` (chaîne) : Adresse du guide sur assemblee-defias.fr.

**Exemple : pnj** (statut 200)

```json
{
  "object": "npc",
  "id": 639,
  "key": "classic:npc:639",
  "version": "classic",
  "name": "Edwin VanCleef",
  "icon": "https://wowdb.assemblee-defias.fr/database-icons/achievement_boss_edwinvancleef.webp",
  "subtitle": "Caïd défias",
  "level_min": 21,
  "level_max": 21,
  "type": "humanoid",
  "type_name": "Humanoïde",
  "family": null,
  "family_name": null,
  "rank": "rare_elite",
  "rank_name": "Élite rare",
  "boss": false,
  "internal": false,
  "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:639",
  "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639",
  "english_name": "Edwin VanCleef",
  "faction": {
    "name": "Confrérie défias",
    "faction": null
  },
  "base_npc": null,
  "zones": [
    {
      "name": "Les Mortemines",
      "positions": 1,
      "instance": {
        "object": "instance",
        "id": 36,
        "version": "classic",
        "name": "Mortemines",
        "type": "dungeon",
        "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
      }
    }
  ],
  "encounter": {
    "object": "encounter",
    "instance": {
      "object": "instance",
      "id": 36,
      "version": "classic",
      "name": "Mortemines",
      "type": "dungeon",
      "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
    },
    "position": 7,
    "total": 8,
    "game_order": true,
    "previous": {
      "name": "Capitaine Vertepeau",
      "npc": {
        "object": "npc",
        "id": 647,
        "version": "classic",
        "name": "Capitaine Vertepeau",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:647",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/647"
      }
    },
    "next": {
      "name": "Mineur Johnson",
      "npc": {
        "object": "npc",
        "id": 3586,
        "version": "classic",
        "name": "Mineur Johnson",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:3586",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3586"
      }
    },
    "items": 4,
    "rogue_items": 3,
    "difficulties": [
      {
        "difficulty": "normal",
        "difficulty_name": "Normal",
        "items": 4
      }
    ],
    "loot_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36/loot?encounter=639"
  },
  "loot": {
    "object": "list",
    "data": [
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5193,
          "version": "classic",
          "name": "Cape de la confrérie",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_misc_cape_08.jpg",
          "item_level": 25,
          "slot_name": "Dos",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5193",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5193"
        },
        "name": "Cape de la confrérie",
        "chance": 30,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5202,
          "version": "classic",
          "name": "Casaque de corsaire",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_shirt_08.webp",
          "item_level": 24,
          "slot_name": "Torse",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5202",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5202"
        },
        "name": "Casaque de corsaire",
        "chance": 30,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 10399,
          "version": "classic",
          "name": "Armure défias noircie",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_chest_leather_08.jpg",
          "item_level": 24,
          "slot_name": "Torse",
          "type_name": "Cuir",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:10399",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/10399"
        },
        "name": "Armure défias noircie",
        "chance": 20,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5191,
          "version": "classic",
          "name": "Barbelure cruelle",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_sword_24.jpg",
          "item_level": 24,
          "slot_name": "À une main",
          "type_name": "Épée",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5191",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5191"
        },
        "name": "Barbelure cruelle",
        "chance": 20,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12985,
          "version": "classic",
          "name": "Anneau de défense",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_jewelry_ring_28.jpg",
          "item_level": 22,
          "slot_name": "Doigt",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12985",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12985"
        },
        "name": "Anneau de défense",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12996,
          "version": "classic",
          "name": "Bague de purification",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_jewelry_ring_31.jpg",
          "item_level": 23,
          "slot_name": "Doigt",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12996",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12996"
        },
        "name": "Bague de purification",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 2879,
          "version": "classic",
          "name": "Bâtonnet des antipodes",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_wand_04.webp",
          "item_level": 22,
          "slot_name": "Tenu(e) en main gauche",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:2879",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/2879"
        },
        "name": "Bâtonnet des antipodes",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12987,
          "version": "classic",
          "name": "Braies des ombres",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_pants_06.jpg",
          "item_level": 22,
          "slot_name": "Jambes",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12987",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12987"
        },
        "name": "Braies des ombres",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12979,
          "version": "classic",
          "name": "Cape coupe-feu",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_misc_cape_08.jpg",
          "item_level": 21,
          "slot_name": "Dos",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12979",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12979"
        },
        "name": "Cape coupe-feu",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12978,
          "version": "classic",
          "name": "Ceinture porte-tempête",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_belt_15.jpg",
          "item_level": 20,
          "slot_name": "Taille",
          "type_name": "Mailles",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12978",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12978"
        },
        "name": "Ceinture porte-tempête",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      }
    ],
    "has_more": true,
    "next_cursor": "eyJvIjoxMCwiaCI6InZLczczWkcwNW4ifQ",
    "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot"
  },
  "sells": {
    "object": "list",
    "data": [],
    "has_more": false,
    "next_cursor": null,
    "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/sells"
  },
  "quests": {
    "starts": [],
    "ends": []
  },
  "reputation": [],
  "summoned_by": [],
  "locations_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations",
  "versions": [
    {
      "object": "npc",
      "id": 639,
      "version": "classic",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639"
    },
    {
      "object": "npc",
      "id": 639,
      "version": "tbc",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=tbc:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/tbc/npcs/639"
    },
    {
      "object": "npc",
      "id": 639,
      "version": "wotlk",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=wotlk:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/wotlk/npcs/639"
    }
  ],
  "guides": []
}
```

## L’objet loot

Une ligne de butin d’un PNJ ou d’un objet du monde.

- `object` (chaîne) : Toujours « loot ». Valeurs : `loot`.
- `item` (ItemRef ou null) : L’objet (avec niveau, emplacement et type).
- `name` (chaîne) : Nom de l’objet.
- `chance` (nombre ou null) : Chance de butin en pour cent (la plus haute des difficultés).
- `difficulties` (tableau de codes) : Difficultés où il tombe. Valeurs : `normal`, `heroic`, `10_player`, `25_player`, `10_player_heroic`, `25_player_heroic`.
- `quest_item` (booléen) : Objet de quête (ne tombe que pour qui a la quête).
- `instance` (InstanceRef ou null) : Donjon où il tombe.

## L’objet map

Les emplacements d’un PNJ ou d’un objet du monde, groupés par fond de carte du jeu. Les coordonnées sont en pour cent de l’image, comme en jeu : placez un point à `left: x%` et `top: y%` sur l’`image`.

- `object` (chaîne) : Toujours « map ». Valeurs : `map`.
- `name` (chaîne ou null) : Nom de la zone ou de l’étage.
- `floor` (entier ou null) : Étage du donjon ; null pour une zone.
- `instance` (InstanceRef ou null) : Donjon de l’étage.
- `image` (chaîne ou null) : Fond de carte du jeu (WebP), servi par WoWDB.
- `width` (entier) : Largeur de référence du fond, en pixels.
- `height` (entier) : Hauteur de référence.
- `points` (tableau d’objets) : Emplacements, en pour cent de la largeur et de la hauteur (0 à 100), comme les coordonnées du jeu.
  - `x` (nombre) : Abscisse (0 à gauche).
  - `y` (nombre) : Ordonnée (0 en haut).

## Lister les PNJ

`GET /v1/{version}/npcs`

Tous les PNJ d’une version (PNJ techniques et variantes de difficulté exclus).

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `q` (chaîne, facultatif) : Filtre sur le nom : chaque mot doit commencer un mot du nom (« lame om » trouve « Lame de l’ombre »). Sans accent ni majuscule nécessaires.
- `type` (codes séparés par des virgules, facultatif) : Types de créature. Plusieurs valeurs séparées par des virgules. Valeurs : `humanoid`, `beast`, `demon`, `dragonkin`, `elemental`, `giant`, `undead`, `mechanical`, `critter`, `totem`, `non_combat_pet`, `gas_cloud`, `not_specified`, `none`.
- `rank` (codes séparés par des virgules, facultatif) : Rangs. Plusieurs valeurs séparées par des virgules. Valeurs : `normal`, `elite`, `rare_elite`, `boss`, `rare`.
- `family` (codes séparés par des virgules, facultatif) : Familles de bêtes et de démons. Plusieurs valeurs séparées par des virgules. Valeurs : voir https://docs.wowdb.assemblee-defias.fr/filtres#codes.
- `min_level` (entier, facultatif) : Niveau (le plus haut) minimum (1 à 100).
- `max_level` (entier, facultatif) : Niveau (le plus haut) maximum (1 à 100).
- `sort` (chaîne, facultatif) : Ordre : name, level ; « - » devant inverse l’ordre (-item_level : du plus haut au plus bas). Valeurs vides en fin de liste. Par défaut : name. Valeurs : `name`, `-name`, `level`, `-level`.
- `limit` (entier, facultatif) : Nombre d’éléments par page, de 1 à 100 (20 par défaut).
- `cursor` (chaîne, facultatif) : Curseur de la page suivante : la valeur de next_cursor de la page précédente, avec les mêmes filtres.
- `include_total` (booléen, facultatif) : true : ajoute total_count, le nombre total d’éléments avec ces filtres.

**Renvoie** : Une liste (`object: "list"`) d’objets `npc`, paginée par curseur.

**Requête (GET /v1/{version}/npcs)**

#### cURL

```bash
curl -G https://api.wowdb.assemblee-defias.fr/v1/classic/npcs \
  -d q=vancleef \
  -d limit=2
```

#### JavaScript

```js
const params = new URLSearchParams({
  q: "vancleef",
  limit: "2",
});
const res = await fetch(`https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?${params}`);
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const npc of page.data) {
  console.log(npc.name);
}
```

#### Python

```python
import json
import urllib.parse
import urllib.request

params = urllib.parse.urlencode({
    "q": "vancleef",
    "limit": "2",
})
req = urllib.request.Request(
    f"https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?{params}",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    page = json.load(res)
for npc in page["data"]:
    print(npc["name"])
```

#### PHP

```php
<?php
$params = http_build_query([
    'q' => 'vancleef',
    'limit' => '2',
]);
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?' . $params, false, $context);
$page = json_decode($json, true);
foreach ($page['data'] as $npc) {
    echo $npc['name'], PHP_EOL;
}
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs")
uri.query = URI.encode_www_form(
  q: "vancleef",
  limit: "2",
)
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
page = JSON.parse(res.body)
page["data"].each do |npc|
  puts npc["name"]
end
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

func main() {
	params := url.Values{}
	params.Set("q", "vancleef")
	params.Set("limit", "2")
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?"+params.Encode(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var page struct {
		Data    []map[string]any `json:"data"`
		HasMore bool             `json:"has_more"`
	}
	if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
		panic(err)
	}
	for _, npc := range page.Data {
		fmt.Println(npc["name"])
	}
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?q=vancleef&limit=2"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?q=vancleef&limit=2");
using var doc = JsonDocument.Parse(json);
foreach (var npc in doc.RootElement.GetProperty("data").EnumerateArray())
    Console.WriteLine(npc.GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let page: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .query("q", "vancleef")
        .query("limit", "2")
        .call()?
        .into_json()?;
    for npc in page["data"].as_array().into_iter().flatten() {
        println!("{}", npc["name"].as_str().unwrap_or_default());
    }
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "npc",
      "id": 639,
      "key": "classic:npc:639",
      "version": "classic",
      "name": "Edwin VanCleef",
      "icon": "https://wowdb.assemblee-defias.fr/database-icons/achievement_boss_edwinvancleef.webp",
      "subtitle": "Caïd défias",
      "level_min": 21,
      "level_max": 21,
      "type": "humanoid",
      "type_name": "Humanoïde",
      "family": null,
      "family_name": null,
      "rank": "rare_elite",
      "rank_name": "Élite rare",
      "boss": false,
      "internal": false,
      "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs?q=vancleef"
}
```

## Obtenir un PNJ

`GET /v1/{version}/npcs/{id}`

La fiche complète du PNJ : les champs de la liste, plus ceux marqués « fiche seulement ».

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `id` (entier, dans le chemin, obligatoire) : Identifiant du jeu du PNJ.

**Renvoie** : La fiche complète, objet `npc` (champs du résumé et champs « fiche seulement »), ou une erreur 404.

**Requête (GET /v1/{version}/npcs/{id})**

#### cURL

```bash
curl https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639
```

#### JavaScript

```js
const res = await fetch("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639");
if (!res.ok) throw new Error((await res.json()).error.message);
const npc = await res.json();
console.log(npc.name);
```

#### Python

```python
import json
import urllib.request

req = urllib.request.Request(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    npc = json.load(res)
print(npc["name"])
```

#### PHP

```php
<?php
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639', false, $context);
$npc = json_decode($json, true);
echo $npc['name'], PHP_EOL;
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639")
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
npc = JSON.parse(res.body)
puts npc["name"]
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var npc map[string]any
	if err := json.NewDecoder(res.Body).Decode(&npc); err != nil {
		panic(err)
	}
	fmt.Println(npc["name"])
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639");
using var doc = JsonDocument.Parse(json);
Console.WriteLine(doc.RootElement.GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let npc: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .call()?
        .into_json()?;
    println!("{}", npc["name"].as_str().unwrap_or_default());
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "npc",
  "id": 639,
  "key": "classic:npc:639",
  "version": "classic",
  "name": "Edwin VanCleef",
  "icon": "https://wowdb.assemblee-defias.fr/database-icons/achievement_boss_edwinvancleef.webp",
  "subtitle": "Caïd défias",
  "level_min": 21,
  "level_max": 21,
  "type": "humanoid",
  "type_name": "Humanoïde",
  "family": null,
  "family_name": null,
  "rank": "rare_elite",
  "rank_name": "Élite rare",
  "boss": false,
  "internal": false,
  "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:639",
  "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639",
  "english_name": "Edwin VanCleef",
  "faction": {
    "name": "Confrérie défias",
    "faction": null
  },
  "base_npc": null,
  "zones": [
    {
      "name": "Les Mortemines",
      "positions": 1,
      "instance": {
        "object": "instance",
        "id": 36,
        "version": "classic",
        "name": "Mortemines",
        "type": "dungeon",
        "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
      }
    }
  ],
  "encounter": {
    "object": "encounter",
    "instance": {
      "object": "instance",
      "id": 36,
      "version": "classic",
      "name": "Mortemines",
      "type": "dungeon",
      "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
    },
    "position": 7,
    "total": 8,
    "game_order": true,
    "previous": {
      "name": "Capitaine Vertepeau",
      "npc": {
        "object": "npc",
        "id": 647,
        "version": "classic",
        "name": "Capitaine Vertepeau",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:647",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/647"
      }
    },
    "next": {
      "name": "Mineur Johnson",
      "npc": {
        "object": "npc",
        "id": 3586,
        "version": "classic",
        "name": "Mineur Johnson",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:3586",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3586"
      }
    },
    "items": 4,
    "rogue_items": 3,
    "difficulties": [
      {
        "difficulty": "normal",
        "difficulty_name": "Normal",
        "items": 4
      }
    ],
    "loot_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36/loot?encounter=639"
  },
  "loot": {
    "object": "list",
    "data": [
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5193,
          "version": "classic",
          "name": "Cape de la confrérie",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_misc_cape_08.jpg",
          "item_level": 25,
          "slot_name": "Dos",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5193",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5193"
        },
        "name": "Cape de la confrérie",
        "chance": 30,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5202,
          "version": "classic",
          "name": "Casaque de corsaire",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_shirt_08.webp",
          "item_level": 24,
          "slot_name": "Torse",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5202",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5202"
        },
        "name": "Casaque de corsaire",
        "chance": 30,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 10399,
          "version": "classic",
          "name": "Armure défias noircie",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_chest_leather_08.jpg",
          "item_level": 24,
          "slot_name": "Torse",
          "type_name": "Cuir",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:10399",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/10399"
        },
        "name": "Armure défias noircie",
        "chance": 20,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 5191,
          "version": "classic",
          "name": "Barbelure cruelle",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_sword_24.jpg",
          "item_level": 24,
          "slot_name": "À une main",
          "type_name": "Épée",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5191",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5191"
        },
        "name": "Barbelure cruelle",
        "chance": 20,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12985,
          "version": "classic",
          "name": "Anneau de défense",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_jewelry_ring_28.jpg",
          "item_level": 22,
          "slot_name": "Doigt",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12985",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12985"
        },
        "name": "Anneau de défense",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12996,
          "version": "classic",
          "name": "Bague de purification",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_jewelry_ring_31.jpg",
          "item_level": 23,
          "slot_name": "Doigt",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12996",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12996"
        },
        "name": "Bague de purification",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 2879,
          "version": "classic",
          "name": "Bâtonnet des antipodes",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_wand_04.webp",
          "item_level": 22,
          "slot_name": "Tenu(e) en main gauche",
          "type_name": null,
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:2879",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/2879"
        },
        "name": "Bâtonnet des antipodes",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12987,
          "version": "classic",
          "name": "Braies des ombres",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_pants_06.jpg",
          "item_level": 22,
          "slot_name": "Jambes",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12987",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12987"
        },
        "name": "Braies des ombres",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12979,
          "version": "classic",
          "name": "Cape coupe-feu",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_misc_cape_08.jpg",
          "item_level": 21,
          "slot_name": "Dos",
          "type_name": "Tissu",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12979",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12979"
        },
        "name": "Cape coupe-feu",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      },
      {
        "object": "loot",
        "item": {
          "object": "item",
          "id": 12978,
          "version": "classic",
          "name": "Ceinture porte-tempête",
          "quality": "rare",
          "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_belt_15.jpg",
          "item_level": 20,
          "slot_name": "Taille",
          "type_name": "Mailles",
          "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:12978",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/12978"
        },
        "name": "Ceinture porte-tempête",
        "chance": 0.01,
        "difficulties": [
          "normal"
        ],
        "quest_item": false,
        "instance": {
          "object": "instance",
          "id": 36,
          "version": "classic",
          "name": "Mortemines",
          "type": "dungeon",
          "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
          "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
        }
      }
    ],
    "has_more": true,
    "next_cursor": "eyJvIjoxMCwiaCI6InZLczczWkcwNW4ifQ",
    "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot"
  },
  "sells": {
    "object": "list",
    "data": [],
    "has_more": false,
    "next_cursor": null,
    "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/sells"
  },
  "quests": {
    "starts": [],
    "ends": []
  },
  "reputation": [],
  "summoned_by": [],
  "locations_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations",
  "versions": [
    {
      "object": "npc",
      "id": 639,
      "version": "classic",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=classic:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639"
    },
    {
      "object": "npc",
      "id": 639,
      "version": "tbc",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=tbc:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/tbc/npcs/639"
    },
    {
      "object": "npc",
      "id": 639,
      "version": "wotlk",
      "name": "Edwin VanCleef",
      "url": "https://wowdb.assemblee-defias.fr/?id=wotlk:npc:639",
      "api_url": "https://api.wowdb.assemblee-defias.fr/v1/wotlk/npcs/639"
    }
  ],
  "guides": []
}
```

## Lister le butin d’un PNJ

`GET /v1/{version}/npcs/{id}/loot`

Tout le butin du PNJ, une ligne par objet (difficultés réunies), la meilleure qualité puis la meilleure chance d’abord.

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `id` (entier, dans le chemin, obligatoire) : Identifiant du jeu du PNJ.
- `limit` (entier, facultatif) : Nombre d’éléments par page, de 1 à 100 (20 par défaut).
- `cursor` (chaîne, facultatif) : Curseur de la page suivante : la valeur de next_cursor de la page précédente, avec les mêmes filtres.
- `include_total` (booléen, facultatif) : true : ajoute total_count, le nombre total d’éléments avec ces filtres.

**Renvoie** : Une liste (`object: "list"`) d’objets `loot`, paginée par curseur.

**Requête (GET /v1/{version}/npcs/{id}/loot)**

#### cURL

```bash
curl -G https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot \
  -d limit=2
```

#### JavaScript

```js
const params = new URLSearchParams({
  limit: "2",
});
const res = await fetch(`https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?${params}`);
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const loot of page.data) {
  console.log(loot.name);
}
```

#### Python

```python
import json
import urllib.parse
import urllib.request

params = urllib.parse.urlencode({
    "limit": "2",
})
req = urllib.request.Request(
    f"https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?{params}",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    page = json.load(res)
for loot in page["data"]:
    print(loot["name"])
```

#### PHP

```php
<?php
$params = http_build_query([
    'limit' => '2',
]);
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?' . $params, false, $context);
$page = json_decode($json, true);
foreach ($page['data'] as $loot) {
    echo $loot['name'], PHP_EOL;
}
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot")
uri.query = URI.encode_www_form(
  limit: "2",
)
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
page = JSON.parse(res.body)
page["data"].each do |loot|
  puts loot["name"]
end
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

func main() {
	params := url.Values{}
	params.Set("limit", "2")
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?"+params.Encode(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var page struct {
		Data    []map[string]any `json:"data"`
		HasMore bool             `json:"has_more"`
	}
	if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
		panic(err)
	}
	for _, loot := range page.Data {
		fmt.Println(loot["name"])
	}
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?limit=2"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot?limit=2");
using var doc = JsonDocument.Parse(json);
foreach (var loot in doc.RootElement.GetProperty("data").EnumerateArray())
    Console.WriteLine(loot.GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let page: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .query("limit", "2")
        .call()?
        .into_json()?;
    for loot in page["data"].as_array().into_iter().flatten() {
        println!("{}", loot["name"].as_str().unwrap_or_default());
    }
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "loot",
      "item": {
        "object": "item",
        "id": 5193,
        "version": "classic",
        "name": "Cape de la confrérie",
        "quality": "rare",
        "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_misc_cape_08.jpg",
        "item_level": 25,
        "slot_name": "Dos",
        "type_name": "Tissu",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5193",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5193"
      },
      "name": "Cape de la confrérie",
      "chance": 30,
      "difficulties": [
        "normal"
      ],
      "quest_item": false,
      "instance": {
        "object": "instance",
        "id": 36,
        "version": "classic",
        "name": "Mortemines",
        "type": "dungeon",
        "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
      }
    },
    {
      "object": "loot",
      "item": {
        "object": "item",
        "id": 5202,
        "version": "classic",
        "name": "Casaque de corsaire",
        "quality": "rare",
        "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_shirt_08.webp",
        "item_level": 24,
        "slot_name": "Torse",
        "type_name": "Tissu",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:5202",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/5202"
      },
      "name": "Casaque de corsaire",
      "chance": 30,
      "difficulties": [
        "normal"
      ],
      "quest_item": false,
      "instance": {
        "object": "instance",
        "id": 36,
        "version": "classic",
        "name": "Mortemines",
        "type": "dungeon",
        "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
      }
    }
  ],
  "has_more": true,
  "next_cursor": "eyJvIjoyLCJoIjoidktzNzNaRzA1biJ9",
  "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/loot"
}
```

## Lister les objets vendus par un PNJ

`GET /v1/{version}/npcs/{id}/sells`

Les objets qu’un marchand vend, par nom.

Exemple : **Gerald Crawley**, fournisseur en poisons de la Horde.

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `id` (entier, dans le chemin, obligatoire) : Identifiant du jeu du PNJ.
- `limit` (entier, facultatif) : Nombre d’éléments par page, de 1 à 100 (20 par défaut).
- `cursor` (chaîne, facultatif) : Curseur de la page suivante : la valeur de next_cursor de la page précédente, avec les mêmes filtres.
- `include_total` (booléen, facultatif) : true : ajoute total_count, le nombre total d’éléments avec ces filtres.

**Renvoie** : Une liste (`object: "list"`) d’objets `vendor_item`, paginée par curseur.

**Requête (GET /v1/{version}/npcs/{id}/sells)**

#### cURL

```bash
curl -G https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells \
  -d limit=2
```

#### JavaScript

```js
const params = new URLSearchParams({
  limit: "2",
});
const res = await fetch(`https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?${params}`);
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const entry of page.data) {
  console.log(entry.name);
}
```

#### Python

```python
import json
import urllib.parse
import urllib.request

params = urllib.parse.urlencode({
    "limit": "2",
})
req = urllib.request.Request(
    f"https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?{params}",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    page = json.load(res)
for entry in page["data"]:
    print(entry["name"])
```

#### PHP

```php
<?php
$params = http_build_query([
    'limit' => '2',
]);
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?' . $params, false, $context);
$page = json_decode($json, true);
foreach ($page['data'] as $entry) {
    echo $entry['name'], PHP_EOL;
}
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells")
uri.query = URI.encode_www_form(
  limit: "2",
)
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
page = JSON.parse(res.body)
page["data"].each do |entry|
  puts entry["name"]
end
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

func main() {
	params := url.Values{}
	params.Set("limit", "2")
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?"+params.Encode(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var page struct {
		Data    []map[string]any `json:"data"`
		HasMore bool             `json:"has_more"`
	}
	if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
		panic(err)
	}
	for _, entry := range page.Data {
		fmt.Println(entry["name"])
	}
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?limit=2"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells?limit=2");
using var doc = JsonDocument.Parse(json);
foreach (var entry in doc.RootElement.GetProperty("data").EnumerateArray())
    Console.WriteLine(entry.GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let page: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .query("limit", "2")
        .call()?
        .into_json()?;
    for entry in page["data"].as_array().into_iter().flatten() {
        println!("{}", entry["name"].as_str().unwrap_or_default());
    }
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "vendor_item",
      "item": {
        "object": "item",
        "id": 8923,
        "version": "classic",
        "name": "Essence d'agonie",
        "quality": "common",
        "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_potion_19.jpg",
        "slot_name": null,
        "type_name": "Artisanat",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:8923",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/8923"
      },
      "name": "Essence d'agonie"
    },
    {
      "object": "vendor_item",
      "item": {
        "object": "item",
        "id": 2930,
        "version": "classic",
        "name": "Essence de douleur",
        "quality": "common",
        "icon": "https://wowdb.assemblee-defias.fr/database-icons/inv_potion_12.jpg",
        "slot_name": null,
        "type_name": "Artisanat",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:item:2930",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/items/2930"
      },
      "name": "Essence de douleur"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJvIjoyLCJoIjoiSEk5VVplcjI0UyJ9",
  "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/3090/sells"
}
```

## Lister les quêtes d’un PNJ

`GET /v1/{version}/npcs/{id}/quests`

Les quêtes que le PNJ donne (starts) et reprend (ends), par niveau.

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `id` (entier, dans le chemin, obligatoire) : Identifiant du jeu du PNJ.
- `role` (codes séparés par des virgules, facultatif) : starts, ends ou les deux (par défaut). Valeurs : `starts`, `ends`.
- `limit` (entier, facultatif) : Nombre d’éléments par page, de 1 à 100 (20 par défaut).
- `cursor` (chaîne, facultatif) : Curseur de la page suivante : la valeur de next_cursor de la page précédente, avec les mêmes filtres.
- `include_total` (booléen, facultatif) : true : ajoute total_count, le nombre total d’éléments avec ces filtres.

**Renvoie** : Une liste (`object: "list"`) d’objets `npc_quest`, paginée par curseur.

**Requête (GET /v1/{version}/npcs/{id}/quests)**

#### cURL

```bash
curl -G https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests \
  -d role=starts \
  -d limit=2
```

#### JavaScript

```js
const params = new URLSearchParams({
  role: "starts",
  limit: "2",
});
const res = await fetch(`https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?${params}`);
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const entry of page.data) {
  console.log(entry.quest.name);
}
```

#### Python

```python
import json
import urllib.parse
import urllib.request

params = urllib.parse.urlencode({
    "role": "starts",
    "limit": "2",
})
req = urllib.request.Request(
    f"https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?{params}",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    page = json.load(res)
for entry in page["data"]:
    print(entry["quest"]["name"])
```

#### PHP

```php
<?php
$params = http_build_query([
    'role' => 'starts',
    'limit' => '2',
]);
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?' . $params, false, $context);
$page = json_decode($json, true);
foreach ($page['data'] as $entry) {
    echo $entry['quest']['name'], PHP_EOL;
}
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests")
uri.query = URI.encode_www_form(
  role: "starts",
  limit: "2",
)
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
page = JSON.parse(res.body)
page["data"].each do |entry|
  puts entry["quest"]["name"]
end
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

func main() {
	params := url.Values{}
	params.Set("role", "starts")
	params.Set("limit", "2")
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?"+params.Encode(), nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var page struct {
		Data    []map[string]any `json:"data"`
		HasMore bool             `json:"has_more"`
	}
	if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
		panic(err)
	}
	for _, entry := range page.Data {
		fmt.Println(entry["quest"].(map[string]any)["name"])
	}
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?role=starts&limit=2"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?role=starts&limit=2");
using var doc = JsonDocument.Parse(json);
foreach (var entry in doc.RootElement.GetProperty("data").EnumerateArray())
    Console.WriteLine(entry.GetProperty("quest").GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let page: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .query("role", "starts")
        .query("limit", "2")
        .call()?
        .into_json()?;
    for entry in page["data"].as_array().into_iter().flatten() {
        println!("{}", entry["quest"]["name"].as_str().unwrap_or_default());
    }
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "npc_quest",
      "role": "starts",
      "quest": {
        "object": "quest",
        "id": 12,
        "version": "classic",
        "name": "Brigade de la marche de l'Ouest",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:quest:12",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/quests/12"
      },
      "level": 12
    },
    {
      "object": "npc_quest",
      "role": "starts",
      "quest": {
        "object": "quest",
        "id": 13,
        "version": "classic",
        "name": "Brigade de la marche de l'Ouest",
        "url": "https://wowdb.assemblee-defias.fr/?id=classic:quest:13",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/quests/13"
      },
      "level": 14
    }
  ],
  "has_more": true,
  "next_cursor": "eyJvIjoyLCJoIjoiSGxPUEQ5SElyQyJ9",
  "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/234/quests?role=starts"
}
```

## Lister les emplacements d’un PNJ

`GET /v1/{version}/npcs/{id}/locations`

Ses emplacements, groupés par fond de carte du jeu (zone ou étage de donjon), en pour cent de la carte.

**Paramètres**

- `version` (code, dans le chemin, obligatoire) : Version du jeu : classic (Vanilla 1.12.1), tbc (2.4.3) ou wotlk (3.3.5a). Valeurs : `classic`, `tbc`, `wotlk`.
- `id` (entier, dans le chemin, obligatoire) : Identifiant du jeu du PNJ.
- `limit` (entier, facultatif) : Nombre d’éléments par page, de 1 à 100 (20 par défaut).
- `cursor` (chaîne, facultatif) : Curseur de la page suivante : la valeur de next_cursor de la page précédente, avec les mêmes filtres.
- `include_total` (booléen, facultatif) : true : ajoute total_count, le nombre total d’éléments avec ces filtres.

**Renvoie** : Une liste (`object: "list"`) d’objets `map`, paginée par curseur.

**Requête (GET /v1/{version}/npcs/{id}/locations)**

#### cURL

```bash
curl https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations
```

#### JavaScript

```js
const res = await fetch("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations");
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const map of page.data) {
  console.log(map.name);
}
```

#### Python

```python
import json
import urllib.request

req = urllib.request.Request(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations",
    headers={"User-Agent": "mon-application/1.0 (contact@example.com)"},
)
with urllib.request.urlopen(req) as res:
    page = json.load(res)
for map in page["data"]:
    print(map["name"])
```

#### PHP

```php
<?php
$context = stream_context_create([
    'http' => ['header' => "User-Agent: mon-application/1.0 (contact@example.com)\r\n"],
]);
$json = file_get_contents('https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations', false, $context);
$page = json_decode($json, true);
foreach ($page['data'] as $map) {
    echo $map['name'], PHP_EOL;
}
```

#### Ruby

```ruby
require "json"
require "net/http"

uri = URI("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations")
res = Net::HTTP.get_response(uri, { "User-Agent" => "mon-application/1.0 (contact@example.com)" })
page = JSON.parse(res.body)
page["data"].each do |map|
  puts map["name"]
end
```

#### Go

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "mon-application/1.0 (contact@example.com)")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var page struct {
		Data    []map[string]any `json:"data"`
		HasMore bool             `json:"has_more"`
	}
	if err := json.NewDecoder(res.Body).Decode(&page); err != nil {
		panic(err)
	}
	for _, map := range page.Data {
		fmt.Println(map["name"])
	}
}
```

#### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Exemple {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create(
                "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations"))
            .header("User-Agent", "mon-application/1.0 (contact@example.com)")
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // JSON à lire avec Jackson, Gson ou le client WowDb.java (sans dépendance)
        System.out.println(response.body());
    }
}
```

#### .NET

```csharp
using System.Net.Http;
using System.Text.Json;

using var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("mon-application/1.0 (contact@example.com)");
var json = await http.GetStringAsync(
    "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations");
using var doc = JsonDocument.Parse(json);
foreach (var map in doc.RootElement.GetProperty("data").EnumerateArray())
    Console.WriteLine(map.GetProperty("name").GetString());
```

#### Rust

```rust
// Cargo.toml : ureq = { version = "2", features = ["json"] } et serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let page: serde_json::Value = ureq::get("https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations")
        .set("User-Agent", "mon-application/1.0 (contact@example.com)")
        .call()?
        .into_json()?;
    for map in page["data"].as_array().into_iter().flatten() {
        println!("{}", map["name"].as_str().unwrap_or_default());
    }
    Ok(())
}
```

**Réponse** (statut 200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "map",
      "name": "Les Mortemines",
      "floor": 2,
      "instance": {
        "object": "instance",
        "id": 36,
        "version": "classic",
        "name": "Mortemines",
        "type": "dungeon",
        "url": "https://wowdb.assemblee-defias.fr/?instance=classic:36",
        "api_url": "https://api.wowdb.assemblee-defias.fr/v1/classic/instances/36"
      },
      "image": "https://wowdb.assemblee-defias.fr/cartes/classic/36/2.webp",
      "width": 1002,
      "height": 668,
      "points": [
        {
          "x": 60.6,
          "y": 45.9
        }
      ]
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "https://api.wowdb.assemblee-defias.fr/v1/classic/npcs/639/locations"
}
```

---

API de WoWDB des Défias, v1 (2026-10-01). Adresse de base : https://api.wowdb.assemblee-defias.fr/v1. Index : https://docs.wowdb.assemblee-defias.fr/llms.txt.
