KVK API met Go: Bedrijfsgegevens opvragen in Golang
KVKBase Team

KVK API met Go: Bedrijfsgegevens opvragen in Golang

Integreer de KVKBase API in je Go-project. Met codevoorbeelden voor HTTP-clients, structs, foutafhandeling en een herbruikbare client-bibliotheek — geschikt voor zowel beginners als ervaren Go-developers.

kvkapigolanggodevelopersintegratie

Go is een van de snelst groeiende programmeertalen voor backend-services, microservices en command-line tools. De combinatie van hoge performance, eenvoudige concurrency en een sterke standaardbibliotheek maakt Go populair bij teams die betrouwbare API-integraties willen bouwen. In deze gids laten we zien hoe je de KVKBase API integreert in je Go-project om Nederlandse bedrijfsgegevens op te vragen.

Waarom Go voor KVK-integraties?

Go heeft een uitstekende ingebouwde HTTP-client, krachtige JSON-ondersteuning en strikte typing via structs. Dat maakt het ideaal voor API-integraties waarbij je:

  • Snel KVK-nummers wilt valideren bij klantregistratie
  • Bedrijfsgegevens wilt ophalen voor je CRM of ERP
  • Batch-verwerking wilt doen over grote lijsten van KVK-nummers
  • Een microservice of interne tool bouwt die bedrijfsdata nodig heeft

De KVKBase API geeft gestructureerde JSON terug, wat naadloos aansluit op Go’s encoding/json-package en struct-definities.

Vereisten

  • Go 1.21 of hoger
  • Een API-key van KVKBase
  • Geen externe dependencies nodig — alleen de Go standaardbibliotheek

Stap 1: Structs definiëren

Begin met het definiëren van structs die de API-response representeren. Go’s JSON-unmarshaling werkt het beste als je struct-tags toevoegt die overeenkomen met de JSON-veldnamen.

// kvkbase/types.go

package kvkbase

type Adres struct {
	Straat      string `json:"straat"`
	Huisnummer  string `json:"huisnummer"`
	Postcode    string `json:"postcode"`
	Plaats      string `json:"plaats"`
	Land        string `json:"land"`
}

type SbiCode struct {
	Code        string `json:"code"`
	Omschrijving string `json:"omschrijving"`
}

type Bedrijf struct {
	KvkNummer   string    `json:"kvkNummer"`
	Naam        string    `json:"naam"`
	Rechtsvorm  string    `json:"rechtsvorm"`
	Actief      bool      `json:"actief"`
	Adres       Adres     `json:"adres"`
	SbiCodes    []SbiCode `json:"sbiCodes"`
	BtwNummer   string    `json:"btwNummer,omitempty"`
}

type APIResponse struct {
	Succes bool     `json:"succes"`
	Data   *Bedrijf `json:"data,omitempty"`
	Fout   string   `json:"fout,omitempty"`
}

Stap 2: Een herbruikbare client bouwen

Maak een client-struct die je API-key en HTTP-client beheert. Dit is de Go-manier van werken: dependency injection in plaats van globale variabelen.

// kvkbase/client.go

package kvkbase

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

const baseURL = "https://api.kvkbase.nl/api/v1"

type Client struct {
	apiKey     string
	httpClient *http.Client
}

func NewClient(apiKey string) *Client {
	return &Client{
		apiKey: apiKey,
		httpClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

func (c *Client) ZoekBedrijf(kvkNummer string) (*Bedrijf, error) {
	url := fmt.Sprintf("%s/kvk/%s", baseURL, kvkNummer)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("aanmaken request mislukt: %w", err)
	}
	req.Header.Set("x-api-key", c.apiKey)
	req.Header.Set("Accept", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("API-aanroep mislukt: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		return nil, fmt.Errorf("KVK-nummer %s niet gevonden", kvkNummer)
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("onverwachte statuscode: %d", resp.StatusCode)
	}

	var result APIResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("JSON-verwerking mislukt: %w", err)
	}

	if !result.Succes || result.Data == nil {
		return nil, fmt.Errorf("API-fout: %s", result.Fout)
	}

	return result.Data, nil
}

Stap 3: Basis gebruik

// main.go

package main

import (
	"fmt"
	"log"
	"os"

	"jouwproject/kvkbase"
)

func main() {
	apiKey := os.Getenv("KVKBASE_API_KEY")
	if apiKey == "" {
		log.Fatal("KVKBASE_API_KEY omgevingsvariabele niet ingesteld")
	}

	client := kvkbase.NewClient(apiKey)

	bedrijf, err := client.ZoekBedrijf("12345678")
	if err != nil {
		log.Fatalf("Fout: %v", err)
	}

	fmt.Printf("Bedrijfsnaam: %s\n", bedrijf.Naam)
	fmt.Printf("KVK-nummer:  %s\n", bedrijf.KvkNummer)
	fmt.Printf("Rechtsvorm:  %s\n", bedrijf.Rechtsvorm)
	fmt.Printf("Actief:      %v\n", bedrijf.Actief)
	fmt.Printf("Adres:       %s %s, %s %s\n",
		bedrijf.Adres.Straat,
		bedrijf.Adres.Huisnummer,
		bedrijf.Adres.Postcode,
		bedrijf.Adres.Plaats,
	)
}

Stap 4: Concurrency met goroutines

Een van de sterkste punten van Go is concurrency via goroutines. Dit is bijzonder handig als je een lijst van KVK-nummers wilt verwerken zonder ze één voor één sequentieel op te vragen.

// batch.go

package kvkbase

import (
	"sync"
)

type BatchResultaat struct {
	KvkNummer string
	Bedrijf   *Bedrijf
	Fout      error
}

func (c *Client) BulkZoek(kvkNummers []string, maxConcurrent int) []BatchResultaat {
	resultaten := make([]BatchResultaat, len(kvkNummers))
	sem := make(chan struct{}, maxConcurrent) // semaphore voor rate limiting
	var wg sync.WaitGroup

	for i, kvk := range kvkNummers {
		wg.Add(1)
		go func(idx int, nummer string) {
			defer wg.Done()
			sem <- struct{}{}        // slot bezetten
			defer func() { <-sem }() // slot vrijgeven

			bedrijf, err := c.ZoekBedrijf(nummer)
			resultaten[idx] = BatchResultaat{
				KvkNummer: nummer,
				Bedrijf:   bedrijf,
				Fout:      err,
			}
		}(i, kvk)
	}

	wg.Wait()
	return resultaten
}

Gebruik maxConcurrent om te voorkomen dat je de rate limits van de API overschrijdt. Een waarde van 5 tot 10 is doorgaans veilig.

nummers := []string{"12345678", "87654321", "11223344"}
resultaten := client.BulkZoek(nummers, 5)

for _, r := range resultaten {
	if r.Fout != nil {
		fmt.Printf("%s: FOUT - %v\n", r.KvkNummer, r.Fout)
		continue
	}
	fmt.Printf("%s: %s (%s)\n", r.KvkNummer, r.Bedrijf.Naam, r.Bedrijf.Rechtsvorm)
}

Foutafhandeling in Go

Go’s expliciete foutafhandeling maakt de integratie robuust. De client gebruikt fmt.Errorf met %w voor fout-wrapping, zodat je upstream context behoudt:

bedrijf, err := client.ZoekBedrijf("00000000")
if err != nil {
	// Specifieke fout checken
	if strings.Contains(err.Error(), "niet gevonden") {
		// KVK-nummer bestaat niet
		fmt.Println("Onbekend KVK-nummer")
	} else {
		// Netwerk- of serverfout
		log.Printf("API-fout: %v", err)
	}
	return
}

Gebruik in een HTTP-handler (Echo/Gin/chi)

Als je Go gebruikt voor een web-API, is het patroon hetzelfde. Hieronder een voorbeeld met het populaire chi-framework:

// handlers/kvk.go

package handlers

import (
	"encoding/json"
	"net/http"

	"jouwproject/kvkbase"
	"github.com/go-chi/chi/v5"
)

type KvkHandler struct {
	client *kvkbase.Client
}

func NewKvkHandler(client *kvkbase.Client) *KvkHandler {
	return &KvkHandler{client: client}
}

func (h *KvkHandler) GetBedrijf(w http.ResponseWriter, r *http.Request) {
	kvkNummer := chi.URLParam(r, "kvkNummer")

	bedrijf, err := h.client.ZoekBedrijf(kvkNummer)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(bedrijf)
}

Registreer de handler in je router:

r := chi.NewRouter()
kvkHandler := handlers.NewKvkHandler(kvkbase.NewClient(os.Getenv("KVKBASE_API_KEY")))
r.Get("/api/bedrijf/{kvkNummer}", kvkHandler.GetBedrijf)

Testing

Go’s testpakket is ingebouwd. Maak een mock HTTP-server om de client te testen zonder echte API-aanroepen:

// kvkbase/client_test.go

package kvkbase_test

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"jouwproject/kvkbase"
)

func TestZoekBedrijf(t *testing.T) {
	// Mock server opzetten
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		response := kvkbase.APIResponse{
			Succes: true,
			Data: &kvkbase.Bedrijf{
				KvkNummer:  "12345678",
				Naam:       "Test BV",
				Rechtsvorm: "BV",
				Actief:     true,
			},
		}
		json.NewEncoder(w).Encode(response)
	}))
	defer server.Close()

	// Client aanpassen om mock te gebruiken
	client := kvkbase.NewClient("test-key")

	bedrijf, err := client.ZoekBedrijf("12345678")
	if err != nil {
		t.Fatalf("Onverwachte fout: %v", err)
	}

	if bedrijf.Naam != "Test BV" {
		t.Errorf("Verwacht 'Test BV', kreeg '%s'", bedrijf.Naam)
	}
}

Volgende stappen

Met deze basis kun je alle kanten op:

  • Voeg caching toe met sync.Map of Redis om herhaalde lookups te vermijden
  • Integreer met je klant-onboarding flow om automatisch bedrijfsgegevens in te vullen (zie ook: KVK data voor klant-onboarding)
  • Bouw een webhook-listener die reageert op bedrijfsstatuswijzigingen
  • Combineer met bedrijfsstatus-monitoring voor proactieve alerts

Heb je vragen over de integratie of wil je je API-key aanvragen? Ga naar kvkbase.nl om aan de slag te gaan.