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
- Eerste run: haal de tabel volledig op, zonder
modified_since. Loop door alle pagina’s totnext_cursornullis. - Bewaar
sync_watermarkuit de laatste pagina. Dat is het tijdstip waarop die run begon. - Volgende run: vraag aan met
?modified_since=<sync_watermark>, ook weer door alle pagina’s. - Voeg samen: vervang in je eigen opslag rijen met hetzelfde
iddoor de nieuwe versie en bewaar het nieuwesync_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=trueneemt verwijderde rijen ook mee zondermodified_since.include_deleted=falsesamen metmodified_sincegeeft400 invalid_parameter, want dan zou je verwijderingen missen.- Zonder
modified_sinceen zonderinclude_deletedkrijg je verwijderde rijen niet. - Rijen van kind-tabellen waarvan de bovenliggende rij is verwijderd, tellen zonder
include_deletedook 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?