Contents
API documentation
With the Robinsontjek API you check people against the Danish Robinson list straight from your own system, before you call or send addressed mail. You send a name and an address and get back exactly what we found for every person.
Every call goes to https://api.robinsontjek.dk and uses JSON. Error messages and match.label come in Danish or English following the Accept-Language header.
What you get for every person
match.level: what we found, from 0 (nobody on the list at the address) to 5 (same name and address). See the levels.match.code,match.labelandmatch.on: the same as a code and a text, and what matched.list_version: which edition of the Robinson list we checked against.
You decide which level you go by. For integrations that only want a yes or no, there is also status, computed from your chosen level (stop_level).
https://api.robinsontjek.dkcurl https://api.robinsontjek.dk/v2/list-versions/current \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY"const res = await fetch("https://api.robinsontjek.dk/v2/list-versions/current", {
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.robinsontjek.dk/v2/list-versions/current",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/list-versions/current")
req = Net::HTTP::Get.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/list-versions/current");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
}Authentication
Every call needs your API key in the Authorization header:
Authorization: Bearer rl_live_…
You create keys in the app under Settings. Keep the key secret and use it only from your own server, never in a browser or an app.
Test and live
rl_live_…checks against the real Robinson list and counts towards your usage.rl_test_…returns fixed, made-up answers, touches no real list and costs nothing. Answers to a test key carry the headerX-Robinsontjek-Mode: test.
With a test key, the name decides the answer:
| The name contains | Answer |
|---|---|
Test Blokeret |
level 5, same name and address |
Test Tidligere |
level 4, previous address |
Test Manuel |
level 1, someone else in the household |
| anything else with an address we can read | level 0 |
Companies with CVR 99999999 are protected from marketing, and the phone number 99999999 returns a person who is on the list (level 5).
Authorization: Bearer rl_test_4f9c1a7e2b8d6035HTTP/1.1 401 Unauthorized
{
"error": {
"code": "unauthorized",
"message": "The API key is invalid or revoked.",
"request_id": "req_4a0f6c2e9b1d"
}
}Match levels
Every person gets a level from 0 to 5 in match.level. The level says what we found on the Robinson list at the person's address. The higher the level, the more closely what we found resembles the person.
| Level | match.code |
What we found |
|---|---|---|
| 0 | none |
Nobody on the list at the address |
| 1 | household |
Someone else in the household is on the list |
| 2 | surname |
Same surname at the address |
| 3 | given_name |
Same first name at the address |
| 4 | name_building |
Same name, different flat |
| 5 | name_address |
Same name and address |
If we can't read the address, match.level is null and the reason is address_unreadable. match.previous_address is true when the person is on the list at a previous address. It counts as at least level 4. match.on shows which parts matched.
Your level: status and stop_level
You choose the level you go by, once in the app's settings or per call with stop_level on POST /v2/checks. If you don't choose, we use level 1, the most cautious. The level comes back in every answer as stop_level, and from it we compute status for integrations that want a ready answer:
status |
When |
|---|---|
allowed |
match.level is below your level. |
blocked |
match.level is at your level or above. |
manual |
The address can't be read (match.level is null). Never allowed. |
status is a convenience. If you'd rather decide row by row, use match.level.
Reasons
reasons are codes you can translate and show yourself:
| Code | Means |
|---|---|
name_and_address |
The same name and address are on the list. |
name_and_unit |
The same name in the same flat is on the list. |
household |
Someone else at the address is on the list. |
partial_name |
A similar name is on the list at the address. |
previous_address |
The person is on the list at an address they have moved from. |
address_unreadable |
We could not read the address. Correct it and check again. |
address_not_found |
The address doesn't exist in Denmark as written. |
no_name |
There is no name. |
company_protected |
The company has opted out of marketing in CVR. |
phone_unlisted |
The number is unlisted. |
phone_not_found |
We could not find who has the number. |
list_not_ready |
The Robinson list is being updated. Check again shortly. |
{
"match": {
"level": 5,
"code": "name_address",
"label": "Same name and address",
"on": ["first_name", "last_name", "address", "unit"],
"previous_address": false
},
"status": "blocked",
"stop_level": 1,
"reasons": ["name_and_address"]
}{
"match": {
"level": null,
"code": null,
"label": null,
"on": [],
"previous_address": false
},
"status": "manual",
"stop_level": 1,
"reasons": ["address_unreadable"],
"address": null
}Errors
An error always has the same shape: an error object with a fixed code your code can act on, and a message you can show to a person. Include request_id if you write to us about an error.
| HTTP | code |
What to do |
|---|---|---|
| 400 | invalid_request |
The body isn't valid JSON. Fix it. |
| 401 | unauthorized |
The key is missing, wrong or revoked. |
| 402 | payment_required |
The account has no active plan. Write to us. |
| 403 | phone_not_enabled |
Phone lookups are an add-on and aren't enabled for you. |
| 403 | company_checks_not_enabled |
Company checks are an add-on and aren't enabled for you. |
| 404 | not_found |
It doesn't exist, or it belongs to another account. |
| 409 | idempotency_in_progress |
A call with the same Idempotency-Key is running. Try again shortly. |
| 422 | invalid_request |
Something in the request is wrong. details says what. |
| 422 | idempotency_conflict |
The Idempotency-Key was used before for a different call. |
| 429 | rate_limited |
Too many calls. Wait the number of seconds in Retry-After. |
| 503 | list_not_ready |
The Robinson list is being updated. Try again shortly. |
| 500 | internal_error |
Something failed on our side. Try again. |
{
"error": {
"code": "invalid_request",
"message": "Person 3 has no address.",
"details": {
"field": "people",
"index": 3
},
"request_id": "req_7f2c9a41e0b3"
}
}Rate limits and idempotency
Each key can make 600 calls a minute. Go over, and we answer 429 with a Retry-After header that says how many seconds to wait.
If you have many people, send them together: up to 1,000 in one call to POST /v2/checks, or up to 1,000,000 as a job with POST /v2/check-jobs.
Idempotency-Key
Send the Idempotency-Key header with a unique value, such as a UUID, on POST /v2/checks and POST /v2/check-jobs. If the connection fails and you send the same call again with the same key, you get the first answer back instead of the list being checked a second time.
- A key is valid for 24 hours.
- A repeated answer carries the header
Idempotent-Replayed: true. - The same key with different content returns
422 idempotency_conflict.
curl -X POST https://api.robinsontjek.dk/v2/checks \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: b3f1c2de-5a7e-4c41-9d0a-2f6e8a91c7d4" \
-d '{
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
}
]
}'const res = await fetch("https://api.robinsontjek.dk/v2/checks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
people: [
{
id: "lead-1042",
name: "Anne Marie Jensen",
address: "Stationsalleen 48, 1. b, 2770 Kastrup"
}
]
}),
});
const data = await res.json();import os
import uuid
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/checks",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
}
]
},
)
data = res.json()require "net/http"
require "json"
require "securerandom"
uri = URI("https://api.robinsontjek.dk/v2/checks")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
"Idempotency-Key" => SecureRandom.uuid,
})
req.body = JSON.generate({
"people" => [
{
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
}
]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/checks");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
"people" => [
[
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
]
]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);HTTP/1.1 429 Too Many Requests
Retry-After: 12
RateLimit-Limit: 600
RateLimit-Remaining: 0
RateLimit-Reset: 12
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Wait a moment and try again.",
"request_id": "req_1d8e4b07c2a9"
}
}Webhooks
Robinsontjek sends a POST to your URL when something happens, so your system doesn't have to keep asking. Create the endpoint in the app under Settings. The signing secret is shown once, when the endpoint is created.
| Event | Happens when |
|---|---|
check_job.completed |
A job is done. Fetch the results. |
check_job.failed |
A job failed. data.error says why. |
saved_list.changed |
A saved list has changes after a new check. |
list_version.activated |
A new edition of the Robinson list has gone live. |
A webhook never carries personal data, only ids and counts. id is unique per delivery, so you can ignore a delivery you have already handled.
Verify the signature
Every delivery has the header Robinsontjek-Signature: t=<timestamp>,v1=<signature>. The signature is the hex HMAC-SHA256, with your secret, of the text <timestamp>.<raw body>.
- Read the header and take
tandv1. - Compute HMAC-SHA256 of
t + "." + raw bodywith your secret. - Compare with
v1using a constant-time comparison. - Reject deliveries where
tis more than 5 minutes old.
Use the raw body, not JSON that was parsed and written again.
Replies and retries
Reply with a 2xx status within 5 seconds. Anything else counts as a failure, and we retry up to 8 times over about a day.
POST /hooks/robinsontjek HTTP/1.1
Content-Type: application/json
Robinsontjek-Signature: t=1792057351,v1=5d3c0b8e…
{
"id": "6f1c0c1e-93b4-4d2a-8e57-2c1f0a9b7d34",
"type": "check_job.completed",
"created_at": "2026-10-14T09:42:31Z",
"data": {
"check_job_id": "5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58",
"status": "done",
"source": "api",
"external_ref": "kampagne-oktober",
"row_count": 1840,
"allowed_count": 1512,
"blocked_count": 312,
"manual_count": 16,
"finished_at": "2026-10-14T09:42:30Z"
}
}import crypto from "node:crypto";
// rawBody: the request body exactly as it arrived (a Buffer or string)
function verify(header, rawBody, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && expected.length === parts.v1.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}import hashlib
import hmac
import time
def verify(header, raw_body: bytes, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts["t"])) < 300
return fresh and hmac.compare_digest(expected, parts["v1"])require "openssl"
def verify(header, raw_body, secret)
parts = header.split(",").to_h { |p| p.split("=", 2) }
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{parts["t"]}.#{raw_body}")
fresh = (Time.now.to_i - parts["t"].to_i).abs < 300
fresh && Rack::Utils.secure_compare(expected, parts["v1"])
end<?php
function verify(string $header, string $rawBody, string $secret): bool {
parse_str(str_replace(",", "&", $header), $parts);
$expected = hash_hmac("sha256", $parts["t"] . "." . $rawBody, $secret);
$fresh = abs(time() - (int) $parts["t"]) < 300;
return $fresh && hash_equals($expected, $parts["v1"]);
}Check people
POST/v2/checks
Checks up to 1,000 people and answers straight away, in the order you sent them. Give each person a name and an address. The address can be one field or split into street, house number, floor, door, postcode and town.
Headers
Idempotency-KeystringA unique value, such as a UUID. Send the same call again with the same key within 24 hours and you get the first answer back. See Rate limits and idempotency.
Body
peoplearray of objectsrequiredThe people to check. At least 1 and at most 1,000.
Show 12 fields
idstringYour own reference, such as the id in your CRM. It comes back in the response.
namestringFull name. Or use
first_nameandlast_name.first_namestringFirst name (and any middle names).
last_namestringSurname.
addressstringThe whole address in one field, such as
Stationsalleen 48, 1. b, 2770 Kastrup. Or use the fields below.streetstringStreet name.
house_numberstringHouse number, with a letter if there is one, such as
12B.floorstringFloor, such as
st(ground),1orkl(basement).doorstringDoor, such as
tv(left),th(right),mf(middle) orb.postal_codestringPostcode, four digits.
citystringTown.
phonestringPhone number, if you have it. Only used when phone lookups are switched on.
stop_levelinteger · 1–5Go by a different level than your organisation's for this call only. It changes
status, not what we found. See Match levels.
Response200
One result per person, in the same order.
list_versionobjectrequiredThe edition of the Robinson list that was checked against.
Show 4 fields
idstring (uuid)requiredThe list version's id.
sourcestringcpris the Robinson list.customeris your own list of people who asked not to be contacted.published_onstring (date)requiredThe date CPR published the list.
activated_atstring (date-time)When we put the list to use.
resultsarray of objectsrequiredOne result per person, in the same order as the request.
Show 7 fields
idstring · can be nullYour reference from the request.
matchobjectWhat we found on the Robinson list at the person's address.
Show 5 fields
levelinteger · 0–5 · can be nullrequiredLevel from 0 to 5.
nullwhen the address can't be read.codestring · can be nullrequiredThe level as a code:
none,household,surname,given_name,name_buildingorname_address.labelstring · can be nullThe level in words, in Danish or English following
Accept-Language.onarray of stringsThe parts that matched:
first_name,last_name,addressandunit.previous_addressbooleantruewhen the person is on the list at a previous address. It counts as at least level 4.
statusobjectrequiredFor integrations, computed from your chosen level:
allowed(below the level),blocked(at the level or above) ormanual(the address can't be read).stop_levelinteger · 0–5The level you go by, which
statusis computed from. 1 by default, the most cautious.reasonsarray of objectsrequiredCodes that explain the answer. See Reasons.
addressobject · can be nullHow we read the address.
nullif it could not be read.Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
phone_lookupobject · can be nullPhone numbers only: what the directory lookup returned.
Show 3 fields
outcomestringfound,unlistedornot_found.namestringThe name behind the number.
addressstringThe address behind the number.
curl -X POST https://api.robinsontjek.dk/v2/checks \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: b3f1c2de-5a7e-4c41-9d0a-2f6e8a91c7d4" \
-d '{
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id": "lead-1043",
"first_name": "Jens",
"last_name": "Hansen",
"address": "Vestergade 12 8000 Aarhus"
}
]
}'const res = await fetch("https://api.robinsontjek.dk/v2/checks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
people: [
{
id: "lead-1042",
name: "Anne Marie Jensen",
address: "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
id: "lead-1043",
first_name: "Jens",
last_name: "Hansen",
address: "Vestergade 12 8000 Aarhus"
}
]
}),
});
const data = await res.json();import os
import uuid
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/checks",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id": "lead-1043",
"first_name": "Jens",
"last_name": "Hansen",
"address": "Vestergade 12 8000 Aarhus"
}
]
},
)
data = res.json()require "net/http"
require "json"
require "securerandom"
uri = URI("https://api.robinsontjek.dk/v2/checks")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
"Idempotency-Key" => SecureRandom.uuid,
})
req.body = JSON.generate({
"people" => [
{
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id" => "lead-1043",
"first_name" => "Jens",
"last_name" => "Hansen",
"address" => "Vestergade 12 8000 Aarhus"
}
]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/checks");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
"people" => [
[
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
],
[
"id" => "lead-1043",
"first_name" => "Jens",
"last_name" => "Hansen",
"address" => "Vestergade 12 8000 Aarhus"
]
]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"list_version": {
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
},
"results": [
{
"id": "lead-1042",
"match": {
"level": 5,
"code": "name_address",
"label": "Same name and address",
"on": ["first_name", "last_name", "address", "unit"],
"previous_address": false
},
"status": "blocked",
"stop_level": 1,
"reasons": ["name_and_address"],
"address": {
"input": "Stationsalleen 48, 1. b, 2770 Kastrup",
"ok": true,
"street": "Stationsalleen",
"house_number": "48",
"floor": "1",
"door": "b",
"postal_code": "2770",
"city": "Kastrup",
"formatted": "Stationsalleen 48, 1. b, 2770 Kastrup",
"confidence": "exact"
}
},
{
"id": "lead-1043",
"match": {
"level": 0,
"code": "none",
"label": "Nobody on the list at the address",
"on": [],
"previous_address": false
},
"status": "allowed",
"stop_level": 1,
"reasons": [],
"address": {
"input": "Vestergade 12 8000 Aarhus",
"ok": true,
"street": "Vestergade",
"house_number": "12",
"floor": null,
"door": null,
"postal_code": "8000",
"city": "Aarhus C",
"formatted": "Vestergade 12, 8000 Aarhus C",
"confidence": "corrected"
}
}
]
}Create a job
POST/v2/check-jobs
For large lists: up to 1,000,000 people as JSON, or an Excel or CSV file. The job runs in the background. Follow it with »Get a job«, or be told at callback_url or through the check_job.completed webhook.
Headers
Idempotency-KeystringA unique value, such as a UUID. Send the same call again with the same key within 24 hours and you get the first answer back. See Rate limits and idempotency.
Body
peoplearray of objectsrequiredThe people to check. At most 1,000,000.
Show 12 fields
idstringYour own reference, such as the id in your CRM. It comes back in the response.
namestringFull name. Or use
first_nameandlast_name.first_namestringFirst name (and any middle names).
last_namestringSurname.
addressstringThe whole address in one field, such as
Stationsalleen 48, 1. b, 2770 Kastrup. Or use the fields below.streetstringStreet name.
house_numberstringHouse number, with a letter if there is one, such as
12B.floorstringFloor, such as
st(ground),1orkl(basement).doorstringDoor, such as
tv(left),th(right),mf(middle) orb.postal_codestringPostcode, four digits.
citystringTown.
phonestringPhone number, if you have it. Only used when phone lookups are switched on.
callback_urlstring (uri)We send a
POSThere when the job is done.external_refstringYour own reference for the job, such as the campaign name. It comes back in responses and webhooks.
Or send a file (multipart/form-data)
filestring (binary)requiredAn Excel file (xlsx or xls) or a CSV file.
column_mappingobjectWhich column is which. Leave it out and we find the columns ourselves.
Show 15 fields
header_rowintegerThe row with the headings. The first row is 0.
nameinteger | stringThe column's number (the first is 0) or its heading.
first_nameinteger | stringThe column's number (the first is 0) or its heading.
last_nameinteger | stringThe column's number (the first is 0) or its heading.
addressinteger | stringThe column's number (the first is 0) or its heading.
streetinteger | stringThe column's number (the first is 0) or its heading.
house_numberinteger | stringThe column's number (the first is 0) or its heading.
floorinteger | stringThe column's number (the first is 0) or its heading.
doorinteger | stringThe column's number (the first is 0) or its heading.
postal_codeinteger | stringThe column's number (the first is 0) or its heading.
cityinteger | stringThe column's number (the first is 0) or its heading.
phoneinteger | stringThe column's number (the first is 0) or its heading.
cvrinteger | stringThe column's number (the first is 0) or its heading.
company_nameinteger | stringThe column's number (the first is 0) or its heading.
external_idinteger | stringThe column's number (the first is 0) or its heading.
callback_urlstring (uri)We send a
POSThere when the job is done.external_refstringYour own reference for the job, such as the campaign name. It comes back in responses and webhooks.
Response202
The job is accepted and queued.
idstring (uuid)requiredThe job's id.
sourcestringrequiredWhere the list came from, such as
api,uploadoremail.One of:
upload,email,sftp,api,monitor,connectorstatusstringrequiredqueued,running,doneorfailed. A file waiting for you in the app isdraftorawaiting_payment.One of:
draft,awaiting_payment,queued,running,done,failed,deletedfilenamestring · can be nullThe file's name, when the list came as a file.
file_formatstring · can be nullxlsx,xlsorcsv.row_countintegerRows in the list.
processed_countintegerRows checked so far.
allowed_countintegerHow many are below your level.
blocked_countintegerHow many are at your level or above.
manual_countintegerHow many addresses could not be read.
level_countsobjectShow 7 fields
0integerrequired1integerrequired2integerrequired3integerrequired4integerrequired5integerrequiredunreadintegerrequired
columnsarray of stringsThe file's column headings, while the job is a draft.
column_mappingobjectWhich columns were used for what.
Show 15 fields
header_rowintegerThe row with the headings. The first row is 0.
nameinteger | stringThe column's number (the first is 0) or its heading.
first_nameinteger | stringThe column's number (the first is 0) or its heading.
last_nameinteger | stringThe column's number (the first is 0) or its heading.
addressinteger | stringThe column's number (the first is 0) or its heading.
streetinteger | stringThe column's number (the first is 0) or its heading.
house_numberinteger | stringThe column's number (the first is 0) or its heading.
floorinteger | stringThe column's number (the first is 0) or its heading.
doorinteger | stringThe column's number (the first is 0) or its heading.
postal_codeinteger | stringThe column's number (the first is 0) or its heading.
cityinteger | stringThe column's number (the first is 0) or its heading.
phoneinteger | stringThe column's number (the first is 0) or its heading.
cvrinteger | stringThe column's number (the first is 0) or its heading.
company_nameinteger | stringThe column's number (the first is 0) or its heading.
external_idinteger | stringThe column's number (the first is 0) or its heading.
optionsobjectWhat the job checks.
Show 5 fields
check_peoplebooleanCheck people against the Robinson list.
check_companiesbooleanCheck companies against the CVR register's marketing protection.
check_phonesbooleanLook up phone numbers.
use_private_listbooleanAlso check against your own list of people who asked not to be contacted.
stop_levelinteger · 1–5
previewarray of objectsThe first 20 rows with their results.
Show 2 fields
cellsarray of stringsresultobjectShow 7 fields
idstring · can be nullYour reference from the request.
matchobjectWhat we found on the Robinson list at the person's address.
Show 5 fields
levelinteger · 0–5 · can be nullrequiredLevel from 0 to 5.
nullwhen the address can't be read.codestring · can be nullrequiredThe level as a code:
none,household,surname,given_name,name_buildingorname_address.labelstring · can be nullThe level in words, in Danish or English following
Accept-Language.onarray of stringsThe parts that matched:
first_name,last_name,addressandunit.previous_addressbooleantruewhen the person is on the list at a previous address. It counts as at least level 4.
statusobjectrequiredFor integrations, computed from your chosen level:
allowed(below the level),blocked(at the level or above) ormanual(the address can't be read).stop_levelinteger · 0–5The level you go by, which
statusis computed from. 1 by default, the most cautious.reasonsarray of objectsrequiredCodes that explain the answer. See Reasons.
addressobject · can be nullHow we read the address.
nullif it could not be read.Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
phone_lookupobject · can be nullPhone numbers only: what the directory lookup returned.
Show 3 fields
outcomestringfound,unlistedornot_found.namestringThe name behind the number.
addressstringThe address behind the number.
priceobject · can be nullWhat the job costs.
nullon the annual plan.Show 3 fields
productstringThe price the job falls under.
One of:
list_small,list_large,customamount_centsintegerThe amount in øre (1/100 krone), excluding VAT.
currencystringCurrency, such as
DKK.
list_versionobjectThe edition of the Robinson list that was checked against.
Show 4 fields
idstring (uuid)requiredThe list version's id.
sourcestringcpris the Robinson list.customeris your own list of people who asked not to be contacted.published_onstring (date)requiredThe date CPR published the list.
activated_atstring (date-time)When we put the list to use.
certificate_tokenstring · can be nullThe check receipt, once the job is done. It can be seen at
robinsontjek.dk/en/receipt/<token>.errorstring · can be nullWhy the job failed.
delete_afterstring (date-time) · can be nullWhen the file and the rows are deleted.
created_atstring (date-time)When the job was created.
finished_atstring (date-time) · can be nullWhen the job finished.
curl -X POST https://api.robinsontjek.dk/v2/check-jobs \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: b3f1c2de-5a7e-4c41-9d0a-2f6e8a91c7d4" \
-d '{
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id": "lead-1043",
"first_name": "Jens",
"last_name": "Hansen",
"address": "Vestergade 12 8000 Aarhus"
}
],
"callback_url": "https://example.com/hooks/robinsontjek",
"external_ref": "kampagne-oktober"
}'const res = await fetch("https://api.robinsontjek.dk/v2/check-jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
people: [
{
id: "lead-1042",
name: "Anne Marie Jensen",
address: "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
id: "lead-1043",
first_name: "Jens",
last_name: "Hansen",
address: "Vestergade 12 8000 Aarhus"
}
],
callback_url: "https://example.com/hooks/robinsontjek",
external_ref: "kampagne-oktober"
}),
});
const data = await res.json();import os
import uuid
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/check-jobs",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"people": [
{
"id": "lead-1042",
"name": "Anne Marie Jensen",
"address": "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id": "lead-1043",
"first_name": "Jens",
"last_name": "Hansen",
"address": "Vestergade 12 8000 Aarhus"
}
],
"callback_url": "https://example.com/hooks/robinsontjek",
"external_ref": "kampagne-oktober"
},
)
data = res.json()require "net/http"
require "json"
require "securerandom"
uri = URI("https://api.robinsontjek.dk/v2/check-jobs")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
"Idempotency-Key" => SecureRandom.uuid,
})
req.body = JSON.generate({
"people" => [
{
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
},
{
"id" => "lead-1043",
"first_name" => "Jens",
"last_name" => "Hansen",
"address" => "Vestergade 12 8000 Aarhus"
}
],
"callback_url" => "https://example.com/hooks/robinsontjek",
"external_ref" => "kampagne-oktober"
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/check-jobs");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
"people" => [
[
"id" => "lead-1042",
"name" => "Anne Marie Jensen",
"address" => "Stationsalleen 48, 1. b, 2770 Kastrup"
],
[
"id" => "lead-1043",
"first_name" => "Jens",
"last_name" => "Hansen",
"address" => "Vestergade 12 8000 Aarhus"
]
],
"callback_url" => "https://example.com/hooks/robinsontjek",
"external_ref" => "kampagne-oktober"
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"id": "5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58",
"source": "api",
"status": "queued",
"filename": null,
"file_format": null,
"row_count": 2,
"processed_count": 0,
"allowed_count": 0,
"blocked_count": 0,
"manual_count": 0,
"list_version": {
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
},
"certificate_token": null,
"created_at": "2026-10-14T09:41:58Z",
"finished_at": null
}Get a job
GET/v2/check-jobs/{id}
Shows how far the job has come, and the counts once it is done. certificate_token points to the check receipt, which can be seen at robinsontjek.dk/en/receipt/<token>.
Path
idstring (uuid)requiredThe job's id.
Response200
The job as it stands.
idstring (uuid)requiredThe job's id.
sourcestringrequiredWhere the list came from, such as
api,uploadoremail.One of:
upload,email,sftp,api,monitor,connectorstatusstringrequiredqueued,running,doneorfailed. A file waiting for you in the app isdraftorawaiting_payment.One of:
draft,awaiting_payment,queued,running,done,failed,deletedfilenamestring · can be nullThe file's name, when the list came as a file.
file_formatstring · can be nullxlsx,xlsorcsv.row_countintegerRows in the list.
processed_countintegerRows checked so far.
allowed_countintegerHow many are below your level.
blocked_countintegerHow many are at your level or above.
manual_countintegerHow many addresses could not be read.
level_countsobjectShow 7 fields
0integerrequired1integerrequired2integerrequired3integerrequired4integerrequired5integerrequiredunreadintegerrequired
columnsarray of stringsThe file's column headings, while the job is a draft.
column_mappingobjectWhich columns were used for what.
Show 15 fields
header_rowintegerThe row with the headings. The first row is 0.
nameinteger | stringThe column's number (the first is 0) or its heading.
first_nameinteger | stringThe column's number (the first is 0) or its heading.
last_nameinteger | stringThe column's number (the first is 0) or its heading.
addressinteger | stringThe column's number (the first is 0) or its heading.
streetinteger | stringThe column's number (the first is 0) or its heading.
house_numberinteger | stringThe column's number (the first is 0) or its heading.
floorinteger | stringThe column's number (the first is 0) or its heading.
doorinteger | stringThe column's number (the first is 0) or its heading.
postal_codeinteger | stringThe column's number (the first is 0) or its heading.
cityinteger | stringThe column's number (the first is 0) or its heading.
phoneinteger | stringThe column's number (the first is 0) or its heading.
cvrinteger | stringThe column's number (the first is 0) or its heading.
company_nameinteger | stringThe column's number (the first is 0) or its heading.
external_idinteger | stringThe column's number (the first is 0) or its heading.
optionsobjectWhat the job checks.
Show 5 fields
check_peoplebooleanCheck people against the Robinson list.
check_companiesbooleanCheck companies against the CVR register's marketing protection.
check_phonesbooleanLook up phone numbers.
use_private_listbooleanAlso check against your own list of people who asked not to be contacted.
stop_levelinteger · 1–5
previewarray of objectsThe first 20 rows with their results.
Show 2 fields
cellsarray of stringsresultobjectShow 7 fields
idstring · can be nullYour reference from the request.
matchobjectWhat we found on the Robinson list at the person's address.
Show 5 fields
levelinteger · 0–5 · can be nullrequiredLevel from 0 to 5.
nullwhen the address can't be read.codestring · can be nullrequiredThe level as a code:
none,household,surname,given_name,name_buildingorname_address.labelstring · can be nullThe level in words, in Danish or English following
Accept-Language.onarray of stringsThe parts that matched:
first_name,last_name,addressandunit.previous_addressbooleantruewhen the person is on the list at a previous address. It counts as at least level 4.
statusobjectrequiredFor integrations, computed from your chosen level:
allowed(below the level),blocked(at the level or above) ormanual(the address can't be read).stop_levelinteger · 0–5The level you go by, which
statusis computed from. 1 by default, the most cautious.reasonsarray of objectsrequiredCodes that explain the answer. See Reasons.
addressobject · can be nullHow we read the address.
nullif it could not be read.Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
phone_lookupobject · can be nullPhone numbers only: what the directory lookup returned.
Show 3 fields
outcomestringfound,unlistedornot_found.namestringThe name behind the number.
addressstringThe address behind the number.
priceobject · can be nullWhat the job costs.
nullon the annual plan.Show 3 fields
productstringThe price the job falls under.
One of:
list_small,list_large,customamount_centsintegerThe amount in øre (1/100 krone), excluding VAT.
currencystringCurrency, such as
DKK.
list_versionobjectThe edition of the Robinson list that was checked against.
Show 4 fields
idstring (uuid)requiredThe list version's id.
sourcestringcpris the Robinson list.customeris your own list of people who asked not to be contacted.published_onstring (date)requiredThe date CPR published the list.
activated_atstring (date-time)When we put the list to use.
certificate_tokenstring · can be nullThe check receipt, once the job is done. It can be seen at
robinsontjek.dk/en/receipt/<token>.errorstring · can be nullWhy the job failed.
delete_afterstring (date-time) · can be nullWhen the file and the rows are deleted.
created_atstring (date-time)When the job was created.
finished_atstring (date-time) · can be nullWhen the job finished.
Possible errors404
curl https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58 \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY"const res = await fetch("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58", {
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58")
req = Net::HTTP::Get.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"id": "5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58",
"source": "api",
"status": "done",
"filename": null,
"file_format": null,
"row_count": 1840,
"processed_count": 1840,
"allowed_count": 1512,
"blocked_count": 312,
"manual_count": 16,
"list_version": {
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
},
"certificate_token": "rk_9QeT4mXw2LbN7cVz",
"delete_after": "2026-11-13T09:42:30Z",
"created_at": "2026-10-14T09:41:58Z",
"finished_at": "2026-10-14T09:42:30Z"
}Get results
GET/v2/check-jobs/{id}/results
Results come in pages of up to 10,000. Send next_cursor from the response as cursor in the next call, until it is null.
Path
idstring (uuid)requiredThe job's id.
Query
cursorstringnext_cursorfrom the previous page. Leave it out for the first page.limitintegerResults per page. At most 10,000.
Response200
A page of results.
resultsarray of objectsThe results on this page.
Show 7 fields
idstring · can be nullYour reference from the request.
matchobjectWhat we found on the Robinson list at the person's address.
Show 5 fields
levelinteger · 0–5 · can be nullrequiredLevel from 0 to 5.
nullwhen the address can't be read.codestring · can be nullrequiredThe level as a code:
none,household,surname,given_name,name_buildingorname_address.labelstring · can be nullThe level in words, in Danish or English following
Accept-Language.onarray of stringsThe parts that matched:
first_name,last_name,addressandunit.previous_addressbooleantruewhen the person is on the list at a previous address. It counts as at least level 4.
statusobjectrequiredFor integrations, computed from your chosen level:
allowed(below the level),blocked(at the level or above) ormanual(the address can't be read).stop_levelinteger · 0–5The level you go by, which
statusis computed from. 1 by default, the most cautious.reasonsarray of objectsrequiredCodes that explain the answer. See Reasons.
addressobject · can be nullHow we read the address.
nullif it could not be read.Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
phone_lookupobject · can be nullPhone numbers only: what the directory lookup returned.
Show 3 fields
outcomestringfound,unlistedornot_found.namestringThe name behind the number.
addressstringThe address behind the number.
next_cursorstring · can be nullSend it as
cursorto get the next page.nullwhen there are no more.
curl https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58/results?limit=1000 \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY"const res = await fetch("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58/results?limit=1000", {
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58/results",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
params={
"limit": 1000
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58/results?limit=1000")
req = Net::HTTP::Get.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/check-jobs/5b1d9e3c-2a4f-4c7e-8d16-0e9f7a2b4c58/results?limit=1000");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"results": [
{
"id": "lead-1042",
"match": {
"level": 5,
"code": "name_address",
"label": "Same name and address",
"on": ["first_name", "last_name", "address", "unit"],
"previous_address": false
},
"status": "blocked",
"stop_level": 1,
"reasons": ["name_and_address"],
"address": {
"input": "Stationsalleen 48, 1. b, 2770 Kastrup",
"ok": true,
"street": "Stationsalleen",
"house_number": "48",
"floor": "1",
"door": "b",
"postal_code": "2770",
"city": "Kastrup",
"formatted": "Stationsalleen 48, 1. b, 2770 Kastrup",
"confidence": "exact"
}
},
{
"id": "lead-1043",
"match": {
"level": 0,
"code": "none",
"label": "Nobody on the list at the address",
"on": [],
"previous_address": false
},
"status": "allowed",
"stop_level": 1,
"reasons": [],
"address": {
"input": "Vestergade 12 8000 Aarhus",
"ok": true,
"street": "Vestergade",
"house_number": "12",
"floor": null,
"door": null,
"postal_code": "8000",
"city": "Aarhus C",
"formatted": "Vestergade 12, 8000 Aarhus C",
"confidence": "corrected"
}
}
],
"next_cursor": "eyJyb3ciOjEwMDB9"
}Companies (add-on, by agreement)
Add-on, by agreement. Check companies against the CVR register's marketing protection. If the company is a sole proprietorship, we also check the owner against the Robinson list. The endpoint answers 403 until it is enabled for your account. Write to us if you want it.
Check companies
POST/v2/company-checks
Add-on, by agreement: answers 403 until it is enabled for your account. Send the CVR number or the name of up to 1,000 companies at a time.
Body
companiesarray of objectsrequiredThe companies to check. At most 1,000.
Show 3 fields
idstringYour own reference. It comes back in the response.
cvrstringCVR number, eight digits.
namestringThe company's name, if you don't have the CVR number.
Response200
One result per company.
resultsarray of objectsOne result per company.
Show 7 fields
idstring · can be nullYour reference from the request.
cvrstring · can be nullThe CVR number.
namestring · can be nullThe company's name in CVR.
statusobjectrequiredblockedwhen the company has opted out of marketing.manualwhen we can't find it.One of:
allowed,blocked,manualreasonsarray of objectsrequiredCodes that explain the answer, such as
company_protected.company_formstring · can be nullLegal form, such as
ApSorEnkeltmandsvirksomhed(sole proprietorship).synced_atstring (date-time) · can be nullWhen we last fetched the company from CVR.
curl -X POST https://api.robinsontjek.dk/v2/company-checks \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"companies": [
{
"id": "acc-301",
"cvr": "12345678"
},
{
"id": "acc-302",
"name": "Eksempel Byg ApS"
}
]
}'const res = await fetch("https://api.robinsontjek.dk/v2/company-checks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
companies: [
{
id: "acc-301",
cvr: "12345678"
},
{
id: "acc-302",
name: "Eksempel Byg ApS"
}
]
}),
});
const data = await res.json();import os
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/company-checks",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
json={
"companies": [
{
"id": "acc-301",
"cvr": "12345678"
},
{
"id": "acc-302",
"name": "Eksempel Byg ApS"
}
]
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/company-checks")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
})
req.body = JSON.generate({
"companies" => [
{
"id" => "acc-301",
"cvr" => "12345678"
},
{
"id" => "acc-302",
"name" => "Eksempel Byg ApS"
}
]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/company-checks");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"companies" => [
[
"id" => "acc-301",
"cvr" => "12345678"
],
[
"id" => "acc-302",
"name" => "Eksempel Byg ApS"
]
]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"results": [
{
"id": "acc-301",
"cvr": "12345678",
"name": "Prøve Handel ApS",
"status": "blocked",
"reasons": ["company_protected"],
"company_form": "ApS",
"synced_at": "2026-10-14T03:00:00Z"
},
{
"id": "acc-302",
"cvr": "87654321",
"name": "Eksempel Byg ApS",
"status": "allowed",
"reasons": [],
"company_form": "ApS",
"synced_at": "2026-10-14T03:00:00Z"
}
]
}Check phone numbers
POST/v2/phone-checks
Add-on, by agreement: answers 403 until it is enabled for your account. Up to 500 numbers at a time, with or without +45 and spaces. If the number is unlisted, or we can't find who has it, the answer is manual.
Body
phonesarray of objectsrequiredThe numbers to look up. At most 500.
Show 2 fields
phonestringrequiredThe phone number, such as
+4520202020or20 20 20 20.idstringYour own reference. It comes back in the response.
Response200
One result per number.
list_versionobjectThe edition of the Robinson list that was checked against.
Show 4 fields
idstring (uuid)requiredThe list version's id.
sourcestringcpris the Robinson list.customeris your own list of people who asked not to be contacted.published_onstring (date)requiredThe date CPR published the list.
activated_atstring (date-time)When we put the list to use.
resultsarray of objectsOne result per number.
Show 7 fields
idstring · can be nullYour reference from the request.
matchobjectWhat we found on the Robinson list at the person's address.
Show 5 fields
levelinteger · 0–5 · can be nullrequiredLevel from 0 to 5.
nullwhen the address can't be read.codestring · can be nullrequiredThe level as a code:
none,household,surname,given_name,name_buildingorname_address.labelstring · can be nullThe level in words, in Danish or English following
Accept-Language.onarray of stringsThe parts that matched:
first_name,last_name,addressandunit.previous_addressbooleantruewhen the person is on the list at a previous address. It counts as at least level 4.
statusobjectrequiredFor integrations, computed from your chosen level:
allowed(below the level),blocked(at the level or above) ormanual(the address can't be read).stop_levelinteger · 0–5The level you go by, which
statusis computed from. 1 by default, the most cautious.reasonsarray of objectsrequiredCodes that explain the answer. See Reasons.
addressobject · can be nullHow we read the address.
nullif it could not be read.Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
phone_lookupobject · can be nullPhone numbers only: what the directory lookup returned.
Show 3 fields
outcomestringfound,unlistedornot_found.namestringThe name behind the number.
addressstringThe address behind the number.
Possible errors403
curl -X POST https://api.robinsontjek.dk/v2/phone-checks \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phones": [
{
"id": "lead-77",
"phone": "+4520202020"
}
]
}'const res = await fetch("https://api.robinsontjek.dk/v2/phone-checks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
phones: [
{
id: "lead-77",
phone: "+4520202020"
}
]
}),
});
const data = await res.json();import os
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/phone-checks",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
json={
"phones": [
{
"id": "lead-77",
"phone": "+4520202020"
}
]
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/phone-checks")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
})
req.body = JSON.generate({
"phones" => [
{
"id" => "lead-77",
"phone" => "+4520202020"
}
]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/phone-checks");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"phones" => [
[
"id" => "lead-77",
"phone" => "+4520202020"
]
]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"list_version": {
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
},
"results": [
{
"id": "lead-77",
"match": {
"level": 0,
"code": "none",
"label": "Nobody on the list at the address",
"on": [],
"previous_address": false
},
"status": "allowed",
"stop_level": 1,
"reasons": [],
"phone_lookup": {
"outcome": "found",
"name": "Testa Testesen",
"address": "Prøvevej 12, 8000 Aarhus C"
}
}
]
}Get the current list
GET/v2/list-versions/current
Use it to show the list's date in your own system, or to see whether a new edition has gone live since you last looked.
Response200
The edition checks run against now.
idstring (uuid)requiredThe list version's id.
sourcestringcpris the Robinson list.customeris your own list of people who asked not to be contacted.published_onstring (date)requiredThe date CPR published the list.
activated_atstring (date-time)When we put the list to use.
curl https://api.robinsontjek.dk/v2/list-versions/current \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY"const res = await fetch("https://api.robinsontjek.dk/v2/list-versions/current", {
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.robinsontjek.dk/v2/list-versions/current",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/list-versions/current")
req = Net::HTTP::Get.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/list-versions/current");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"id": "4c8e2a1f-7d3b-4e6a-9b25-1f0c6d8e3a71",
"source": "cpr",
"published_on": "2026-10-01",
"activated_at": "2026-10-02T05:00:00Z"
}Read addresses
POST/v2/addresses/resolve
Up to 100 addresses at a time. confidence says whether the address matched exactly (exact), was corrected (corrected) or could not be read (unresolved).
Body
addressesarray of stringsrequiredThe addresses as free text. At most 100.
Response200
One reading per address.
resultsarray of objectsOne reading per address, in the same order.
Show 10 fields
inputstringThe address as you sent it.
okbooleantruewhen the address could be read.streetstring · can be nullStreet name.
house_numberstring · can be nullHouse number.
floorstring · can be nullFloor.
doorstring · can be nullDoor.
postal_codestring · can be nullPostcode.
citystring · can be nullTown.
formattedstring · can be nullThe address written neatly on one line.
confidencestringexact,correctedorunresolved(could not be read).
curl -X POST https://api.robinsontjek.dk/v2/addresses/resolve \
-H "Authorization: Bearer $ROBINSONTJEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"addresses": ["Stationsalleen 48 1b 2770 Kastrup", "Norregade 7, 1165 Kbh K"]
}'const res = await fetch("https://api.robinsontjek.dk/v2/addresses/resolve", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROBINSONTJEK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
addresses: ["Stationsalleen 48 1b 2770 Kastrup", "Norregade 7, 1165 Kbh K"]
}),
});
const data = await res.json();import os
import requests
res = requests.post(
"https://api.robinsontjek.dk/v2/addresses/resolve",
headers={
"Authorization": f"Bearer {os.environ['ROBINSONTJEK_API_KEY']}",
},
json={
"addresses": ["Stationsalleen 48 1b 2770 Kastrup", "Norregade 7, 1165 Kbh K"]
},
)
data = res.json()require "net/http"
require "json"
uri = URI("https://api.robinsontjek.dk/v2/addresses/resolve")
req = Net::HTTP::Post.new(uri, {
"Authorization" => "Bearer #{ENV.fetch("ROBINSONTJEK_API_KEY")}",
"Content-Type" => "application/json",
})
req.body = JSON.generate({
"addresses" => ["Stationsalleen 48 1b 2770 Kastrup", "Norregade 7, 1165 Kbh K"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)<?php
$ch = curl_init("https://api.robinsontjek.dk/v2/addresses/resolve");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ROBINSONTJEK_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"addresses" => ["Stationsalleen 48 1b 2770 Kastrup", "Norregade 7, 1165 Kbh K"]
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);{
"results": [
{
"input": "Stationsalleen 48 1b 2770 Kastrup",
"ok": true,
"street": "Stationsalleen",
"house_number": "48",
"floor": "1",
"door": "b",
"postal_code": "2770",
"city": "Kastrup",
"formatted": "Stationsalleen 48, 1. b, 2770 Kastrup",
"confidence": "corrected"
},
{
"input": "Norregade 7, 1165 Kbh K",
"ok": true,
"street": "Nørregade",
"house_number": "7",
"floor": null,
"door": null,
"postal_code": "1165",
"city": "København K",
"formatted": "Nørregade 7, 1165 København K",
"confidence": "corrected"
}
]
}