Leer hoe je efficiënt grote hoeveelheden adressen geocodeert met batch processing, async patterns en optimale performance.
Wanneer je honderden of duizenden adressen moet geocoderen - denk aan CRM-imports, data migraties of analytics - is efficiënte batch processing essentieel. In dit artikel behandelen we strategieën voor optimale throughput zonder je API limits te overschrijden.

Batch vs Single requests
GeoRex biedt twee manieren om adressen te geocoderen:
| Methode | Endpoint | Max adressen | Use case |
|---|---|---|---|
| Single | /geocode?q=...&token=TOKEN | 1 | Real-time, checkout |
| Loop | Meerdere /geocode calls | Onbeperkt* | Bulk import, analytics |
Bulk geocoding implementeren
Met de token-based authenticatie kun je efficiënt meerdere adressen verwerken. Het token is 10 minuten geldig, dus je kunt veel requests doen met één token:
// Token ophalen (geldig voor 10 minuten)async function getToken(apiKey) {const res = await fetch('https://api.georex.nl/api/token', {method: 'POST',headers: { 'X-API-Key': apiKey }});return (await res.json()).token;}// Enkele geocode requestasync function geocodeOne(address, token) {const query = encodeURIComponent(address);const res = await fetch(`https://api.georex.nl/geocode?q=${query}&token=${token}`);return res.json();}// Batch verwerking met rate limitingasync function batchGeocode(addresses, apiKey) {const token = await getToken(apiKey);const results = [];for (const address of addresses) {const result = await geocodeOne(address, token);if (result.features?.length > 0) {const [lon, lat] = result.features[0].geometry.coordinates;results.push({address,status: 'found',lat, lon,...result.features[0].properties});} else {results.push({ address, status: 'not_found' });}// Kleine delay om rate limits te respecterenawait new Promise(r => setTimeout(r, 100));}return results;}// Gebruikconst results = await batchGeocode(['Damrak 1, Amsterdam', 'Coolsingel 40, Rotterdam'],'gx_live_your_key');console.log(results);
Grote datasets verwerken
Voor grote datasets moet je chunken, token vernieuwing en rate limiting implementeren:
class BulkGeocoder {constructor(apiKey, options = {}) {this.apiKey = apiKey;this.batchSize = options.batchSize || 100;this.delayBetweenRequests = options.delay || 100; // msthis.maxRetries = options.maxRetries || 3;this.token = null;this.tokenExpiry = 0;}// Token management (vernieuw als bijna verlopen)async getToken() {if (!this.token || Date.now() > this.tokenExpiry - 60000) {const res = await fetch('https://api.georex.nl/api/token', {method: 'POST',headers: { 'X-API-Key': this.apiKey }});const data = await res.json();this.token = data.token;this.tokenExpiry = Date.now() + (data.expires_in * 1000);}return this.token;}/*** Geocodeer een grote dataset* @param {string[]} addresses - Array van adressen* @param {Function} onProgress - Callback voor voortgang*/async processAll(addresses, onProgress) {const results = [];const chunks = this.chunkArray(addresses, this.batchSize);for (let i = 0; i < chunks.length; i++) {const chunk = chunks[i];// Progress callbackif (onProgress) {onProgress({current: i + 1,total: chunks.length,processed: results.length,totalAddresses: addresses.length});}// Process chunk met retryconst chunkResults = await this.processChunkWithRetry(chunk);results.push(...chunkResults);}return results;}async processChunkWithRetry(addresses) {let lastError;for (let attempt = 1; attempt <= this.maxRetries; attempt++) {try {return await this.processChunk(addresses);} catch (error) {lastError = error;if (error.status === 429) {const waitTime = this.delayBetweenRequests * attempt * 10;console.log(`Rate limited, wacht ${waitTime}ms...`);await this.sleep(waitTime);} else if (attempt < this.maxRetries) {await this.sleep(1000 * attempt);}}}throw lastError;}async processChunk(addresses) {const results = [];for (const address of addresses) {const token = await this.getToken();const query = encodeURIComponent(address);const response = await fetch(`https://api.georex.nl/geocode?q=${query}&token=${token}`);if (!response.ok) {const error = new Error(`Request failed: ${response.status}`);error.status = response.status;throw error;}const data = await response.json();if (data.features?.length > 0) {const [lon, lat] = data.features[0].geometry.coordinates;results.push({address,status: 'found',lat, lon,...data.features[0].properties});} else {results.push({ address, status: 'not_found' });}await this.sleep(this.delayBetweenRequests);}return results;}chunkArray(array, size) {const chunks = [];for (let i = 0; i < array.length; i += size) {chunks.push(array.slice(i, i + size));}return chunks;}sleep(ms) {return new Promise(resolve => setTimeout(resolve, ms));}}// Gebruikconst geocoder = new BulkGeocoder('gx_live_your_key');const addresses = ['Damrak 1, Amsterdam','Coolsingel 40, Rotterdam',// ... nog duizenden adressen];const results = await geocoder.processAll(addresses, (progress) => {console.log(`Chunk ${progress.current}/${progress.total} (${progress.processed} adressen verwerkt)`);});console.log(`Klaar! ${results.length} adressen geocodeerd.`);
Parallelle verwerking
Voor nog snellere verwerking kun je meerdere batches parallel uitvoeren (mits je rate limit dit toestaat):
const processParallel = async (addresses, concurrency = 5) => {const chunks = chunkArray(addresses, 100);const results = [];// Verwerk chunks in groepen van 'concurrency'for (let i = 0; i < chunks.length; i += concurrency) {const batch = chunks.slice(i, i + concurrency);const batchResults = await Promise.all(batch.map((chunk, index) =>processBatch(chunk, i + index)));results.push(...batchResults.flat());// Kleine pauze tussen parallelle groepenawait sleep(500);}return results;};
Let op je rate limits
Parallelle verwerking kan snel je rate limit bereiken. Check je plan voor de maximale requests per seconde en pas de concurrency daarop aan.
Resultaten verwerken
Na batch geocoding moet je de resultaten analyseren en eventuele fouten afhandelen:
const analyzeResults = (results) => {const stats = {total: results.length,found: 0,notFound: 0,errors: 0,lowConfidence: 0};const successful = [];const failed = [];const needsReview = [];for (const result of results) {if (result.status === 'found') {stats.found++;if (result.confidence < 0.7) {stats.lowConfidence++;needsReview.push(result);} else {successful.push(result);}} else if (result.status === 'not_found') {stats.notFound++;failed.push(result);} else {stats.errors++;failed.push(result);}}return {stats,successful,failed,needsReview,successRate: ((stats.found / stats.total) * 100).toFixed(1) + '%'};};// Gebruikconst analysis = analyzeResults(results);console.log(`Success rate: ${analysis.successRate}`);console.log(`Needs review: ${analysis.needsReview.length} adressen`);
Best practices voor batch geocoding
- Valideer input vooraf - Filter ongeldige adressen vóór verzending
- Gebruik eigen IDs - Koppel resultaten terug aan je brondata
- Implementeer retry logic - Netwerk kan falen, rate limits kunnen optreden
- Log voortgang - Bij grote datasets wil je weten waar je bent
- Bewaar failed records - Voor handmatige verwerking achteraf
- Batch async wanneer mogelijk - Gebruik workers of queue systems
Samenvatting
Efficiënte batch geocoding combineert de juiste batch sizes, rate limiting, error handling en parallellisatie. Met de patronen in dit artikel kun je datasets van elke grootte verwerken terwijl je binnen je API limits blijft en robuust omgaat met fouten.
Gerelateerde artikelen

Hoe werkt geocoding onder de motorkap?
Een technische deep-dive in de werking van geocoding: van adres parsing tot coördinaat matching. Begrijp wat er gebeurt wanneer je een adres omzet.
Lees meer
Nauwkeurigheid in geocoding: wat betekent het echt?
Wat betekent een confidence score van 0.95? Leer over precisieniveaus, wanneer resultaten betrouwbaar zijn, en hoe je kwaliteit meet.
Lees meer
Privacy-first geocoding: waarom het belangrijk is
Locatiedata is gevoelige data. Leer waarom privacy-first geocoding essentieel is voor GDPR-compliance en hoe GeoRex hiermee omgaat.
Lees meer