Developer API · Version 1
RailKernel locomotive API
A read-only, server-to-server API for retrieving one complete locomotive record or all locomotive variants for a manufacturer/article combination from the RailKernel Model Database (RMDB), including product identity, prototype data, technical properties, decoder functions, media metadata and external links.
1. API contract
- Base URL
https://railkernel.nl/api/v1- Resource
GET /locomotives.php- Current version
v1- Format
- JSON encoded as UTF-8
- Access
- HTTPS with a Bearer API key
- Required scope
rmdb:locomotive:read- Caching
- Responses currently use
Cache-Control: no-store
The endpoint retrieves either one exact locomotive variant or the collection belonging to one manufacturer and catalogue number. It does not provide bulk export, unrestricted search or mutation operations. An HTTP GET is the only supported method.
2. Authentication
Send the issued API key in the HTTP Authorization header. Never put an API key in a URL or query parameter.
Authorization: Bearer rk_live_<your-api-key>
Keys are shown only when issued, are assigned to one integration partner and can be revoked or replaced independently. Do not embed a key in browser JavaScript, a mobile application, a public repository or downloadable software. Calls must originate from a controlled server.
3. Requesting a locomotive
Preferred: stable RMDB identifier
Use the locomotive’s stable rmdb_uid as id. The colon and any other reserved characters must be URL-encoded by the HTTP client.
curl --fail-with-body \
--request GET \
--header "Authorization: Bearer ${RAILKERNEL_API_KEY}" \
"https://railkernel.nl/api/v1/locomotives.php?id=piko%3A40375"
Collection: manufacturer and catalogue number
Supply exactly vendor and catalog_number to retrieve every locomotive variant recorded for that article. Both parameters are mandatory: a vendor or catalogue number on its own is rejected. The response remains a collection when only one locomotive is found.
curl --fail-with-body \
--get \
--header "Authorization: Bearer ${RAILKERNEL_API_KEY}" \
--data-urlencode "vendor=piko" \
--data-urlencode "catalog_number=40375" \
"https://railkernel.nl/api/v1/locomotives.php"
Alternative: complete composite key
If no RMDB identifier is available, supply all four key components. catalog_suffix is required even when its value is empty, and variant_no starts at 1.
curl --fail-with-body \
--get \
--header "Authorization: Bearer ${RAILKERNEL_API_KEY}" \
--data-urlencode "vendor=piko" \
--data-urlencode "catalog_number=40375" \
--data-urlencode "catalog_suffix=" \
--data-urlencode "variant_no=1" \
"https://railkernel.nl/api/v1/locomotives.php"
| Parameter | Required | Meaning |
|---|---|---|
id or rmdb_uid | Preferred | The stable RMDB locomotive identifier. |
vendor | Article collection or composite request | Technical manufacturer name, compared case-insensitively. Must be supplied together with catalog_number. |
catalog_number | Article collection or composite request | Exact catalogue number; do not convert it to a number. Must be supplied together with vendor. |
catalog_suffix | Exact composite request only | Exact suffix, or an explicitly supplied empty value. Must be supplied together with variant_no. |
variant_no | Exact composite request only | Integer from 1 through 65535. Must be supplied together with catalog_suffix. |
4. Response envelope
A successful request returns HTTP 200. The locomotive resource is always found at data.locomotive.
{
"success": true,
"data": {
"locomotive": {
"identity": {
"id": "piko:40375",
"vendor": "piko",
"catalogNumber": "40375",
"catalogSuffix": "",
"variantNumber": 1
},
"product": { "name": "…" },
"vendor": { "displayName": "Piko", "aliases": [] },
"railway": { "company": "NS", "country": "NL" },
"scale": { "gauge": "N", "ratio": "1:160" },
"dimensions": { "lengthMm": 104 },
"electrical": { "powerSystem": "DC", "digitalProtocol": "DCC" },
"features": {},
"technical": {},
"decoders": [],
"media": { "images": [], "manuals": [] },
"links": { "manufacturerProductUrl": null, "retailers": [] },
"pricing": { "msrp": null, "currency": null },
"remarks": null,
"provenance": { "source": null, "revision": null, "importedAt": null }
}
},
"meta": {
"apiVersion": "1",
"generatedAt": "2026-08-27T08:15:30+00:00"
}
}
The example is intentionally shortened. The complete set of returned fields is documented below. Consumers must read the JSON by field name and must not depend on property order.
Article collection response
A request containing only vendor and catalog_number returns HTTP 200 with an array at data.locomotives. Every array element has the same complete structure documented for data.locomotive. meta.count contains the number of returned variants.
{
"success": true,
"data": {
"locomotives": [
{
"identity": {
"id": "piko:40375",
"vendor": "piko",
"catalogNumber": "40375",
"catalogSuffix": "",
"variantNumber": 1
},
"product": { "name": "…" },
"decoders": []
}
]
},
"meta": {
"apiVersion": "1",
"generatedAt": "2026-08-28T08:30:00+00:00",
"count": 1
}
}
5. Complete field reference
All paths below are relative to data.locomotive.
Identity
| Field path | Type | Description |
|---|---|---|
identity.id | string | null | Stable RMDB identifier. Use this value for subsequent requests. |
identity.vendor | string | Technical manufacturer name used by RMDB. |
identity.catalogNumber | string | Manufacturer catalogue or article number. Letters and leading zeroes are significant. |
identity.catalogSuffix | string | Catalogue suffix; an empty string means that no suffix applies. |
identity.variantNumber | integer | Variant number within the manufacturer/article/suffix combination. |
Product
| Field path | Type | Description |
|---|---|---|
product.name | string | null | Public product name. |
product.description | string | null | Product description as recorded in RMDB. |
product.category | string | null | Main model category. |
product.subCategory | string | null | More specific model category. |
product.discontinued | boolean | null | Whether the model is known to be discontinued. |
product.production.fromYear | integer | null | First known production year. |
product.production.toYear | integer | null | Last known production year. |
Manufacturer
| Field path | Type | Description |
|---|---|---|
vendor.technicalName | string | null | Canonical technical manufacturer name. |
vendor.displayName | string | null | Manufacturer name for display. |
vendor.aliases | string[] | Known alternative manufacturer names. |
vendor.active | boolean | null | Whether the manufacturer is currently active in the catalogue. |
vendor.websiteUrl | string | null | Official manufacturer website. |
vendor.supportUrl | string | null | Official support website. |
vendor.preferredLocale | string | null | Preferred locale for manufacturer material. |
vendor.countryCode | string | null | Manufacturer country code. |
vendor.publicComment | string | null | Public manufacturer note. |
vendor.linkPolicy.* | boolean | null | Permissions recorded for product links, manuals, images, downloads and hotlinking. |
vendor.copyright.short | string | null | Short copyright statement. |
vendor.copyright.long | string | null | Full copyright statement. |
vendor.copyright.url | string | null | URL for the applicable copyright or media policy. |
Prototype, scale, dimensions and features
| Field path | Type | Description |
|---|---|---|
railway.company | string | null | Railway company or operator. |
railway.country | string | null | Railway country. |
railway.className | string | null | Prototype class or series. |
railway.roadNumber | string | null | Individual running or road number. |
railway.epoch | string | null | Model railway epoch. |
scale.gauge | string | null | Gauge designation, for example N or H0. |
scale.ratio | string | null | Recorded scale ratio. |
dimensions.lengthMm | integer | null | Model length in millimetres. |
dimensions.weightG | integer | null | Model weight in grams. |
dimensions.bufferToFirstAxleFrontMm | integer | null | Front overhang to the first detection axle in millimetres. |
dimensions.bufferToFirstAxleRearMm | integer | null | Rear overhang to the last detection axle in millimetres. |
electrical.powerSystem | string | null | Recorded power system. |
electrical.digitalProtocol | string | null | Recorded digital protocol. |
electrical.decoderDescription | string | null | Free-form decoder description from the model record. |
features.description | string | null | Free-form feature description. |
features.metalBody | boolean | null | Metal body indication. |
features.telex | boolean | null | Remote coupling indication. |
features.smoke | boolean | null | Smoke feature indication. |
features.interiorLight | boolean | null | Interior-light indication. |
Technical locomotive data
| Field path | Type | Description |
|---|---|---|
technical.locomotiveType | string | null | Locomotive or powered-vehicle type. |
technical.wheelArrangement | string | null | Wheel arrangement. |
technical.motor.type | string | null | Motor type. |
technical.motor.partNumber | string | null | Motor spare-part number. |
technical.motor.shieldPartNumber | string | null | Motor-shield part number. |
technical.motor.armaturePartNumber | string | null | Armature part number. |
technical.drive.gearType | string | null | Drive gear type. |
technical.drive.gearTeeth | integer | null | Recorded gear tooth count. |
technical.drive.poweredAxles | integer | null | Number of powered axles. |
technical.drive.tractionTires | integer | null | Number of traction tyres. |
technical.conversion.kit | string | null | Known conversion kit. |
technical.conversion.motor | string | null | Known conversion motor. |
technical.factoryDecoder.manufacturer | string | null | Factory decoder manufacturer. |
technical.factoryDecoder.model | string | null | Factory decoder model. |
technical.factoryDecoder.interface | string | null | Decoder interface or connector. |
technical.sound | boolean | null | Whether sound is recorded for this variant. |
technical.functionCount | integer | null | Recorded number of digital functions. |
technical.maximumSpeedKmh | integer | null | Prototype maximum speed in kilometres per hour. |
technical.minimumRadiusMm | integer | null | Minimum supported model radius in millimetres. |
technical.pantographs | integer | null | Number of pantographs. |
technical.lighting.cab | boolean | null | Cab-lighting indication. |
technical.lighting.interior | boolean | null | Interior-lighting indication. |
technical.smokeGenerator | boolean | null | Smoke-generator indication. |
technical.remarks | string | null | Technical remarks. |
Decoders and functions
| Field path | Type | Description |
|---|---|---|
decoders[] | object[] | Zero or more decoder records for this locomotive variant. |
decoders[].number | integer | null | Decoder sequence number within the model. |
decoders[].manufacturer | string | null | Decoder manufacturer. |
decoders[].model | string | null | Decoder model. |
decoders[].generation | string | null | Decoder generation. |
decoders[].protocol | string | null | Decoder protocol. |
decoders[].programmable | boolean | null | Whether the decoder is recorded as programmable. |
decoders[].defaultAddress | integer | null | Factory default digital address. |
decoders[].mobileStationName | string | null | Name used by a Mobile Station, when known. |
decoders[].centralStationName | string | null | Name used by a Central Station, when known. |
decoders[].functionCount | integer | null | Recorded decoder function count. |
decoders[].functions[] | object[] | Known function descriptions only; each object contains number and description. |
decoders[].remarks | string | null | Decoder remarks. |
decoders[].provenance | object | Source, revision and import timestamp for the decoder record. |
Media, links, pricing and provenance
| Field path | Type | Description |
|---|---|---|
media.images[] | object[] | Image metadata: sourceUrl, availableLocally, localFileName and copyright. |
media.manuals[] | object[] | Manual metadata: sourceUrl, availableLocally and localFileName. |
links.manufacturerProductUrl | string | null | Manufacturer product page. |
links.retailers[] | object[] | Active retailer links with code, productName, productUrl, firstSeenAt and lastSeenAt. |
pricing.msrp | string | null | Recorded manufacturer suggested retail price. Returned as text to preserve source precision. |
pricing.currency | string | null | Currency code for MSRP. |
remarks | string | null | General RMDB remarks. |
provenance.source | string | null | Source of the model record. |
provenance.revision | string | null | Source revision. |
provenance.importedAt | string | null | Source import timestamp. |
6. Missing data and media
- An unknown scalar value is returned as JSON
null. Do not interpret it as zero, false or an empty string. - An absent collection is returned as an empty array, for example
decoders: []. - Only decoder functions with a known description are included in
functions[]. UsefunctionCountseparately when present. availableLocallyandlocalFileNamedescribe RMDB holdings. They do not grant permission to reproduce media and they are not download URLs.- Always respect the returned manufacturer link and copyright policies. A source URL does not automatically grant hotlinking, downloading or republication rights.
7. Errors
Error responses use the same predictable envelope:
{
"success": false,
"error": {
"code": "locomotive_not_found",
"message": "The requested locomotive was not found."
},
"meta": { "apiVersion": "1" }
}
| HTTP status | Meaning | Typical action |
|---|---|---|
400 | Missing, incomplete or invalid identifier. | Supply an ID, both article-collection fields, or all four composite-key fields. |
401 | Missing, invalid, revoked or expired API key. | Check the Bearer header or request a replacement key. |
403 | The key lacks rmdb:locomotive:read. | Request the required scope. |
404 | No locomotive matches the identifier. | Verify the RMDB ID, article combination or complete composite key. |
405 | Unsupported HTTP method. | Use GET. |
409 | The supplied RMDB identifier is ambiguous. | Use the full composite key. |
429 | Rate limit exceeded. | Wait for Retry-After and retry with backoff. |
500 | Unexpected server-side failure. | Retry later; report persistent failures without sending the API key. |
8. Rate limits
The request allowance is assigned per API key. Do not hard-code a universal limit. Every authenticated response exposes the active one-minute window:
X-RateLimit-Limit- Maximum requests in the current one-minute window.
X-RateLimit-Remaining- Requests still available in that window.
X-RateLimit-Reset- Unix timestamp at which the window resets.
Retry-After- Seconds to wait after an HTTP 429 response.
Clients should cache records in their own application where appropriate, avoid repeated reads of an unchanged identifier and use exponential backoff for temporary failures.
9. Compatibility rules
- The major API version is part of the URL. Breaking changes require a new version such as
/api/v2/. - Version 1 may gain new optional fields or new values. Clients must ignore unknown properties and must not reject a response merely because it contains more data.
- Existing field meaning and type will not intentionally change within version 1.
- Use
identity.idas the durable external reference. Catalogue numbers remain strings and must never be normalised numerically. meta.generatedAtis generated in UTC. Other source timestamps should also be treated as UTC unless a future field explicitly carries another offset.
10. Security requirements
- Use HTTPS only and validate the RailKernel TLS certificate.
- Store the API key in a secret store or server-side environment variable.
- Never log the full
Authorizationheader. A short key prefix may be logged for diagnosis. - Never send an API key by email, issue tracker, screenshot or source-code repository.
- Contact RailKernel immediately if a key may have leaked; the key can be revoked without affecting other clients.
- The API is read-only, but returned data and media metadata remain subject to their recorded source and copyright conditions.