Integration guide
For developers connecting a system to Autorekisteri's vehicle-registry lookups.
The formal contract is openapi.json (OpenAPI 3.1); this guide
explains how to use it.
| Production | Test | |
|---|---|---|
| API | https://api.business.autorekisteri.fi |
https://api.business-test.autorekisteri.fi |
| Token URL | https://arek-prod-client.auth.eu-west-1.amazoncognito.com/oauth2/token |
https://arek-test-client.auth.eu-west-1.amazoncognito.com/oauth2/token |
| Data | Traficom production register. Every lookup is billed. | Traficom test system, test plates only |
1. Get credentials
An administrator of your organisation creates an API client in
https://business.autorekisteri.fi → Tili → Rajapinta-asiakkaat and
chooses which lookup tiers it may use. The portal shows the client ID,
client secret, token URL and API URL once. Store the secret in your
secret manager; if it is lost, create a new client and revoke the old one.
Your organisation must first be enabled for API access by Autorekisteri.
Use one client per system, so that one can be revoked without stopping the
others. Revocation takes effect immediately, including for tokens already issued:
every request with the client's tokens then gets 401 CLIENT_REVOKED.
A client can be revoked by an administrator of your organisation, or by Autorekisteri.fi staff (for example when a credential is reported leaked). Your organisation's administrators are e-mailed whenever a client is created or revoked, with the client's name, its tiers, who made the change and when; the mail never contains a client ID or secret. The portal also flags a client nobody has used for 90 days, so that a forgotten credential can be revoked.
2. Get an access token
OAuth 2.0 client credentials, HTTP Basic authentication with the client ID and secret:
curl -s -X POST "$TOKEN_URL" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials'
{ "access_token": "eyJ…", "expires_in": 3600, "token_type": "Bearer" }
Without a scope parameter the token carries every scope your client was
granted. The token is valid for 60 minutes. Cache it and reuse it until
shortly before expires_in; do not request a new token for every lookup.
Scopes, where {api} is the API URL from the table above:
| Scope | Allows |
|---|---|
{api}/lookup.perus |
lookups with tier: perus |
{api}/lookup.laaja |
lookups with tier: laaja |
{api}/lookup.historia |
lookups with tier: historia |
{api}/usage.read |
GET /v1/usage |
Any lookup.* scope also allows reading back your own lookups and their PDFs.
3. Look up a vehicle
curl -s -X POST "$API/v1/lookups" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-4711' \
-d '{"registrationNumber":"ABC-123","vehicleType":"auto","tier":"historia"}'
registrationNumber: a Finnish plate (ABC-123,123-ABC,CD-1234,C-12345). Case and spaces do not matter and a missing hyphen is inserted (abc123isABC-123); the plate in every response is the normalised one. The plate's shape must be one the vehicle type has in the register (L a letter, Å, Ä and Ö included; N a digit):vehicleTypeshapes autoLL-N…LL-NNNN,LLL-N…LLL-NNNperakarryLLL-N…LLL-NNN,LL-NNNNmoottoripyoraLL-N…LL-NNN,NN-LLLmopoNN-LLL,LL-NN,LL-NNNmoottorityokoneN-LL…NNN-LL,N-LLL…NNN-LLLmaastoajoneuvoNNN-LL,NNN-LLLCD-N…CD-NNNNandC-N…C-NNNNNare accepted for every type. Anything else is refused with400 INVALID_REGISTRATION_NUMBERbefore a query is made or billed; when the plate is a valid shape for some other type, the problem also carriesvehicleTypeandexpectedShapes(the list above for that type). Check both the registration number and the vehicle type.vehicleType:auto,moottoripyora,moottorityokone,maastoajoneuvo,perakarry(trailers and caravans) ormopo.tier:perus,laajaorhistoria.
The call is synchronous and usually answers within a few seconds; allow up to 30 seconds before treating it as timed out.
{
"lookupId": "k3q7…",
"registrationNumber": "ABC-123",
"vehicleType": "auto",
"tier": "historia",
"createdAt": "2026-10-16T10:15:00.000Z",
"outcome": "ok",
"traficomErrorCode": null,
"resultExpiresAt": "2026-10-17T10:15:00.000Z",
"report": { "sections": [ … ], "owners": [ … ], "notices": [ … ], "unknownCodes": [] }
}
outcome — check this before reading report
outcome |
Meaning | report |
What to do |
|---|---|---|---|
ok |
Traficom answered | present | use it |
atjError |
Traficom answered with an error code, most often "no such vehicle" | null |
show traficomErrorCode; check the plate and the vehicle type before a new lookup |
empty |
Traficom did not answer | null |
try again later; do not retry in a loop |
All three are HTTP 200, because the query completed and was recorded.
The report
report.sections[] contains { key, title, fields[] }, and each field is
{ key, label, value }. Program against key. title and label are
Finnish display text, and value is a display string ("13.09.2010",
"1 598 cm3") or null when the register has no value. Every section and field
key is listed in openapi.json. Repeated entries have numbered keys
(katsastus.1, katsastus.2, …).
report.owners[] follows the disclosure rules filed with Traficom: a private
person's name or address may be withheld, and is then replaced by fixed
wording. Each owner's group says whether it is a current owner (owner), a
current holder (holder) or an ended relationship (previous, historia only),
the grouping Traficom's own report uses. report.notices[] holds restrictions
and warnings ("Huom. …"). A section that has nothing to show and says so
carries emptyState, the sentence to print in its place.
Retention
The report is kept for 24 hours (resultExpiresAt), then deleted. This is a
contractual maximum, so plan on it. Your own copy is subject to your
agreement with Traficom.
4. Retries and Idempotency-Key
Each lookup is a billed government query. If a request times out you cannot
tell whether it reached Traficom, so send an Idempotency-Key on every
lookup, unique to your business transaction (A-Z a-z 0-9 . _ : -, at most
128 characters). Repeat the request with the same key and body:
| Situation | Answer |
|---|---|
| first request completed | the same 200 again, with header idempotent-replayed: true; no new query |
| first request still running | 409 IDEMPOTENCY_IN_PROGRESS with Retry-After |
| first request was refused before querying (e.g. rate limit) | the lookup now runs |
| same key, different body | 422 IDEMPOTENCY_KEY_REUSED |
first request failed (502 LOOKUP_FAILED, or 503 LOOKUP_THROTTLED) |
502 LOOKUP_FAILED; use a new key to try again |
Keys are remembered for 24 hours.
5. Reading results back, PDF and usage
GET /v1/lookups/{lookupId}returns the same object for 24 hours, then410 RESULT_EXPIRED.GET /v1/lookups/{lookupId}/report.pdfwithAccept: application/pdfreturns the report as a PDF.GET /v1/usage?month=2026-10(scopeusage.read) returns your client's lookups and per-tier counts for invoice reconciliation.
A client can read only lookups it made itself.
6. Errors
Errors are application/problem+json (RFC 9457):
{ "type": "about:blank", "title": "Tier not permitted for this client", "status": 403, "code": "TIER_NOT_PERMITTED" }
Switch on code; title and detail may change. Errors raised by the API
gateway itself (missing or invalid token, missing scope, malformed body,
unknown path, throttling, firewall) carry the same body with
Content-Type: application/json; parse the body, not the header.
| Status | code |
Meaning |
|---|---|---|
| 400 | INVALID_BODY, INVALID_REGISTRATION_NUMBER, INVALID_VEHICLE_TYPE, INVALID_IDEMPOTENCY_KEY, INVALID_MONTH |
fix the request |
| 401 | UNAUTHENTICATED |
missing, expired or invalid token: get a new one |
| 401 | CLIENT_REVOKED |
the client was revoked: ask your administrator for a new one |
| 402 | PAYMENT_REQUIRED |
the organisation's card payment did not go through: see below |
| 403 | SCOPE_MISSING, TIER_NOT_PERMITTED |
the client may not use this tier or route |
| 403 | API_NOT_ENABLED, ORGANISATION_DISABLED, NO_CUSTOMER_NUMBER |
account configuration: contact Autorekisteri |
| 403 | FORBIDDEN |
blocked by the firewall |
| 404 | NOT_FOUND |
no such lookup for this client |
| 409 / 422 | IDEMPOTENCY_IN_PROGRESS / IDEMPOTENCY_KEY_REUSED |
see section 4 |
| 410 | RESULT_EXPIRED |
the 24 hours have passed |
| 429 | RATE_LIMITED |
wait Retry-After seconds |
| 502 | LOOKUP_FAILED, INTERNAL |
did not complete; retry later with a new Idempotency-Key |
| 503 | LOOKUP_THROTTLED |
too many lookups in progress at once; Traficom was not asked and nothing is billed. Wait Retry-After seconds and retry with a new Idempotency-Key |
| 503 | RATE_LIMIT_UNCONFIGURED |
service misconfigured: contact Autorekisteri |
402 Payment Required
Your organisation's monthly lookups are charged to its payment card on the 1st of the following month. If that charge does not go through, the organisation's admins are warned in the business portal, and after a few days' grace (3 by default) new lookups are paused until the month is paid:
{ "type": "https://api.business.autorekisteri.fi/docs#payment-required", "title": "Payment required", "status": 402, "code": "PAYMENT_REQUIRED", "detail": "…" }
Only POST /v1/lookups stops; reading earlier lookups, reports and usage
keeps working. Nothing is sent to Traficom and nothing is billed. An
organisation admin updates the card in the business portal (Laskutus), and
lookups resume as soon as the payment succeeds, which a new card triggers at
once. Retrying the request does not help until then; treat it as a state of
your account, not of the request. The type is this page's anchor, on the
host you call.
7. Rate limits
Lookups are limited per client, per organisation and service-wide over
rolling windows; exceeding a limit answers 429 with Retry-After. Ask
Autorekisteri if your expected volume needs higher limits. Fetching tokens is limited separately by the
token service, which is another reason to cache them.
8. Moving from the old API
The old endpoint (index.php?option=com_autorekisteripayment&view=api) takes
api_pub plus an MD5 auth in the body and answers with Finnish-keyed JSON.
This API replaces all of that:
| Old | New |
|---|---|
api_pub + auth = md5(…) |
Authorization: Bearer <token> from the client-credentials grant |
vehicle_type: "Auto", "Perävaunu" |
vehicleType: "auto", "perakarry" |
query_type |
tier |
regno |
registrationNumber, checked against the vehicle type's plate shapes (section 3) |
errors as HTTP 200/400 with {error, description} |
problem+json with code |
yleiset, omistajat, katsastustiedot, … |
report.sections[].key (see openapi.json) |
The two endpoints run in parallel during an agreed transition period.
API reference
Servers
| Environment | Base URL |
|---|---|
| Production | https://api.business.autorekisteri.fi |
| Test: Traficom test system, test plates only | https://api.business-test.autorekisteri.fi |
Authentication
OAuth 2.0 client credentials. {resourceServer} is the API's own base URL (https://api.business.autorekisteri.fi or the test one). The token URL is shown with your client credentials in the portal.
| Scope | Allows |
|---|---|
{resourceServer}/lookup.perus | Look up with tier perus; read back own lookups |
{resourceServer}/lookup.laaja | Look up with tier laaja; read back own lookups |
{resourceServer}/lookup.historia | Look up with tier historia; read back own lookups |
{resourceServer}/usage.read | Read own monthly usage |
POST /v1/lookups
Look up a vehicle
Synchronous. Answers 200 for every completed query, including when Traficom has no data (outcome: empty) or answers with an error code (outcome: atjError); neither should be retried automatically. Send Idempotency-Key to make a retry after a timeout free of charge.
Scope (any one of): {resourceServer}/lookup.perus {resourceServer}/lookup.laaja {resourceServer}/lookup.historia
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
Idempotency-Key | header | string | Your own identifier for this request, 1-128 characters of A-Z a-z 0-9 . _ : -. A repeat within 24 hours returns the first answer without a second query; reusing it for a different request is 422.Pattern: |
Request body
application/json: LookupRequest
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The query completed. idempotent-replayed: true marks an answer from an earlier request with the same key. | application/json Lookup |
400 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
401 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
402 | PAYMENT_REQUIRED: the organisation's card payment for a past month did not go through and the grace period has passed, so new lookups are paused. Traficom was not asked and nothing is billed. An organisation admin updates the card in the business portal; lookups resume as soon as the payment succeeds. Earlier lookups and reports stay readable. The problem's type is https://api.business.autorekisteri.fi/docs#payment-required (the test tier's own host on test). | application/problem+json Problem |
403 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
409 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
410 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
422 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
429 | A per-client, per-organisation or service-wide limit. Wait Retry-After seconds. | application/problem+json Problem |
502 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
503 | LOOKUP_THROTTLED: too many lookups are in progress at once, so this one was not started and Traficom was not asked. Nothing is billed. Wait Retry-After seconds and retry with a new Idempotency-Key. RATE_LIMIT_UNCONFIGURED: the service is misconfigured; no Retry-After, contact Autorekisteri. | application/problem+json Problem |
GET /v1/lookups/{lookupId}
Read back one of your own lookups
The report is kept for 24 hours (a contractual maximum), then 410 RESULT_EXPIRED.
Scope (any one of): {resourceServer}/lookup.perus {resourceServer}/lookup.laaja {resourceServer}/lookup.historia
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
lookupId required | path | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The lookup | application/json Lookup |
401 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
403 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
404 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
410 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
GET /v1/lookups/{lookupId}/report.pdf
The report as a PDF
Send Accept: application/pdf. Available for 24 hours, like the report.
Scope (any one of): {resourceServer}/lookup.perus {resourceServer}/lookup.laaja {resourceServer}/lookup.historia
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
lookupId required | path | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The PDF | application/pdf string (binary) |
401 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
403 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
404 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
410 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
GET /v1/usage
Your client's lookups and counts for a month
Scope (any one of): {resourceServer}/usage.read
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
month | query | string | YYYY-MM, UTC. Defaults to the current month.Pattern: |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Usage | application/json Usage |
400 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
401 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
403 | RFC 9457 problem details. Switch on code; title and detail may be reworded. Errors raised by the gateway itself carry the same body as application/json. | application/problem+json Problemapplication/json Problem |
Schemas
LookupRequest
| Field | Type | Notes |
|---|---|---|
registrationNumber required | string | Finnish registration number. Case and spaces do not matter and a missing hyphen is inserted. Its shape must be one the vehicle type has in the register (INTEGRATION.md section 3); otherwise 400 INVALID_REGISTRATION_NUMBER. Example: |
vehicleType required | string | One of: |
tier required | string | One of: |
Lookup
| Field | Type | Notes |
|---|---|---|
lookupId required | string | |
registrationNumber required | string | |
vehicleType required | string | One of: |
tier required | string | One of: |
createdAt required | string (date-time) | |
outcome required | string | ok: report included. empty: Traficom did not answer (no report). atjError: Traficom answered with traficomErrorCode, most often an unknown vehicle.One of: |
traficomErrorCode required | string | null | |
resultExpiresAt required | string | null (date-time) | |
report required | Report | null |
Report
The same structure the portal and the PDF render. Keys are stable identifiers; title and label are the Finnish display text and value a display string (dates as dd.mm.yyyy, units appended). Owner data follows the disclosure rules filed with Traficom.
| Field | Type | Notes |
|---|---|---|
sections required | array of ReportSection | |
owners required | array of Owner | |
notices required | array of string | |
vehicleStatus | array of object | Ajoneuvon tila and, for a vehicle out of traffic or off the register, Rekisterikilvet palautettu, drawn above the report. Absent on a lookup stored before 2026-10-19. |
vehicleStatus[].label required | string | |
vehicleStatus[].value required | string | |
transferNotices | array of object | The Luovutusilmoitustiedot block: each notice of a sale or handover the register holds, as its kind and date only. The buyer is never included. Absent on a lookup stored before 2026-10-19. |
transferNotices[].date required | string | null | dd.mm.yyyy, or null when the register gave none. |
transferNotices[].kind required | string | null | |
unknownCodes required | array of object | |
unknownCodes[].codeSet | string | |
unknownCodes[].code | string |
ReportSection
| Field | Type | Notes |
|---|---|---|
key required | string | 24 allowed values
|
title required | string | |
fields required | array of ReportField | |
emptyState | string | Present only on a section that has nothing to show and states so, as Traficom's own report does (Ei aiempia omistajatietoja., Ei liikennekäytöstäpoistoja.). |
ReportField
| Field | Type | Notes |
|---|---|---|
key required | string | string | A fixed field key, or a repeated entry <base>.<n> (1-based).70 allowed values
|
label required | string | |
value required | string | null |
Owner
| Field | Type | Notes |
|---|---|---|
group | string | A current owner, a current holder, or an ended relationship (historia and kaikki only). Absent on a lookup stored before 2026-10-19. One of: |
title required | string | |
name required | string | |
address required | string | null | |
deathEstate required | string | null |
Usage
| Field | Type | Notes |
|---|---|---|
month | string | |
total | integer | |
totalsByTier | map of integer | |
byDayAndTier | array of object | |
byDayAndTier[].day | string | |
byDayAndTier[].tier | string | |
byDayAndTier[].count | integer | |
lookups | array of object | |
lookups[].lookupId | string | |
lookups[].registrationNumber | string | |
lookups[].vehicleType | string | |
lookups[].tier | string | |
lookups[].createdAt | string (date-time) | |
lookups[].outcome | string | One of: |
Problem
| Field | Type | Notes |
|---|---|---|
type required | string | about:blank, except PAYMENT_REQUIRED, whose type is this API's /docs#payment-required.Example: |
title required | string | |
status required | integer | |
code required | string | 24 allowed values
|
detail | string | |
vehicleType | string | On INVALID_REGISTRATION_NUMBER for a shape the vehicle type does not have: the type sent. |
expectedShapes | array of string | On INVALID_REGISTRATION_NUMBER for a shape the vehicle type does not have: the shapes it has, L a letter and N a digit. CD- and C- plates are accepted for every type. |
retryAfterSeconds | integer | On RATE_LIMITED and LOOKUP_THROTTLED: the same value as the Retry-After header. |