The goal of kvkapiR is to provide a convenient R programming language interface to the Dutch Chamber of Commerce (KvK) APIs. This package is built using the httr2 package and follows best practices for wrapping APIs in R, as outlined in the httr2 documentation. It simplifies authentication, request handling, and response parsing when interacting with the KvK API.
The package provides access to all the KvK APIs:
- KvK Search API
- KvK Basisprofiel API
- KvK Vestigingsprofiel API
- KvK Naamgeving API
More details on the API can be found on the developers website of the KvK.
📋 Important Note: This package is developed independently and is not officially affiliated with or endorsed by the Dutch Chamber of Commerce (Kamer van Koophandel). It provides an unofficial interface to their public APIs.
API keys
Optaining an API key
To access the KvK API, you need an API key. You can apply for an API key and learn about different KvK APIs on the KvK Developer Portal.
Temporary API Key (Current Session Only)
If you only need an API key for the current session, use the
kvk_set_api_key()
function.
kvk_set_api_key(api_key = "your_api_key_here")
⚠️ Note: This only sets the key for the current R session. The key will be lost once you restart R.
Persistent API Key (Stored in .Renviron
)
For persistent storage across R sessions, you can manually add your
API key to your .Renviron
file:
- Open your
.Renviron
file:usethis::edit_r_environ()
- Add the line:
KVK_API_KEY=your_api_key_here
- Save the file and restart R
⚠️ Warning: Only store your API key this way if you can safely store it on your system.
Search the KvK API
The kvk_search()
function is a wrapper for the KvK
Search API. It automatically handles pagination to retrieve all
available results, up to the API limit of 1,000 records.
Below are examples demonstrating how to perform searches and handle API responses.
Example 1: single variable, less then 1.000 records
Lets search for KvK registrations in the village of Koudum. Since this is a small village with fewer than 1.000 registered businesses (the API limit), all records are retrieved in one request.
koudum <- kvk_search(plaats = "Koudum")
koudum
#> # A tibble: 512 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 01036576 000007810083 Stichting Gemeenschapsc… <named list> neve… <list>
#> 2 92692966 000032358083 M. de Boer <named list> neve… <list>
#> 3 60112891 000045511098 Corriente 't Kofjehúske <named list> neve… <list>
#> 4 40005051 000021251479 Bogerman <named list> neve… <list>
#> 5 30168016 000057781184 It Fûgelnêst <named list> neve… <list>
#> 6 01123666 000040288196 Winterberging IJzendoorn <named list> hoof… <list>
#> 7 58496505 000027833429 HFK Verhuur <named list> hoof… <list>
#> 8 62724843 000031708129 De Klink Exploitatie B.… <named list> hoof… <list>
#> 9 01091668 000000678279 Multiservice Beheer en … <named list> hoof… <list>
#> 10 41005555 000021707499 Stichting Jongerenwerk … <named list> hoof… <list>
#> # ℹ 502 more rows
Example 2: single variable, more then 1.000 records
The city of Rotterdam is significantly larger than Koudum. Searching for all registrations in Rotterdam will return more than 1,000 records, exceeding the API limit. When this happens, a warning is displayed:
rotterdam <- kvk_search(plaats = "Rotterdam")
#> ! API response contains more than 1000 results. Only the first 1000 will be retrieved.
rotterdam
#> # A tibble: 1,000 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 30019112 000022201726 Albron Campus Catering <named list> neve… <list>
#> 2 41126601 000023151153 Albert Schweitzer Groep… <named list> neve… <list>
#> 3 24483298 000023336749 Klooster Afrikaander <named list> neve… <list>
#> 4 34360247 000022666397 UWV WERKbedrijf Rotterd… <named list> neve… <list>
#> 5 24483298 000022493352 Gemeentewerken Rotterda… <named list> neve… <list>
#> 6 24104827 000020343264 Simonis Verf B.V. <named list> neve… <list>
#> 7 24335233 000010150471 Shop Rosie <named list> neve… <list>
#> 8 24483298 000022962077 GGD Rotterdam-Rijnmond <named list> neve… <list>
#> 9 34238535 000060066164 Levéo Groep B.V. <named list> neve… <list>
#> 10 27295998 000059996013 Temporary Works Design … <named list> neve… <list>
#> # ℹ 990 more rows
Example 3: combining multiple search terms in variables
You can combine multiple search terms in any parameter to refine your search. The API treats multiple words within a parameter as an AND operation by default.
# Search for businesses with both "snackbar" and "hoekje" in their name
snackbar_hoekje <- kvk_search(naam = "snackbar hoekje")
snackbar_hoekje
#> # A tibble: 17 × 7
#> kvkNummer vestigingsnummer naam adres type links vervallenNaam
#> <chr> <chr> <chr> <list> <chr> <list> <chr>
#> 1 01117517 000001223720 Snackbar … <named list> hoof… <list> NA
#> 2 08714826 000015163474 V.O.F. Sn… <named list> hoof… <list> NA
#> 3 01034036 000005461081 Bar-Snack… <named list> hoof… <list> NA
#> 4 67276644 000014002736 Café-Snac… <named list> hoof… <list> NA
#> 5 27238930 000004196724 Snackbar … <named list> hoof… <list> NA
#> 6 04034441 000009116559 Klaver 4 … <named list> hoof… <list> NA
#> 7 01048627 000003587142 Snackwage… <named list> hoof… <list> "SNACKBAR 'T…
#> 8 09053061 000006155480 Cafetaria… <named list> hoof… <list> "SNACKBAR 'T…
#> 9 33167324 000003928373 Cafetaria… <named list> hoof… <list> "Snackbar/Av…
#> 10 06046872 000014471450 Toon´s et… <named list> hoof… <list> "SNACKBAR 'T…
#> 11 34148178 000002749122 Grillroom… <named list> hoof… <list> NA
#> 12 81885326 000014587653 Snackbar … <named list> hoof… <list> NA
#> 13 01047770 000019937814 Jack's Ca… <named list> hoof… <list> "SNACKBAR \"…
#> 14 01117517 NA Snackbar … <NULL> rech… <list> NA
#> 15 08714826 NA V.O.F. Sn… <NULL> rech… <list> NA
#> 16 06046872 NA Toon´s et… <NULL> rech… <list> "SNACKBAR 'T…
#> 17 04034441 NA Klaver 4 … <NULL> rech… <list> NA
Example 4: Searching with Multiple Criteria
You can combine multiple search criteria in a query.
For example, let’s search for businesses in Utrecht where the name
contains both snackbar
and hoek
:
snackbar <- kvk_search(plaats = "Utrecht", naam = "snackbar")
snackbar
#> # A tibble: 17 × 7
#> kvkNummer vestigingsnummer naam adres type links vervallenNaam
#> <chr> <chr> <chr> <list> <chr> <list> <chr>
#> 1 30055828 000001534106 "Snackbar… <named list> hoof… <list> NA
#> 2 30149968 000001373366 "Snackbar… <named list> hoof… <list> NA
#> 3 30061168 000007852940 "Snackbar… <named list> hoof… <list> NA
#> 4 91479894 000057155518 "De Snack… <named list> hoof… <list> NA
#> 5 77162838 000044881207 "Snackbar… <named list> hoof… <list> NA
#> 6 30048901 000015639428 "Snackbar… <named list> neve… <list> NA
#> 7 63545659 000032452004 "Snackbar… <named list> hoof… <list> NA
#> 8 60975784 000028183169 "Snackbar… <named list> hoof… <list> NA
#> 9 91242894 000002767961 "Snackbar… <named list> hoof… <list> NA
#> 10 30083297 000009193693 "Cafetari… <named list> hoof… <list> "SNACKBAR HO…
#> 11 30050751 000007808208 "Petit-Re… <named list> hoof… <list> "Snackbar \"…
#> 12 30192953 000002554313 "Cafetari… <named list> hoof… <list> "Snackbar de…
#> 13 30156975 000013513508 "Bestaria… <named list> hoof… <list> "Snackbar \"…
#> 14 86470833 000005957397 "Giros" <named list> hoof… <list> "Snackbar Gi…
#> 15 29036082 000020552270 "Restaura… <named list> hoof… <list> "Snackbar de…
#> 16 76537293 000044313047 "Biltstra… <named list> hoof… <list> "Snackbar de…
#> 17 76449548 000044233884 "Snackbar… <named list> hoof… <list> NA
Example 5: Filtering by Business Type
The API allows filtering by business type using the type
parameter. You can search for:
-
hoofdvestiging
(main branch) -
nevenvestiging
(secondary branch)
-
rechtspersoon
(legal entity)
You can also combine multiple types by passing the type
parameter multiple times:
# Search for both main branches AND legal entities in Amsterdam
mixed_types <- kvk_search(
plaats = "Amsterdam",
type = "hoofdvestiging",
type = "rechtspersoon"
)
#> ! API response contains more than 1000 results. Only the first 1000 will be retrieved.
mixed_types
#> # A tibble: 1,000 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 41208816 000022202625 Stichting tot Behoud va… <named list> hoof… <list>
#> 2 52255786 000022204849 Groenhoed Servicios Y C… <named list> hoof… <list>
#> 3 52257673 000022206469 Ponooc B.V. <named list> hoof… <list>
#> 4 52800636 000022712550 Werf 't Kromhout B.V. <named list> hoof… <list>
#> 5 53190173 000023069244 Maxje-Saar B.V. <named list> hoof… <list>
#> 6 53256417 000000970077 Boeketterie Wendy <named list> hoof… <list>
#> 7 53225279 000023105658 AAC Rijsnel <named list> hoof… <list>
#> 8 53034015 000022921362 Borealis Staete Partici… <named list> hoof… <list>
#> 9 41200805 000021790221 Kath. St. Bevordering v… <named list> hoof… <list>
#> 10 52117839 000022081356 MIH PayU B.V. <named list> hoof… <list>
#> # ℹ 990 more rows
Example 6: Search by Address
You can search for businesses at a specific address using different combinations:
Postcode and house number search:
# Search businesses at a specific address
secret_address <- kvk_search(postcode = "2594BD", huisnummer = "10")
secret_address
#> # A tibble: 1 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 57096317 000028603745 politie <named list [1]> nevenvestiging <list [2]>
Street name only search:
# Search all businesses on a specific street
street_businesses <- kvk_search(straatnaam = "Bezuidenhoutseweg", plaats = "Den Haag")
street_businesses
#> # A tibble: 667 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 32135977 000017722586 HE Den Haag B.V. <named list> hoof… <list>
#> 2 80195644 000046563466 Crux Company B.V. <named list> hoof… <list>
#> 3 57096317 000048659274 politie <named list> neve… <list>
#> 4 41085610 000020758170 Reclassering Nederland <named list> neve… <list>
#> 5 57096317 000038480476 politie <named list> neve… <list>
#> 6 30046259 000005123917 Rabobank Regio Den Haag <named list> neve… <list>
#> 7 50030817 000022709053 Kinderopvang Huisje Boo… <named list> neve… <list>
#> 8 27354017 000015683672 Tandartspraktijk A.G. S… <named list> hoof… <list>
#> 9 80942598 000047272619 JDMD Holding B.V. <named list> hoof… <list>
#> 10 27185211 000003272141 Van Rijn Holding B.V. <named list> hoof… <list>
#> # ℹ 657 more rows
Retrieving Profile Data
The package provides functions to retrieve three types of detailed profiles from the API:
- Basisprofiel (Basic Profile) - General company information
- Vestigingsprofiel (Establishment Profile) - Detailed branch/location information
- Naamgeving (Name History) - Historical company names
💰 Note: Each profile retrieval costs EUR 0.02 per call (except for government organizations - see below).
Basisprofiel
The basic profile contains general company information:
# Get basic profile for a social organization (Stichting Gemeenschapscentrum Koudum)
stichting_profile <- kvk_get_basisprofiel(kvkNummer = "01036576")
stichting_profile
#> # A tibble: 1 × 10
#> kvkNummer indNonMailing naam materieleRegistratie…¹ totaalWerkzamePersonen
#> <chr> <chr> <chr> <chr> <int>
#> 1 01036576 Nee Stichti… 19660217 5
#> # ℹ abbreviated name: ¹materieleRegistratie$datumAanvang
#> # ℹ 5 more variables: statutaireNaam <chr>, handelsnamen <list>,
#> # sbiActiviteiten <list>, links <list>, `_embedded` <list>
Vestigingsprofiel
The establishment profile provides detailed information about a specific location, including geographical data:
# Get establishment profile for the same organization
stichting_vestiging <- kvk_get_vestigingsprofiel(vestigingsnummer = "000007810083")
stichting_vestiging
#> # A tibble: 1 × 16
#> vestigingsnummer kvkNummer rsin indNonMailing materieleRegistratie$datu…¹
#> <chr> <chr> <chr> <chr> <chr>
#> 1 000007810083 01036576 002897295 Nee 19840825
#> # ℹ abbreviated name: ¹materieleRegistratie$datumAanvang
#> # ℹ 11 more variables: statutaireNaam <chr>, eersteHandelsnaam <chr>,
#> # indHoofdvestiging <chr>, indCommercieleVestiging <chr>,
#> # voltijdWerkzamePersonen <int>, totaalWerkzamePersonen <int>,
#> # deeltijdWerkzamePersonen <int>, handelsnamen <list>, adressen <list>,
#> # sbiActiviteiten <list>, links <list>
# The vestigingsprofiel contains geographical data for mapping and analysis
# Access coordinates through the nested 'adressen' column:
if (nrow(stichting_vestiging) > 0 && length(stichting_vestiging$adressen[[1]]) > 0) {
address_data <- stichting_vestiging$adressen[[1]]
if ("geoData" %in% names(address_data)) {
geo_info <- address_data$geoData
cat("Geographical coordinates available for mapping and spatial analysis\n")
}
}
Naamgeving
The name history shows all historical names of a company:
# Get name history
stichting_names <- kvk_get_naamgeving(kvkNummer = "01036576")
stichting_names
#> # A tibble: 1 × 7
#> kvkNummer rsin statutaireNaam naam startdatum vestigingen links
#> <chr> <chr> <chr> <chr> <chr> <list> <list>
#> 1 01036576 002897295 Stichting Gemeenschap… Stic… 19660217 <list [2]> <list>
Usage Tracking and Cost Management
The kvkapiR package includes automatic session-based usage tracking to help you monitor API calls and costs within your current R session.
View Usage Report
Use kvk_usage_report()
to see your current session’s API
usage and costs:
# View summary report (default format)
kvk_usage_report()
#>
#> ── KvK API Usage Report - Current Session ──
#>
#> ── API Calls
#> • Search calls (free): 7
#> • Profile calls (EUR 0.02 each): 3
#> • Total calls: 10
#>
#> ── Session Costs
#> • Paid API calls: 3 x EUR 0.02 = EUR 0.06
#>
# Get structured tibble format for data analysis
usage_data <- kvk_usage_report(format = "tibble")
usage_data
#> # A tibble: 1 × 6
#> Search Basisprofiel Vestiging Naamgeving `Total Calls` `Costs (EUR)`
#> <int> <int> <int> <int> <int> <dbl>
#> 1 7 1 1 1 10 0.06
# Get detailed format with one row per API call
usage_detailed <- kvk_usage_report(format = "detailed")
usage_detailed
#> # A tibble: 10 × 6
#> timestamp date year month call_type test_environment
#> <dttm> <date> <int> <int> <chr> <lgl>
#> 1 2025-06-22 21:09:16 2025-06-22 2025 6 search FALSE
#> 2 2025-06-22 21:09:20 2025-06-22 2025 6 search FALSE
#> 3 2025-06-22 21:09:20 2025-06-22 2025 6 search FALSE
#> 4 2025-06-22 21:09:21 2025-06-22 2025 6 search FALSE
#> 5 2025-06-22 21:09:24 2025-06-22 2025 6 search FALSE
#> 6 2025-06-22 21:09:24 2025-06-22 2025 6 search FALSE
#> 7 2025-06-22 21:09:26 2025-06-22 2025 6 search FALSE
#> 8 2025-06-22 21:09:26 2025-06-22 2025 6 basisprofiel FALSE
#> 9 2025-06-22 21:09:27 2025-06-22 2025 6 vestigingsprofiel FALSE
#> 10 2025-06-22 21:09:27 2025-06-22 2025 6 naamgeving FALSE
The different output formats serve different purposes:
- “summary” (default): Human-readable overview with session costs and totals
- “tibble”: Structured summary of your session usage
- “detailed”: One row per API call, perfect for detailed analysis
The report shows your current session costs: - Per-query costs: EUR 0.02 for profile retrievals (search calls are free) - Total session costs and call counts by type
Set Usage Alerts
Protect against unexpected costs with session-based usage alerts:
# Set session cost limit of EUR 5.00
kvk_usage_alert(max_cost = 5.00)
#> ✔ Max cost: EUR 5.00 for this session
# Set session limit for paid API calls only (search calls don't count)
kvk_usage_alert(max_calls = 100)
#> ✔ Max paid calls: 100 for this session
# Set both limits for your current session
kvk_usage_alert(max_calls = 100, max_cost = 5.00)
#> ✔ Max paid calls: 100 for this session
#> ✔ Max cost: EUR 5.00 for this session
# Disable all alerts for this session
kvk_usage_alert()
#> ✔ All usage alerts have been disabled for this session
Alert features:
- Paid calls only: Call limits apply only to paid API calls (basisprofiel, vestigingsprofiel, naamgeving)
- Search calls are free: Search calls don’t count towards call limits
- Session-based: Alerts apply only to your current R session
- Real-time: Immediate notification when limits are exceeded
Export Usage Data
Export your session usage data for external analysis:
# Export session summary (default)
kvk_export_usage("session_summary.csv")
# Export detailed call data (one row per API call)
kvk_export_usage("detailed_calls.csv", format = "detailed")
Usage Tracking Privacy and Control
Session-Based Design: kvkapiR uses session-based usage tracking with these privacy features:
- Session Only: All usage data is stored in memory for your current R session only
- No File Storage: No data is written to your computer’s file system
- No External Transmission: Nothing is sent to external servers
- Automatic Reset: All tracking data is cleared when you restart R
Control Options:
# Disable tracking completely
Sys.setenv(KVKAPI_DISABLE_TRACKING = "true")
# Clear current session usage data
kvk_reset_usage()
Cost Information
⚠️ Important: The costs mentioned below are charged by the Dutch Chamber of Commerce (KvK) for API usage. These costs may change in the future, and any updates will be reflected in future versions of this package.
Pricing Structure
- Monthly base fee: EUR 6.20 (charged when you have API access)
- Search API: Free (after base fee)
- Profile APIs: EUR 0.02 per call (basisprofiel, vestigingsprofiel, naamgeving)
API Access Requirements
Important: Only authorized business representatives can apply for API access and sign contracts with KvK. Private individuals cannot register for API access.
Authorized representatives include: - Business owners (sole proprietorships) - Directors of companies (BV, NV) - Board members of foundations and associations - Partners in partnerships (VOF, CV, Maatschap) - Authorized signatories with full or limited authority
Test Environment
KvK provides a test environment where you can experiment with the API before using it in production. More information is available on the KvK documentation page.
All search and profile retrieval functions in kvkapiR
support testing mode via the test_environment
argument,
which is set to FALSE
by default.
🦆 Tip: The test environment contains fictional data based on characters from the Dutch comic magazine “Donald Duck”. Try searching for names like “Donald” and “Dagobert” to see how the API works with sample data.
The test environment:
- Uses a different base URL:
https://api.kvk.nl/test/api/
- Contains fictional business data for testing purposes
- Does not require an API key (the package uses a built-in test key)
- Has the same functionality and endpoints as production
- Does not incur any costs
- Does not count towards usage tracking
- Displays informative messages for each API call, indicating test environment usage
Note: When using the test environment, each API call displays a message like:
ℹ You are using the KvK test environment. Test data will be returned.
Test Environment Examples
# Search in test environment - no API key needed
# Note: You'll see a message indicating test environment usage
test_results <- kvk_search(naam = "Donald", test_environment = TRUE)
#> ℹ You are using the KvK test environment. Test data will be returned.
test_results
#> # A tibble: 3 × 6
#> kvkNummer vestigingsnummer naam adres type links
#> <chr> <chr> <chr> <list> <chr> <list>
#> 1 68750110 000037178601 Test BV Donald Nevenvest… <named list> neve… <list>
#> 2 68750110 000037178598 Test BV Donald <named list> hoof… <list>
#> 3 68750110 NA Test BV Donald <NULL> rech… <list>
# Get test profiles - no costs incurred
donald <- kvk_search(naam = "Donald", test_environment = TRUE)
#> ℹ You are using the KvK test environment. Test data will be returned.
test_basisprofiel <- kvk_get_basisprofiel(
kvkNummer = donald$kvkNummer[1],
test_environment = TRUE
)
#> ℹ You are using the KvK test environment. Test data will be returned.
test_basisprofiel
#> # A tibble: 1 × 11
#> kvkNummer indNonMailing naam formeleRegistratieda…¹ materieleRegistratie…²
#> <chr> <chr> <chr> <chr> <chr>
#> 1 68750110 Ja Test BV… 20170519 20170519
#> # ℹ abbreviated names: ¹formeleRegistratiedatum,
#> # ²materieleRegistratie$datumAanvang
#> # ℹ 6 more variables: totaalWerkzamePersonen <int>, statutaireNaam <chr>,
#> # handelsnamen <list>, sbiActiviteiten <list>, links <list>,
#> # `_embedded` <list>
test_vestigingsprofiel <- kvk_get_vestigingsprofiel(
vestigingsnummer = donald$vestigingsnummer[1],
test_environment = TRUE
)
#> ℹ You are using the KvK test environment. Test data will be returned.
test_vestigingsprofiel
#> # A tibble: 1 × 17
#> vestigingsnummer kvkNummer rsin indNonMailing formeleRegistratiedatum
#> <chr> <chr> <chr> <chr> <chr>
#> 1 000037178601 68750110 857587973 Ja 20170519
#> # ℹ 12 more variables: materieleRegistratie <tibble[,1]>, statutaireNaam <chr>,
#> # eersteHandelsnaam <chr>, indHoofdvestiging <chr>,
#> # indCommercieleVestiging <chr>, voltijdWerkzamePersonen <int>,
#> # totaalWerkzamePersonen <int>, deeltijdWerkzamePersonen <int>,
#> # handelsnamen <list>, adressen <list>, sbiActiviteiten <list>, links <list>
test_naamgeving <- kvk_get_naamgeving(
kvkNummer = donald$kvkNummer[1],
test_environment = TRUE
)
#> ℹ You are using the KvK test environment. Test data will be returned.
test_naamgeving
#> # A tibble: 1 × 7
#> kvkNummer rsin statutaireNaam naam startdatum vestigingen links
#> <chr> <chr> <chr> <chr> <chr> <list> <list>
#> 1 68750110 857587973 Test BV Donald Test BV Dona… 20170519 <list [2]> <list>
Conclusion
The kvkapiR
package provides a comprehensive interface
for accessing KvK business data in R with:
- Simple authentication management
- Full API coverage (search and profile retrieval)
- Test environment support for development
- Session-based usage tracking and cost monitoring
- Flexible search options including multiple filters
- Real-time usage alerts with smart filtering (paid calls only)
- Government organization fee exemption
- Privacy-focused design with no permanent file storage
For more details, refer to the official KvK API documentation.