/api/v1/Découvrir le catalogue
Les accès disponibles et les liens de documentation.
Permis Online · Documentation API
Recherche une règle, filtre par région et récupère une fiche avec son texte, ses sources et sa date de vérification. L’accès public se fait en lecture seule, sans clé.
Commencer
L’API renvoie du JSON par requête GET. Aucune clé n’est nécessaire pour consulter ces fiches publiques.
GET https://permis-online.be/api/v1/facts?q=permis%20provisoire®ion=wallonie&kind=fact&limit=10&offset=0Cet exemple recherche « permis provisoire » en Wallonie, parmi les règles et démarches, avec dix résultats au maximum. La réponse contient results, total, limit et offset.
Les routes
/api/v1/Les accès disponibles et les liens de documentation.
/api/v1/factsLa liste des fiches, filtrable et paginée.
/api/v1/facts/{id}Le texte complet, le contexte, les sources et la date.
/api/v1/openapi.jsonLa description structurée des routes, paramètres et réponses.
Affiner la recherche
| Paramètre | Usage | Exemple |
|---|---|---|
q | Mot ou expression, jusqu’à 300 caractères. Sans ce paramètre, l’API liste les fiches. | permis provisoire |
region | wallonie, bruxelles, flandre ou belgique. Cette dernière valeur sélectionne les fiches nationales. Sans filtre : tout le corpus. | wallonie |
kind | fact, expertise, organization, person, offer, professional, article, lesson, page ou tool. | fact |
limit | De 1 à 50 fiches par réponse. Valeur par défaut : 10. | 10 |
offset | Nombre de résultats à passer, de 0 à 10 000. Commence à zéro, puis avance selon le nombre de résultats déjà lus. | 0 |
Le document OpenAPI précise les valeurs acceptées et les réponses.
La limite partagée entre API et MCP est de 120 requêtes par minute et par adresse IP. En cas de réponse 429, attends le délai indiqué par l’en-tête Retry-After avant de recommencer.
Lire et citer
Conserve l’identifiant retourné par la recherche pour demander la fiche complète. La réponse est directement l’objet de la fiche. Le champ url pointe vers sa version lisible.
GET https://permis-online.be/api/v1/facts/fait-f2a7c629fbe649c3{
"id": "fait-f2a7c629fbe649c3",
"kind": "fact",
"title": "Format de l'examen théorique",
"text": "L'examen théorique du permis B est accessible dès 17 ans. Il comporte 50 questions à choix multiple sur ordinateur, avec une seule réponse correcte par question. Les situations peuvent être illustrées. Les questions sont lues à voix haute et affichées à l'écran ; après la lecture complète, le candidat dispose de 15 secondes pour répondre. L'épreuve ordinaire dure environ 30 minutes. Des séances adaptées existent et peuvent prévoir un déroulement différent.",
"url": "https://permis-online.be/brand-facts/#fait-f2a7c629fbe649c3",
"regions": [
"Belgique"
],
"sources": [
{
"url": "https://www.monpermisdeconduire.be/examen-theorique",
"label": "SPW, AWSR et organismes d'examen agréés : Examen théorique B en Wallonie",
"type": "official"
},
{
"url": "https://www.vlaanderen.be/mobiliteit-en-openbare-werken/auto-en-motor/rijbewijzen-en-rijopleiding/rijbewijs-b/theorie-examen-voor-rijbewijs-b",
"label": "Vlaanderen, Departement Mobiliteit en Openbare Werken : Theorie-examen voor rijbewijs B",
"type": "official"
},
{
"url": "https://be.brussels/fr/transport-mobilite/permis-de-conduire-et-immatriculation/obtenir-son-permis/formation-la-conduite-et-examens-pour-le-permis-de-conduire-b",
"label": "Bruxelles Mobilité : Formation à la conduite et examens pour le permis B",
"type": "official"
}
],
"verified_at": "2026-09-18",
"verification_scope": "Format ordinaire du théorique B, âge, nombre de questions, temps de réponse et séances adaptées."
}Les champs text, sources et verification_scope permettent de lire la fiche et de comprendre ce que ses références documentent. Conserve aussi regions, verified_at et, lorsqu’il est présent, evidence_kind, qui distingue notamment une analyse, une méthode ou une déclaration. Les dates modified_at et verified_at correspondent respectivement à la modification du contenu et à la vérification indiquée dans la fiche. Les documents related apportent un contexte complémentaire à lire avant de répondre.
Prêt à brancher
Une requête HTTP, un affichage sur ton site ou une lecture du catalogue en Python. Choisis l’exemple adapté à ton outil.
Les requêtes GET publiques sont accessibles depuis un navigateur externe, sans cookie ni authentification. Le quota de 120 requêtes par minute et par adresse IP est partagé entre API et MCP. Pour parcourir tout le catalogue, utilise la pagination de l’API ; la recherche MCP retourne au maximum 20 résultats.
Le client doit suivre les redirections HTTP sur le même domaine. Le paramètre technique po_live=1 est ajouté automatiquement pour servir une lecture actualisée ; tu peux aussi le transmettre directement. Avec curl, utilise --location.
Cette requête publique renvoie jusqu’à trois fiches, leur texte et leurs références. Aucune clé n’est nécessaire.
curl --location --fail --silent --show-error --get \
'https://permis-online.be/api/v1/facts' \
--header 'Accept: application/json' \
--data-urlencode 'q=permis provisoire' \
--data 'kind=fact' \
--data 'limit=3'Place ce JavaScript après le contenu de ta page, dans un script de type module. Il affiche les textes, une attribution visible et les références avec des liens sûrs, sans cookie ni clé.
await (async () => {
const section = document.createElement('section');
section.setAttribute('aria-label', 'Informations Permis Online');
const status = document.createElement('p');
status.setAttribute('role', 'status');
status.textContent = 'Chargement des informations…';
section.append(status);
document.body.append(section);
const link = (label, address) => {
const text = String(label || address || 'Référence');
try {
const url = new URL(address);
if (!['https:', 'http:'].includes(url.protocol)) throw new Error();
const anchor = document.createElement('a');
anchor.textContent = text;
anchor.href = url.href;
return anchor;
} catch {
const span = document.createElement('span');
span.textContent = text;
return span;
}
};
try {
const url = new URL('https://permis-online.be/api/v1/facts');
url.search = new URLSearchParams({ q: 'permis provisoire', kind: 'fact', limit: '3' });
const response = await fetch(url, {
credentials: 'omit',
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(15000)
});
if (!response.ok) {
if (response.status === 429) {
const retry = Number(response.headers.get('Retry-After'));
throw new Error(Number.isFinite(retry) && retry > 0
? `Limite atteinte. Réessaie dans ${Math.ceil(retry)} secondes.`
: 'Limite atteinte. Réessaie dans une minute.');
}
throw new Error(`Les informations sont indisponibles (HTTP ${response.status}).`);
}
const data = await response.json();
if (!Array.isArray(data.results)) throw new Error('La réponse reçue est invalide.');
for (const record of data.results) {
const article = document.createElement('article');
const title = document.createElement('h2');
title.textContent = record.title;
const text = document.createElement('p');
text.textContent = record.text;
text.style.whiteSpace = 'pre-line';
const attribution = document.createElement('p');
attribution.append('Source : ', link('Permis Online, consulter cette fiche', record.url));
article.append(title, text, attribution);
if (record.verified_at) {
const date = document.createElement('p');
date.textContent = `Date de vérification publiée : ${record.verified_at}`;
if (record.verification_scope) date.textContent += ` · ${record.verification_scope}`;
article.append(date);
}
const references = document.createElement('ul');
for (const source of record.sources || []) {
const item = document.createElement('li');
item.append(link(source.label || source.title || source.url, source.url));
references.append(item);
}
for (const reference of record.bibliography || []) {
const item = document.createElement('li');
item.textContent = reference;
references.append(item);
}
if (references.childElementCount) article.append(references);
for (const related of record.related || []) {
const context = document.createElement('p');
context.append('À lire avec cette fiche : ', link(related.title, related.url), ` ${related.text || ''}`);
article.append(context);
}
section.append(article);
}
status.textContent = data.results.length ? `${data.results.length} fiche(s) affichée(s).` : 'Aucune fiche trouvée.';
} catch (error) {
status.textContent = error.name === 'TimeoutError'
? 'Le chargement prend trop de temps. Réessaie dans un instant.'
: error.message || 'Le chargement a échoué.';
}
})();Aucune bibliothèque à installer. Chaque ligne de sortie contient une fiche complète avec son URL et ses sources. La pagination s’arrête au dernier résultat. Après une réponse 429, le script respecte Retry-After, avec trois reprises au maximum et un arrêt si l’attente demandée dépasse une minute.
import json
import time
from email.utils import parsedate_to_datetime
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
API = "https://permis-online.be/api/v1/facts"
def request_json(url):
for attempt in range(4):
try:
request = Request(url, headers={"Accept": "application/json"})
with urlopen(request, timeout=15) as response:
return json.load(response)
except HTTPError as error:
retry_after = error.headers.get("Retry-After", "")
status = error.code
error.close()
if status != 429 or attempt == 3:
raise RuntimeError(f"Requête interrompue : HTTP {status}") from error
try:
delay = float(retry_after)
except ValueError:
try:
delay = parsedate_to_datetime(retry_after).timestamp() - time.time()
except (ValueError, TypeError, OverflowError):
delay = 2 ** attempt
if not 0 <= delay <= 60:
raise RuntimeError("Attente trop longue. Reprends la collecte plus tard.") from error
time.sleep(max(1, delay))
def iter_records(query="permis provisoire"):
offset = 0
while offset <= 10000:
params = {"q": query, "kind": "fact", "limit": 50, "offset": offset}
page = request_json(API + "?" + urlencode(params))
rows, total = page["results"], page["total"]
if not isinstance(rows, list) or not isinstance(total, int):
raise RuntimeError("Réponse API invalide.")
if not rows:
if offset < total:
raise RuntimeError("Page vide avant la fin des résultats.")
return
yield from rows
offset += len(rows)
if offset >= total:
return
raise RuntimeError("Limite de pagination atteinte. Précise la recherche.")
if __name__ == "__main__":
for record in iter_records():
print(json.dumps(record, ensure_ascii=False))400 : corrige les paramètres. 404 : refais une recherche pour retrouver une fiche disponible. 429 : respecte le délai Retry-After. 503 : réessaie plus tard avec un nombre limité de tentatives. En MCP, contrôle aussi error et isError dans la réponse.
Mise à jour automatique
L’API et le serveur MCP donnent accès aux publications publiques de Permis Online. Les informations disponibles suivent les mises à jour du site, avec un lien vers la page d’origine et les références associées.
Pour préparer une réponse ou actualiser ton outil, consulte à nouveau les fiches utiles. Tu retrouves ainsi leur version disponible, leurs sources et leurs dates.
Conditions de réutilisation
Les contenus publics de Permis Online peuvent être consultés, cités et résumés pour documenter une réponse ou construire un outil, y compris commercial, avec une attribution visible et un lien vers la page utilisée : « Source : Permis Online ».
Conserve les références, la région et les réserves qui accompagnent l’information. Indique les adaptations apportées et distingue les règles officielles des analyses, offres et contenus pédagogiques de Permis Online.
Les documents et visuels de tiers restent soumis à leurs propres conditions. Pour republier intégralement un cours ou un article de Permis Online, demande une autorisation à contact@permis.online. L’accès aux données ne vaut pas partenariat ni approbation de ton service par Permis Online.
Toujours retrouver l’origine
Chaque information peut être relue sur le site, avec les références qui l’accompagnent.