PHPackages                             dij-digital/kvk-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. dij-digital/kvk-laravel

ActiveLibrary[API Development](/categories/api)

dij-digital/kvk-laravel
=======================

KVK API package for Laravel

0.1.1(2mo ago)2282↓50%MITPHPPHP ^8.4|^8.5

Since Mar 3Pushed 2mo agoCompare

[ Source](https://github.com/DIJ-digital/kvk-laravel)[ Packagist](https://packagist.org/packages/dij-digital/kvk-laravel)[ RSS](/packages/dij-digital-kvk-laravel/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (7)Versions (3)Used By (0)

KVK Laravel
===========

[](#kvk-laravel)

A Laravel package for the Dutch KVK (Kamer van Koophandel) Handelsregister API. Provides a clean, typed, and fakable interface for searching the company registry.

Requirements
------------

[](#requirements)

- PHP ^8.4|^8.5
- Laravel ^12

Installation
------------

[](#installation)

```
composer require dij-digital/kvk-laravel
```

The service provider and facade are auto-discovered.

Publish the config file:

```
php artisan vendor:publish --tag=kvk
```

Configuration
-------------

[](#configuration)

Add your API key to `.env`:

```
KVK_API_KEY=your-api-key
KVK_BASE_URL=https://api.kvk.nl
```

For testing against the KVK sandbox, use:

```
KVK_API_KEY=l7xx1f2691f2520d487b902f4e0b57a0b197
KVK_BASE_URL=https://api.kvk.nl/test
```

Usage
-----

[](#usage)

### Search

[](#search)

Search the KVK registry using the fluent API:

```
use DIJ\Kvk\Facades\KVK;

$result = KVK::search()
    ->kvkNumber('69599068')
    ->city('Amsterdam')
    ->includeInactiveRegistrations()
    ->get();

foreach ($result->items as $item) {
    echo $item->kvkNumber;  // '69599068'
    echo $item->name;       // 'Test BV Donald'
    echo $item->type;       // 'hoofdvestiging'
    echo $item->active;     // 'Ja'
}
```

Or pass a `SearchParameters` DTO directly:

```
use DIJ\Kvk\Data\SearchParameters;
use DIJ\Kvk\Facades\KVK;

$params = new SearchParameters(
    kvkNumber: '69599068',
    city: 'Amsterdam',
    includeInactiveRegistrations: true,
);
$result = KVK::search()->search($params);
```

Both approaches produce identical API calls.

### Available search parameters

[](#available-search-parameters)

MethodTypeDescription`kvkNumber(string)`string8-digit KVK number`rsin(string)`string9-digit RSIN number`branchNumber(string)`string12-digit branch number`name(string)`stringTrade name or statutory name`streetName(string)`stringStreet name`city(string)`stringCity name`postalCode(string)`stringPostal code (with houseNumber or poBoxNumber)`houseNumber(int)`intHouse number (with postalCode)`houseLetter(string)`stringHouse letter (with houseNumber)`poBoxNumber(int)`intPO box number (with postalCode)`type(array)`arrayFilter: `hoofdvestiging`, `nevenvestiging`, `rechtspersoon``includeInactiveRegistrations(bool)`boolInclude dissolved companies (default: false)`page(int)`intPage number (default: 1)`resultsPerPage(int)`intResults per page (default: 100, max: 100)### Base Profile

[](#base-profile)

Retrieve detailed company information by KVK number using the fluent API:

```
use DIJ\Kvk\Facades\KVK;

// Get main company profile
$profile = KVK::baseProfile('69599068')->get();
echo $profile->kvkNumber;    // '69599068'
echo $profile->name;         // 'Test Stichting Bolderbast'
echo $profile->statutoryName; // 'Stichting Bolderbast'

// Get owner information
$owner = KVK::baseProfile('69599068')->owner();
echo $owner->legalForm;      // 'BesloteVennootschap'
foreach ($owner->addresses as $address) {
    echo $address->city;     // 'Woerden'
}

// Get main branch details
$mainBranch = KVK::baseProfile('69599068')->mainBranch();
echo $mainBranch->branchNumber;    // '000037178598'
echo $mainBranch->totalEmployees;  // 15

// Get all branches listing
$branches = KVK::baseProfile('69599068')->branches();
echo $branches->totalBranchCount;  // 1
foreach ($branches->branches as $branch) {
    echo $branch->firstTradeName;  // 'Test BV Donald'
}

// Include GPS coordinates in addresses (geoData)
$owner = KVK::baseProfile('69599068')->geoData()->owner();
foreach ($owner->addresses as $address) {
    echo $address->geoData?->gpsLatitude;  // 52.08151653230184
}
```

### Available base profile options

[](#available-base-profile-options)

MethodDescription`get()`Main company profile — name, registration dates, trade names, SBI activities`owner()`Owner information — legal form, addresses, websites`mainBranch()`Main branch details — employees, addresses, trade names, SBI activities`branches()`All branches listing — counts and summary per branch`geoData(bool)`Include GPS coordinates in address responses (default: false)### Branch Profile

[](#branch-profile)

Retrieve detailed branch information by branch number (`vestigingsnummer`):

```
use DIJ\Kvk\Facades\KVK;

$branch = KVK::branchProfile('000037178598')->get();
echo $branch->branchNumber;   // '000037178598'
echo $branch->kvkNumber;      // '68750110'
echo $branch->firstTradeName; // 'Test BV Donald'

// Include GPS coordinates in addresses
$branch = KVK::branchProfile('000037178598')->geoData()->get();
foreach ($branch->addresses as $address) {
    echo $address->geoData?->gpsLatitude;
}
```

### Available branch profile options

[](#available-branch-profile-options)

MethodDescription`get()`Branch profile — trade names, employees, addresses, websites, and SBI activities`geoData(bool)`Include GPS coordinates in address responses (default: false)### Naming (Trade Names)

[](#naming-trade-names)

Retrieve trade names by KVK number:

```
use DIJ\Kvk\Facades\KVK;

$naming = KVK::naming('69599068')->get();
echo $naming->kvkNumber;     // '69599068'
echo $naming->statutoryName; // 'Stichting Bolderbast'
echo $naming->name;          // 'Test Stichting Bolderbast'

foreach ($naming->branches as $branch) {
    echo $branch->branchNumber;     // '000037178598'
    echo $branch->firstTradeName;   // 'Test Stichting Bolderbast' (commercial)
    echo $branch->name;             // 'Stichting Branch' (non-commercial)
    echo $branch->alsoKnownAs;      // 'Branch Alias'
}
```

### Available naming options

[](#available-naming-options)

MethodDescription`get()`Trade names profile — statutory name, primary name, aliases, and branch-level names### Subscriptions (Mutatieservice)

[](#subscriptions-mutatieservice)

Monitor changes to KVK registry entries via the Mutatieservice (change notifications):

```
use DIJ\Kvk\Facades\KVK;

// List all subscriptions
$subscriptions = KVK::subscriptions()->get();
echo $subscriptions->customerId;  // 'customer-123'
foreach ($subscriptions->subscriptions as $sub) {
    echo $sub->id;         // 'subscription-456'
    echo $sub->startDate;  // '2024-01-01T00:00:00Z'
    echo $sub->active;     // true
}

// Get signals (change notifications) for a subscription
$signals = KVK::subscriptions()
    ->subscription('subscription-456')
    ->from('2024-01-01T00:00:00Z')
    ->to('2024-12-31T23:59:59Z')
    ->page(1)
    ->resultsPerPage(50)
    ->signals();

foreach ($signals->signals as $signal) {
    echo $signal->kvkNumber;   // '69792917'
    echo $signal->signalType;  // 'SignaalGewijzigdeInschrijving'
    echo $signal->timestamp;   // '2024-05-14T15:25:13.773Z'
}

// Get a specific signal's full details
$signal = KVK::subscriptions()
    ->subscription('subscription-456')
    ->signal('signal-001');

echo $signal->messageId;              // '3e96fad5-...'
echo $signal->signalType;             // 'SignaalGewijzigdeInschrijving'
echo $signal->registrationTimestamp;  // '2024-05-14T15:25:13.773Z'
echo $signal->relatesTo['kvkNummer']; // '69792917'
```

### Available subscription options

[](#available-subscription-options)

MethodDescription`get()`List all subscriptions for your API key`subscription(string)`Scope to a specific subscription (returns a scoped builder)### Available signal options (on scoped subscription)

[](#available-signal-options-on-scoped-subscription)

MethodDescription`from(string)`Filter signals from this ISO 8601 datetime`to(string)`Filter signals until this ISO 8601 datetime`page(int)`Page number (default: 1)`resultsPerPage(int)`Results per page (min: 10, max: 500, default: 100)`signals()`List signals for this subscription (paginated)`signal(string)`Get a specific signal's full detailsError Handling
--------------

[](#error-handling)

The package throws typed exceptions for API failures. All exceptions extend `KvkException` and expose `statusCode` and `responseBody` properties.

ExceptionWhen`KvkAuthenticationException`HTTP 401 or 403 — invalid or missing API key`KvkServerException`HTTP 500+ — KVK API server error`KvkRequestException`Any other non-successful HTTP response```
use DIJ\Kvk\Exceptions\KvkAuthenticationException;
use DIJ\Kvk\Exceptions\KvkException;

try {
    $result = KVK::search()->kvkNumber('69599068')->get();
} catch (KvkAuthenticationException $e) {
    // Invalid API key — check KVK_API_KEY in .env
    $e->statusCode;    // 401
    $e->responseBody;  // raw response body
} catch (KvkException $e) {
    // Any other API error
    $e->statusCode;
    $e->responseBody;
}
```

Testing
-------

[](#testing)

The package is designed to be easily fakeable in your application tests.

### Basic Usage

[](#basic-usage)

Use `KVK::fake()` to replace all KVK API calls with a fake that returns no results:

```
use DIJ\Kvk\Facades\KVK;

KVK::fake();

$result = KVK::search()->kvkNumber('69599068')->get();
// Returns a SearchResult with 0 items — no HTTP calls made
```

### Faking Specific Responses

[](#faking-specific-responses)

Pass response DTOs to `KVK::fake()` using named parameters to control what each endpoint returns:

```
use DIJ\Kvk\Data\Responses\BaseProfileResponse;
use DIJ\Kvk\Data\Responses\SearchResponse;
use DIJ\Kvk\Data\ValueObjects\SbiActivity;
use DIJ\Kvk\Facades\KVK;

// Search faking — use withSearchResponses() on the returned FakeKVK
KVK::fake()->withSearchResponses(
    SearchResponse::fake(kvkNumber: '69599068', name: 'Acme BV'),
    SearchResponse::fake(kvkNumber: '12345678', name: 'Other BV'),
);

$result = KVK::search()->get();
// $result->total === 2
// $result->items->first()->kvkNumber === '69599068'

// Customize nested data — e.g., test a specific SBI code
KVK::fake(
    baseProfile: BaseProfileResponse::fake(
        sbiActivities: [SbiActivity::fake(sbiCode: '86101')],
    ),
);

$profile = KVK::baseProfile('69599068')->get();
echo $profile->sbiActivities[0]->sbiCode; // '86101'
```

#### Available fake() parameters

[](#available-fake-parameters)

ParameterTypeDescription`$baseProfile``?BaseProfileResponse`Custom base profile response`$baseProfileOwner``?BaseProfileOwnerResponse`Custom owner response`$baseProfileMainBranch``?BaseProfileMainBranchResponse`Custom main branch response`$baseProfileBranches``?BaseProfileBranchesResult`Custom branches result`$branchProfile``?BranchProfileResponse`Custom branch profile response`$naming``?NamingResponse`Custom naming response`$subscriptions``?SubscriptionsResult`Custom subscriptions result`$signals``?SignalsResult`Custom signals result`$signal``?SignalResponse`Custom signal response`...$searchResponses``SearchResponse`Search responses — pass via `withSearchResponses()` on the returned `FakeKVK`#### Fluent Builder Interface

[](#fluent-builder-interface)

You can also update a fake after it's been created using the fluent `with*` methods:

```
KVK::fake()
    ->withBaseProfile(BaseProfileResponse::fake(name: 'Updated Name'))
    ->withSearchResponses(SearchResponse::fake(kvkNumber: '11223344'));
```

#### DTO Fake Defaults

[](#dto-fake-defaults)

Every response DTO provides a `fake()` method with sensible defaults from the KVK test environment. Only specify the fields you need to override:

```
$response = SearchResponse::fake(
    kvkNumber: '69599068',
    name: 'Test BV Donald',
    type: 'hoofdvestiging',
);
```

ParameterDefault`kvkNumber``'69599068'``name``'Test BV Donald'``type``'hoofdvestiging'``active``'Ja'``branchNumber``'000037178598'``rsin``null``address``null`### Facade Cleanup

[](#facade-cleanup)

If you use `KVK::fake()` in tests that extend Laravel's `TestCase`, cleanup is automatic. For plain PHPUnit tests, add this to your test class:

```
protected function tearDown(): void
{
    \Illuminate\Support\Facades\Facade::clearResolvedInstances();
    parent::tearDown();
}
```

### Advanced: HTTP-Level Faking

[](#advanced-http-level-faking)

For lower-level tests that need to verify the full HTTP pipeline (including request parameters and response parsing), you can still use `Http::fake()` directly:

```
use Illuminate\Support\Facades\Http;

Http::fake([
    'api.kvk.nl/*' => Http::response([
        'pagina' => 1,
        'resultatenPerPagina' => 10,
        'totaal' => 1,
        'resultaten' => [
            [
                'kvkNummer' => '69599068',
                'naam' => 'Test BV',
                'type' => 'hoofdvestiging',
                'actief' => 'Ja',
            ],
        ],
    ]),
]);

$result = KVK::search()->kvkNumber('69599068')->get();
```

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance86

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

2

Last Release

68d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0ab892324187874b808c0bd037ff12f74fcb3257f0cf8d9723bbd30768d482ac?d=identicon)[DIJ](/maintainers/DIJ)

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dij-digital-kvk-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/dij-digital-kvk-laravel/health.svg)](https://phpackages.com/packages/dij-digital-kvk-laravel)
```

###  Alternatives

[spatie/laravel-query-builder

Easily build Eloquent queries from API requests

4.4k26.9M220](/packages/spatie-laravel-query-builder)[essa/api-tool-kit

set of tools to build an api with laravel

52680.5k](/packages/essa-api-tool-kit)[esign/laravel-conversions-api

A laravel wrapper package around the Facebook Conversions API

69145.4k](/packages/esign-laravel-conversions-api)[surface/laravel-webfinger

A Laravel package to create an ActivityPub webfinger.

113.8k](/packages/surface-laravel-webfinger)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
