Introduction
Billey is a Dutch accounting product for freelancers, small companies and the firms that keep their books. This API gives your integration read and write access to the accounting data of one administration over a small, predictable REST surface at https://billey.nl/api/v1.
These pages are the guided tour. The exhaustive, field-by-field contract is the machine-readable OpenAPI spec.
One token = one book
Every request is authenticated with a single personal access token, and a token is bound to exactly one administration (book). There is no account-wide key, and no {book} segment in any URL — the token itself decides which book you are working in. An accounting firm that manages several client books issues one token per book.
This is structural, not a convention. A token can never read or write outside the book it was issued for, so an integration built for one client can never accidentally touch another. To work with three books, you hold three tokens.
Money is decimal strings
Every monetary amount is a JSON string with exactly two decimals — "1250.00" — never the number 1250 and never an integer count of cents. null means "no amount" and is distinct from "0.00". Decimal-cast fields (quantities, tax rates, hours) are strings too. This keeps money exact across every language's JSON parser. See Money & decimals.
Quickstart: fetch invoices in two minutes
1. Create a token. In Billey, open your book's Settings → API tokens and create one with the invoices:read ability. Creating a token requires two-factor authentication on your account. The token is shown once — copy it. It looks like 7|billey_xxxxxxxx.
2. Send it as a bearer token.
curl https://billey.nl/api/v1/invoices \
-H "Authorization: Bearer 7|billey_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
3. Read the response. A list is a cursor-paginated envelope — the rows are in data:
{
"data": [
{
"id": 42,
"invoice_number": "2026-0042",
"status": "sent",
"currency": "EUR",
"invoice_date": "2026-07-21",
"due_date": "2026-08-04",
"subtotal": "1033.06",
"btw_total": "216.94",
"total": "1250.00",
"client": { "id": 7, "name": "Example Client B.V." },
"line_items": [
{
"id": 1,
"description": "Consulting",
"quantity": "10.00",
"unit_price": "103.31",
"btw_percentage": "21.00",
"line_total": "1033.06"
}
]
}
],
"links": { "first": null, "last": null, "prev": null, "next": null },
"meta": { "path": "https://billey.nl/api/v1/invoices", "per_page": 25, "next_cursor": null, "prev_cursor": null }
}
That is the whole loop: authenticate, call, read data, follow links.next to page. Fetch a single invoice with GET /api/v1/invoices/42.
Conventions at a glance
- JSON everywhere. A single resource is
{ "data": { … } }; a list addslinksandmeta. - Timestamps are UTC ISO-8601 with a
Zsuffix (2026-07-21T10:15:30Z); date-only fields are plainY-m-d. - Lists use cursor pagination, with incremental sync (
updated_after) and sparse fieldsets (fields). See Pagination. - Creates are idempotent — every
POSTthat creates a resource takes anIdempotency-Keyheader. See Idempotency. - Rate limited to 60 requests/minute per token. See Rate limits.
What is not in the API
Stated up front so you never design around something that isn't there:
- No POS / point-of-sale endpoints, and a POS sale emits no webhook.
- No user-management endpoints — you cannot create or manage users or team members through the API.
- No
*.updatedor*.deletedwebhook events. Only six events fire; see Events.
Where to go next
- Authentication — the bearer header, and how abilities are checked.
- Tokens — the full ability catalogue and the read/write split.
- Errors — every error shape you can get back.
- Invoices and the other endpoints — request and response examples.
- Webhooks — receive events, and verify their signatures.