Incrementeel ophalen

Bijgewerkt op 7 oktober 2026 · Gepubliceerd op 7 oktober 2026

Waarom incrementeel

Bij een grote tabel kost een volledige run veel aanvragen. Met modified_since vraag je alleen de rijen op die zijn toegevoegd of gewijzigd sinds een tijdstip, inclusief rijen die bij de bron zijn verwijderd.

Zo werkt het

  1. Eerste run: haal de tabel volledig op, zonder modified_since. Loop door alle pagina’s tot next_cursor null is.
  2. Bewaar sync_watermark uit de laatste pagina. Dat is het tijdstip waarop die run begon.
  3. Volgende run: vraag aan met ?modified_since=<sync_watermark>, ook weer door alle pagina’s.
  4. Voeg samen: vervang in je eigen opslag rijen met hetzelfde id door de nieuwe versie en bewaar het nieuwe sync_watermark.

Rechts staat een voorbeeld, inclusief een Python-script dat het watermark tussen runs bewaart.

modified_since is een tijdstip in ISO 8601. Een ongeldig tijdstip geeft 400 invalid_parameter.

Verwijderde rijen

Bij een aanvraag met modified_since krijg je ook rijen terug die bij de bron zijn verwijderd. Je herkent ze aan een gevulde kolom bron_verwijderd_op; haal ze in je eigen opslag weg. Dit geldt voor tabellen met die kolom.

  • include_deleted=true neemt verwijderde rijen ook mee zonder modified_since.
  • include_deleted=false samen met modified_since geeft 400 invalid_parameter, want dan zou je verwijderingen missen.
  • Zonder modified_since en zonder include_deleted krijg je verwijderde rijen niet.
  • Rijen van kind-tabellen waarvan de bovenliggende rij is verwijderd, tellen zonder include_deleted ook als verwijderd en komen niet mee.

Overlap en dubbele rijen

De API neemt een marge van 5 minuten vóór modified_since mee, zodat een rij die tijdens je vorige run werd bijgewerkt niet wegvalt. Daardoor kan dezelfde rij in twee runs terugkomen. Verwerk je data daarom altijd per id: een rij die er al is, overschrijf je.

Niet elke tabel ondersteunt het

Alleen tabellen die dit aangeven op hun referentiepagina ondersteunen modified_since. Op andere tabellen geeft de parameter 400 incremental_not_supported. Haal die tabellen bij elke run volledig op.

Onvolledig gesynchroniseerde administraties

Staat in connections een administratie met backfill_complete: false, dan is de eerste synchronisatie nog bezig. Je aanvraag werkt wel, maar de data kan onvolledig zijn. Haal de tabel opnieuw volledig op zodra de synchronisatie klaar is.

Onze tip: bewaar het watermark pas nadat je alle pagina’s succesvol hebt verwerkt. Gaat een run halverwege mis, dan begin je de volgende keer gewoon opnieuw vanaf het oude watermark.

Aanvraag

GET https://app.apiboard.nl/api/v1/wefact/facturen?modified_since=2026-10-07T03%3A00%3A00.000000Z HTTP/1.1
Host: app.apiboard.nl
Authorization: Bearer apib_live_JOUW_SLEUTEL

# Gepagineerd: herhaal met &cursor=<next_cursor> tot next_cursor null is.
curl -H "Authorization: Bearer apib_live_JOUW_SLEUTEL" \
  "https://app.apiboard.nl/api/v1/wefact/facturen?modified_since=2026-10-07T03%3A00%3A00.000000Z"

# Gepagineerd: herhaal met &cursor=<next_cursor> tot next_cursor null is.
import json
import os
import requests

TOKEN = "apib_live_JOUW_SLEUTEL"
URL = "https://app.apiboard.nl/api/v1/wefact/facturen"
STAAT_BESTAND = "wefact_facturen_staat.json"  # bewaart rijen + watermark tussen runs

staat = {"watermark": None, "rijen": {}}
if os.path.exists(STAAT_BESTAND):
    with open(STAAT_BESTAND) as f:
        staat = json.load(f)

params = {}
if staat["watermark"]:
    params["modified_since"] = staat["watermark"]  # eerste run: zonder, dus de hele tabel

while True:
    resp = requests.get(URL, params=params, headers={"Authorization": f"Bearer {TOKEN}"})
    resp.raise_for_status()
    body = resp.json()
    for rij in body["data"]:
        staat["rijen"][rij["id"]] = rij  # dedupliceren op id, laatste versie wint
    if body["next_cursor"] is None:
        staat["watermark"] = body["sync_watermark"]  # pas bewaren na de laatste pagina
        break
    params["cursor"] = body["next_cursor"]

# Rijen die bij de bron verwijderd zijn, uit de lokale set halen.
staat["rijen"] = {i: r for i, r in staat["rijen"].items() if not r.get("bron_verwijderd_op")}

with open(STAAT_BESTAND, "w") as f:
    json.dump(staat, f)

print(f"{len(staat['rijen'])} rijen.")
let
    Basis = "https://app.apiboard.nl",
    Pad = "/api/v1/wefact/facturen",
    Token = "apib_live_JOUW_SLEUTEL",
    Filters = [modified_since = "2026-10-07T03:00:00.000000Z"],

    HaalPaginaOp = (cursor as nullable text) =>
        Json.Document(Web.Contents(Basis, [
            RelativePath = Pad,
            Query = if cursor = null then Filters else Record.AddField(Filters, "cursor", cursor),
            Headers = [Authorization = "Bearer " & Token]
        ])),

    // Volg next_cursor tot die null is; de pagina met next_cursor = null telt ook mee.
    Paginas = List.Generate(
        () => HaalPaginaOp(null),
        each _ <> null,
        each if [next_cursor] = null then null else HaalPaginaOp([next_cursor]),
        each [data]
    ),

    AlleRijen = List.Combine(Paginas),
    Tabel = if List.IsEmpty(AlleRijen)
        then #table({"id", "synced_at"}, {})
        else Table.FromRecords(AlleRijen, null, MissingField.UseNull),
    // Dedupliceer op id: eerst de nieuwste versie bovenaan, dan houdt Distinct die.
    // Verwijder daarna rijen die bij de bron verwijderd zijn.
    NieuwsteEerst = Table.Buffer(Table.Sort(Tabel, {{"synced_at", Order.Descending}})),
    ZonderDubbel = Table.Distinct(NieuwsteEerst, {"id"}),
    ZonderVerwijderd = Table.SelectRows(ZonderDubbel, each Record.FieldOrDefault(_, "bron_verwijderd_op", null) = null)
in
    ZonderVerwijderd

Antwoord

{
  "connections": [
    {
      "id": "43d9a41a-3529-4be2-b891-b23781997895",
      "label": "Voorbeeld B.V.",
      "backfill_complete": true
    }
  ],
  "data": [
    {
      "connection_id": "43d9a41a-3529-4be2-b891-b23781997895",
      "connection_label": "Voorbeeld B.V.",
      "id": "314feb6e-f809-4f72-b6d8-da1b68d15ab5",
      "synced_at": "2026-10-07T02:58:12.304211+00:00"
    }
  ],
  "next_cursor": null,
  "sync_watermark": "2026-10-08T03:00:00.000000Z"
}

Was dit artikel behulpzaam?