KVK API with PHP and Laravel: Fetch Dutch Company Data in Your Webshop or SaaS
KVKBase Team

KVK API with PHP and Laravel: Fetch Dutch Company Data in Your Webshop or SaaS

Integrate the KVKBase API into your PHP or Laravel project. Code examples for Guzzle, WooCommerce, and Laravel — including form auto-fill and error handling.

kvkapiphplaravelwoocommercedevelopers

PHP is still the backbone of the web. From WordPress plugins to Laravel SaaS platforms and WooCommerce shops, millions of developers work with PHP every day. If you have business customers who need to enter their company details, the KVKBase API is the fastest way to automate that process. In this guide we show you how to integrate the KVK API into your PHP or Laravel project.

Why PHP for KVK integrations?

PHP dominates the CMS and e-commerce landscape. More than 70% of all websites run on PHP, and a large portion of those are WooCommerce or custom Laravel applications. For B2B webshops and SaaS platforms, automatically filling in company details saves business customers a huge amount of time. With KVKBase, a single API call gives you access to company name, address, legal form, and more.

Installation

Add KVKBase to your project via Composer, or use PHP’s built-in file_get_contents function. We recommend using Guzzle for a reliable HTTP client:

composer require guzzlehttp/guzzle

Store your API key as an environment variable. In Laravel, use the .env file:

KVKBASE_API_KEY=your_...

Retrieve your API key from the KVKBase dashboard.

Basic integration with PHP (without a framework)

Below is a minimal implementation using curl for PHP projects without a framework, such as WordPress plugins:

<?php

function kvkbase_lookup_company(string $kvkNumber): ?array {
    $apiKey = getenv('KVKBASE_API_KEY');
    $url    = "https://api.kvkbase.nl/api/v1/kvk/" . urlencode($kvkNumber);

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "X-Api-Key: {$apiKey}",
            "Content-Type: application/json",
        ],
        CURLOPT_TIMEOUT        => 10,
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode !== 200 || !$response) {
        return null;
    }

    $data = json_decode($response, true);
    return $data['succes'] ? $data['data'] : null;
}

Using WordPress? Add this function to functions.php and call it from a REST endpoint or AJAX handler.

Integration with Laravel (Guzzle + Service Class)

In a Laravel project, create a clean service class. This keeps your code testable and easy to mock:

<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Cache;

class KvkBaseService
{
    protected Client $client;

    public function __construct()
    {
        $this->client = new Client([
            'base_uri' => 'https://api.kvkbase.nl/api/v1/',
            'timeout'  => 10,
            'headers'  => [
                'X-Api-Key'    => config('services.kvkbase.api_key'),
                'Content-Type' => 'application/json',
            ],
        ]);
    }

    public function lookupCompany(string $kvkNumber): ?array
    {
        // Cache results for 1 hour
        return Cache::remember("kvk:{$kvkNumber}", 3600, function () use ($kvkNumber) {
            try {
                $response = $this->client->get("kvk/{$kvkNumber}");
                $data     = json_decode($response->getBody(), true);
                return $data['succes'] ? $data['data'] : null;
            } catch (RequestException $e) {
                if ($e->hasResponse() && $e->getResponse()->getStatusCode() === 404) {
                    return null;
                }
                throw $e;
            }
        });
    }
}

Register the API key in config/services.php:

'kvkbase' => [
    'api_key' => env('KVKBASE_API_KEY'),
],

Laravel Controller and Route

Create a controller that uses the service:

<?php

namespace App\Http\Controllers;

use App\Services\KvkBaseService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class CompanyController extends Controller
{
    public function __construct(protected KvkBaseService $kvkBase) {}

    public function lookup(Request $request): JsonResponse
    {
        $request->validate([
            'kvk_number' => 'required|digits:8',
        ]);

        $company = $this->kvkBase->lookupCompany($request->kvk_number);

        if (!$company) {
            return response()->json(['error' => 'Company not found'], 404);
        }

        return response()->json($company);
    }
}

Add the route to routes/api.php:

Route::get('/company/{kvkNumber}', [CompanyController::class, 'lookup']);

Form auto-fill with Blade + Alpine.js

The most valuable use case is automatically filling in form fields as soon as a user enters their KVK number. Here’s a Blade component with Alpine.js:

<div x-data="{
    kvkNumber: '',
    company: null,
    loading: false,
    async lookup() {
        if (this.kvkNumber.length !== 8) return;
        this.loading = true;
        const res = await fetch('/api/company/' + this.kvkNumber);
        if (res.ok) this.company = await res.json();
        this.loading = false;
    }
}">
    <input
        x-model="kvkNumber"
        @blur="lookup"
        type="text"
        name="kvk_number"
        placeholder="KVK number (8 digits)"
        maxlength="8"
    />

    <span x-show="loading">Loading company details...</span>

    <template x-if="company">
        <div>
            <input name="company_name" :value="company.naam" readonly />
            <input name="street" :value="company.adres.straat + ' ' + company.adres.huisnummer" readonly />
            <input name="postcode" :value="company.adres.postcode" readonly />
            <input name="city" :value="company.adres.plaats" readonly />
        </div>
    </template>
</div>

This works without additional dependencies if Alpine.js is already loaded, which is the case in most modern Laravel projects that use Livewire.

WooCommerce B2B checkout

Do you have a WooCommerce shop with business customers? Add a KVK field to the checkout and automatically fill in the billing details:

<?php
// Add to functions.php of your WooCommerce theme

add_action('woocommerce_before_checkout_billing_form', function ($checkout) {
    woocommerce_form_field('billing_kvk', [
        'type'        => 'text',
        'class'       => ['form-row-wide'],
        'label'       => 'KVK number',
        'placeholder' => '12345678',
        'required'    => false,
    ], $checkout->get_value('billing_kvk'));
});

add_action('woocommerce_checkout_update_order_meta', function ($order_id) {
    if (!empty($_POST['billing_kvk'])) {
        update_post_meta($order_id, '_billing_kvk', sanitize_text_field($_POST['billing_kvk']));
    }
});

Combine this with the JavaScript auto-fill above and your business customers only need to fill in a single field at checkout.

KVK number validation

Validate the KVK number server-side before calling the API. A valid KVK number always consists of exactly 8 digits. Read more about the validation rules in our article on KVK number validation and checksum.

function isValidKvkNumber(string $kvk): bool
{
    return (bool) preg_match('/^\d{8}$/', $kvk);
}

In Laravel you can use a convenient validation rule:

$request->validate([
    'kvk_number' => ['required', 'regex:/^\d{8}$/'],
]);

Caching in Laravel

KVK data rarely changes. Use Laravel’s cache to avoid unnecessary API calls:

$company = Cache::remember("kvk:{$kvkNumber}", now()->addHours(6), function () use ($kvkNumber) {
    return $this->kvkBase->lookupCompany($kvkNumber);
});

For high-traffic sites, use Redis as the cache driver. This significantly speeds up your checkout and reduces your API costs.

Next steps

With this integration you have everything you need to use the KVKBase API in your PHP or Laravel project. From here you can expand with:

Ready to get started? Create a free account at kvkbase.nl and you’ll have API access within a few minutes.