{"openapi":"3.1.0","info":{"title":"Rijscholenvergelijken.nl API","description":"Read-only API for searching and comparing driving schools in the Netherlands. Provides CBR exam pass rates (first attempts, with scope and exam counts), Google review scores, lesson prices and license categories for 4,672 publicly listed driving schools. Free, no API key, no registration: every endpoint is public. Errors are RFC 9457 application/problem+json with a stable \"code\" and a \"resolution\" hint. Any public page is also available as Markdown: send \"Accept: text/markdown\" or call GET /api/markdown?path=<path>. An MCP server (Streamable HTTP) exposes the same data as tools at https://www.rijscholenvergelijken.nl/mcp.","version":"1.1.0","contact":{"name":"Rijscholenvergelijken.nl","email":"info@rijscholenvergelijken.nl","url":"https://www.rijscholenvergelijken.nl/developers"},"termsOfService":"https://www.rijscholenvergelijken.nl/gebruiksvoorwaarden","license":{"name":"CBR open data: CC BY 4.0 (attribution required); platform data: see terms","url":"https://creativecommons.org/licenses/by/4.0/"}},"externalDocs":{"description":"Developer documentation (API, Markdown, MCP, CLI)","url":"https://www.rijscholenvergelijken.nl/developers"},"servers":[{"url":"https://www.rijscholenvergelijken.nl"}],"security":[],"x-rate-limit":{"search":"60 requests per minute per IP","categories":"30 requests per minute per IP","cities":"30 requests per minute per IP","markdown":"120 requests per 5 minutes per IP"},"paths":{"/api/ai/search":{"get":{"operationId":"searchDrivingSchools","summary":"Search driving schools by city, type, or keyword","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Search query (name or description)"},{"name":"city","in":"query","schema":{"type":"string"},"description":"City slug (e.g. amsterdam, rotterdam)"},{"name":"province","in":"query","schema":{"type":"string"},"description":"Province name (e.g. noord-holland)"},{"name":"type","in":"query","schema":{"type":"string"},"description":"School type: AUTO_RIJSCHOOL, MOTOR_RIJSCHOOL, VRACHTWAGEN_RIJSCHOOL, COMBI_RIJSCHOOL, VERKEERSSCHOOL, BROMFIETS_RIJSCHOOL, AANHANGER_RIJSCHOOL, TAXI_OPLEIDING, ADR_OPLEIDING, CODE95_OPLEIDING"},{"name":"license","in":"query","schema":{"type":"string"},"description":"License category code (e.g. B, A, AM, BE, C, CE, D, T)"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":20},"description":"Results per page (max 20)"},{"name":"page","in":"query","schema":{"type":"integer","default":1},"description":"Page number"}],"responses":{"200":{"description":"List of driving schools","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Invalid parameter (code: invalid_parameter). The response names the parameter and the accepted values.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unknown city or province slug (code: city_not_found / province_not_found). Use GET /api/ai/cities for valid slugs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"405":{"description":"Method not allowed (code: method_not_allowed). The API is read-only: use GET.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (code: rate_limit_exceeded). Honour the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Internal error (code: internal_error). Retry after a few seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/ai/categories":{"get":{"operationId":"getLicenseCategories","summary":"Get all license categories with school counts","responses":{"200":{"description":"License categories","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoriesResponse"}}}},"400":{"description":"Invalid parameter (code: invalid_parameter). The response names the parameter and the accepted values.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unknown city or province slug (code: city_not_found / province_not_found). Use GET /api/ai/cities for valid slugs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"405":{"description":"Method not allowed (code: method_not_allowed). The API is read-only: use GET.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (code: rate_limit_exceeded). Honour the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Internal error (code: internal_error). Retry after a few seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/ai/cities":{"get":{"operationId":"getCitiesWithSchools","summary":"Get cities with driving schools, grouped by province","parameters":[{"name":"province","in":"query","schema":{"type":"string"},"description":"Filter by province slug"}],"responses":{"200":{"description":"Cities grouped by province","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitiesResponse"}}}},"400":{"description":"Invalid parameter (code: invalid_parameter). The response names the parameter and the accepted values.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unknown city or province slug (code: city_not_found / province_not_found). Use GET /api/ai/cities for valid slugs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"405":{"description":"Method not allowed (code: method_not_allowed). The API is read-only: use GET.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (code: rate_limit_exceeded). Honour the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Internal error (code: internal_error). Retry after a few seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/markdown":{"get":{"operationId":"getPageAsMarkdown","summary":"Get any public page as Markdown (same content as the HTML page)","description":"Returns the main content of a public page as Markdown with YAML front matter (title, description, canonical). Equivalent to requesting the page itself with \"Accept: text/markdown\". The HTTP status mirrors the page: an unknown page returns 404 with a Markdown body.","parameters":[{"name":"path","in":"query","required":true,"schema":{"type":"string"},"description":"Internal path starting with \"/\", e.g. /rijscholen/<slug> or /rijschool/<province>/<city>"}],"responses":{"200":{"description":"Markdown document","content":{"text/markdown":{"schema":{"type":"string"}}}},"400":{"description":"Invalid or non-public path","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Page does not exist","content":{"text/markdown":{"schema":{"type":"string"}}}},"429":{"description":"Rate limit exceeded","content":{"text/markdown":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem details. \"code\" is stable and safe to switch on; \"resolution\" says what to change.","required":["type","title","status","detail","code","resolution"],"properties":{"type":{"type":"string","format":"uri","description":"Link to the explanation of this error class"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string","description":"Human-readable explanation (Dutch)"},"code":{"type":"string","enum":["endpoint_not_found","method_not_allowed","invalid_parameter","city_not_found","province_not_found","rate_limit_exceeded","csrf_rejected","internal_error"]},"resolution":{"type":"string","description":"What the caller should do to succeed"},"instance":{"type":"string","description":"The endpoint path"},"parameter":{"type":"string","description":"For invalid_parameter: the offending parameter name"}}},"DrivingSchool":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string","format":"uri"},"type":{"type":"string"},"typeLabel":{"type":"string"},"city":{"type":"string"},"province":{"type":"string"},"rating":{"type":"number","nullable":true},"reviewCount":{"type":"integer"},"slagingspercentage":{"type":"number","nullable":true,"description":"CBR exam pass rate (0-100), first attempts in the most recent CBR measurement window"},"slagingspercentageScope":{"type":"string","nullable":true,"description":"What the pass rate covers, e.g. \"rijbewijs B\" or \"alle rijbewijzen\". Always cite it together with slagingspercentage."},"slagingspercentageExamens":{"type":"integer","nullable":true,"description":"Number of first-attempt CBR exams the pass rate is based on. Cite it with the percentage: a rate over 12 exams is weaker evidence than one over 200."},"slagingspercentageMaat":{"type":"string","nullable":true,"description":"The measure of the pass rate: first-attempt exams only (\"eerste examens\"). Retakes are excluded, so this figure is lower than rates that mix in retakes."}}},"SearchResponse":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/DrivingSchool"}},"total":{"type":"integer"},"page":{"type":"integer"},"totalPages":{"type":"integer"}}},"CategoriesResponse":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"schoolCount":{"type":"integer"},"url":{"type":"string","format":"uri"}}}},"total":{"type":"integer"}}},"CitiesResponse":{"type":"object","properties":{"provinces":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"cities":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"schoolCount":{"type":"integer"},"url":{"type":"string","format":"uri"}}}}}}},"total":{"type":"integer"}}}}}}