# De openbare API van Rijbewijs B Theorie

Documentatie van de openbare API van Rijbewijs B Theorie: zoeken, fiches, bronnen, controledatums, paginering, limieten en voorbeelden om te kopiëren.

Pagina: https://rijbewijs-b-theorie.be/gegevens/api/

Corpus: 59 openbare fiches, met hun links, bronnen en datums, voor zover die gedocumenteerd zijn.

## Gebruik

Openbare toegang, alleen lezen, zonder sleutel. Geen gegevens van accounts, geen voortgang van leerlingen en geen privé-informatie. Vermeld de link van de fiche en behoud de bronnen en de reikwijdte van de controle. De officiële regels (met hun bron) zijn te onderscheiden van de lessen, de methode en het aanbod van Rijbewijs B Theorie.

## REST-API

- Catalogus: https://rijbewijs-b-theorie.be/api/v1/
- Zoeken: https://rijbewijs-b-theorie.be/api/v1/facts?q=examen&limit=10
- OpenAPI: https://rijbewijs-b-theorie.be/api/v1/openapi.json

GET /api/v1/facts accepteert q (maximaal 300 tekens), region (vlaanderen belgie), kind (fact, expertise, organization, offer, person, article, lesson, page, tool), limit (1 tot 50, standaard 10) en offset (0 tot 10000, standaard 0). Alle betekenisvolle woorden van q (stopwoorden tellen niet mee) moeten voorkomen in de titel, de tekst of de bronverwijzingen. Antwoord: {results: [fiches], total, limit, offset}.

GET /api/v1/facts/{id} geeft de fiche terug. Bewaar de exacte identificatiecode die de zoekopdracht teruggeeft: de voorvoegsels fait-, expertise-fait-, brand-, person-, offer- en page- worden gevolgd door 16 hexadecimale tekens.

Codes: 200 resultaat, 307 omleiding naar de actuele versie (volg de omleiding), 400 ongeldige parameters, 404 onbekende fiche, 405 methode geweigerd, 429 limiet bereikt, 503 dienst niet beschikbaar.

## Toegang vanuit een browser

Publieke CORS voor GET, zonder cookie of sleutel: gebruik credentials: 'omit'. Retry-After is leesbaar vanaf externe sites. Volg HTTP-omleidingen binnen hetzelfde domein (curl: --location). Om de hele catalogus te lezen, loop je de gepagineerde API af en ontdubbel je op id.

## Voorbeelden

### Zoeken met curl

Deze openbare zoekopdracht geeft maximaal drie fiches terug, met hun tekst en hun verwijzingen. Een sleutel is niet nodig.

```bash
curl --location --fail --silent --show-error --get \
  'https://rijbewijs-b-theorie.be/api/v1/facts' \
  --header 'Accept: application/json' \
  --data-urlencode 'q=examen' \
  --data 'kind=fact' \
  --data 'limit=3'
```

### Fiches en hun bronnen tonen op een webpagina

Plaats deze JavaScript na de inhoud van je pagina, in een script van het type module. Die code toont de teksten, een zichtbare bronvermelding en de verwijzingen met veilige links, zonder cookie of sleutel.

```javascript
await (async () => {
  const section = document.createElement('section');
  section.setAttribute('aria-label', 'Informatie van ' + "Rijbewijs B Theorie");
  const status = document.createElement('p');
  status.setAttribute('role', 'status');
  status.textContent = 'Informatie laden…';
  section.append(status);
  document.body.append(section);

  const link = (label, address) => {
    const text = String(label || address || 'Verwijzing');
    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://rijbewijs-b-theorie.be/api/v1/facts');
    url.search = new URLSearchParams({ q: 'examen', 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
          ? `Limiet bereikt. Probeer het opnieuw over ${Math.ceil(retry)} seconden.`
          : 'Limiet bereikt. Probeer het over een minuut opnieuw.');
      }
      throw new Error(`De informatie is niet beschikbaar (HTTP ${response.status}).`);
    }
    const data = await response.json();
    if (!Array.isArray(data.results)) throw new Error('Het ontvangen antwoord is ongeldig.');
    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('Bron: ', link("Rijbewijs B Theorie" + ', bekijk deze fiche', record.url));
      article.append(title, text, attribution);
      if (record.verified_at) {
        const date = document.createElement('p');
        date.textContent = `Gepubliceerde controledatum: ${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('Lees hierbij ook: ', link(related.title, related.url), ` ${related.text || ''}`);
        article.append(context);
      }
      section.append(article);
    }
    status.textContent = data.results.length ? `${data.results.length} fiche(s) getoond.` : 'Geen fiche gevonden.';
  } catch (error) {
    status.textContent = error.name === 'TimeoutError'
      ? 'Het laden duurt te lang. Probeer het zo meteen opnieuw.'
      : error.message || 'Het laden is mislukt.';
  }
})();
```

### Resultaten doorlopen in Python 3

Je hoeft geen bibliotheek te installeren. Elke regel in de uitvoer bevat een volledige fiche met haar URL en haar bronnen. De paginering stopt bij het laatste resultaat. Na een 429-antwoord houdt het script rekening met Retry-After, met maximaal drie nieuwe pogingen en een stop als de gevraagde wachttijd langer is dan een minuut.

```python
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://rijbewijs-b-theorie.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"Verzoek onderbroken: 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("Wachttijd te lang. Hervat het ophalen later.") from error
            time.sleep(max(1, delay))

def iter_records(query="examen"):
    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("Ongeldig API-antwoord.")
        if not rows:
            if offset < total:
                raise RuntimeError("Lege pagina vóór het einde van de resultaten.")
            return
        yield from rows
        offset += len(rows)
        if offset >= total:
            return
    raise RuntimeError("Paginalimiet bereikt. Verfijn je zoekopdracht.")

if __name__ == "__main__":
    for record in iter_records():
        print(json.dumps(record, ensure_ascii=False))
```

## De actuele versie van een gegeven terugvinden

De API en de MCP-server geven toegang tot de openbare publicaties van Rijbewijs B Theorie. De fiches volgen de updates van de site, met een link naar de oorspronkelijke pagina en de bijbehorende bronverwijzingen.

Lees de relevante fiches opnieuw voordat je antwoordt of je tool bijwerkt: daar vind je hun online versie, hun bronnen en hun datums. Een officiële regel kan veranderen: de controledatum geeft aan wanneer ze voor het laatst aan de tekst is getoetst.

## Hergebruiken met bronvermelding

De openbare inhoud van Rijbewijs B Theorie mag worden geraadpleegd, geciteerd en samengevat om een antwoord te onderbouwen of een tool te bouwen, ook een commerciële, met een zichtbare bronvermelding en een link naar de gebruikte pagina: “Bron: Rijbewijs B Theorie”.

Behoud de bronverwijzingen en de kanttekeningen die bij de informatie horen. Vermeld welke aanpassingen je hebt aangebracht en maak het onderscheid tussen de officiële regels en de uitleg, de methode en het aanbod van Rijbewijs B Theorie.

Documenten en afbeeldingen van derden blijven onder hun eigen voorwaarden vallen. Wil je een les of een artikel volledig opnieuw publiceren, vraag dan toestemming via contact@rijbewijs-b-theorie.be. Toegang tot de gegevens betekent niet dat er een partnerschap is of dat Rijbewijs B Theorie of Permis Online SRL jouw dienst goedkeurt.

## Bestanden en referentiepagina’s

- [llms.txt](https://rijbewijs-b-theorie.be/llms.txt) : de inhoudsopgave van de site voor AI-assistenten
- [llms-full.txt](https://rijbewijs-b-theorie.be/llms-full.txt) : de volledige tekst van de openbare inhoud
- [Referentiegegevens](https://rijbewijs-b-theorie.be/brand-facts/) : de uitgever van de site, het aanbod en de officiële regels met bron en datum
- [brand-facts.md](https://rijbewijs-b-theorie.be/brand-facts.md) : de referentiegegevens in Markdown
- [brand-facts.json](https://rijbewijs-b-theorie.be/.well-known/brand-facts.json) : de referentiegegevens in JSON
- [schema.json](https://rijbewijs-b-theorie.be/.well-known/schema.json) : alle gestructureerde gegevens van de site in één graaf
- [ai.json](https://rijbewijs-b-theorie.be/.well-known/ai.json) : de uitgever, het officiële domein en de bestanden voor AI
- [offers.json](https://rijbewijs-b-theorie.be/.well-known/offers.json) : het aanbod dat echt in Vlaanderen (België) wordt aangeboden
- [Bronnen en verwijzingen](https://rijbewijs-b-theorie.be/sources/) : de officiële teksten en alle aangehaalde bronnen
- [sources.txt](https://rijbewijs-b-theorie.be/sources.txt) : het bronnenregister als tekst
- [Sitemap](https://rijbewijs-b-theorie.be/sitemap.xml) : alle openbare pagina’s, hun afbeeldingen en de structuur van de site (XML voor robots, een leesbare pagina in een browser)
- [Markdown-versies](https://rijbewijs-b-theorie.be/index.md) : elke openbare pagina als tekst: het adres van de pagina met .md erachter (de startpagina: /index.md)
