Storekeeper API
One clean, token-authenticated REST surface over Storekeeper. Read your orders,
catalog, stock, customers, and accounting reports without touching the low-level
Storekeeper protocols.
Open the interactive reference
OpenAPI spec
What is Storekeeper?
Storekeeper is a commerce platform for retail and hospitality: a point of
sale (POS), a webshop, a back office, inventory and stock control, customer management, and
invoicing, all on one account. Shops, bakeries, delis, and multi-location franchises use it
to run day-to-day trading.
Storekeeper runs the entire retail stack for a business. This API is how you read and work
with that account's data from the outside, over plain HTTPS and JSON, on your own schedule.
What is this API?
api-dev.storekeeper.software is the public API of Storekeeper: the
supported, recommended, and only permitted way to connect to a Storekeeper account
programmatically. No other integration method is allowed. It authenticates you once, then
returns flat, predictable JSON. It holds no database of its own: your bearer token
is an encrypted blob of your Storekeeper credentials, so every call acts as you, on
your account.
Your subscription must cover both the number of calls you make and the scope you access.
Staying within those limits keeps your integration supported.
Use cases
Accounting and BI
Financial report, VAT breakdown, payment reconciliation, daily close (Z-report),
and best sellers. Feed into AFAS, Exact, or a dashboard.
Order operations
List and search orders by date, shop, and status. Read a single order with its
addresses and line items.
Catalog and inventory
Search the catalog, read a product, pull price rows, and check per-location stock
for sync or availability checks.
CRM
Search customers, read a customer with contact details, and list customer segments
for marketing or pricing.
Getting started
Every endpoint (except auth and these docs) needs a bearer token. Three steps:
1. Get a token
There is no password endpoint. Ask your Storekeeper administrator for an API key: they
create one in the Storekeeper app and hand you a client_id and a
client_secret. You exchange those for a short-lived access token at your own
account's OAuth endpoint — client_credentials, RFC 6749 §4.4, which every
stock OAuth client library already speaks. That exchange happens at Storekeeper,
not here: this API never sees or brokers your secret.
# where your token comes from — one call, repeatable, nothing consumed
curl -s -X POST https://api-$ACCOUNT.storekeepercloud.com/oauth/token \
-u "$SK_CLIENT_ID:$SK_CLIENT_SECRET" \
-d grant_type=client_credentials
# { "access_token": "...", "token_type": "Bearer", "expires_in": 900 }
2. Call an endpoint
TOKEN="..." # the token from step 1
curl -s "https://api-dev.storekeeper.software/api/orders?from=2026-07-01&to=2026-07-22&limit=25" \
-H "Authorization: Bearer $TOKEN"
3. Know who you are
curl -s https://api-dev.storekeeper.software/api/me -H "Authorization: Bearer $TOKEN"
# returns account, token expiry, user identity, roles, locked location
Access tokens are short-lived — trust the expires_in you were given, not a
constant. There is no renew endpoint here. A browser session lasts one
token: when it runs out, open the app from the backoffice again — and note that a
browser session's token is not a credential for this API at all, because
/api takes only a token minted from an API key and answers a session's
with a 401.
POST /logout revokes both tokens and clears the cookie before then.
Server-to-server integrations do not use a browser session at all. Ask
your Storekeeper administrator for an API key and exchange it for a token at your own
account's OAuth token endpoint — client_credentials, RFC 6749 §4.4, which
every stock OAuth client already speaks. That exchange happens at Storekeeper, not here:
this API never brokers your credential.
Conventions
- Dates are
YYYY-MM-DD in the Europe/Amsterdam timezone. Ranges are inclusive.
- Money is decimal euros.
_wt is incl. VAT (Dutch met). Plain or _ex is excl. VAT.
- Lists return
{count, total, data}. Paginated lists add {start, limit}, so you drive paging (pass start and limit). A truncated flag appears where a hard ceiling was hit.
- Errors are JSON
{error, message, status}: 400 bad input, 401 auth, 404 not found, 429 over the rate limit, 502 upstream Storekeeper error.
Limits
Every /api call authenticates with an access token minted from an API key — a
browser session's token is refused with a 401 that names the fix — and that key
is what your request volume is counted against.
- The ceiling is per API key, per minute. One key is for one integration:
sharing a key across a fleet of workers spends one budget between them, and adding
workers on the same key makes you slower, not faster.
- Writes are limited separately and more tightly than reads. A
POST, PUT, PATCH or DELETE is charged
against both the write counter and the general one.
- Past a ceiling the answer is
429 Too Many Requests with
Retry-After in seconds. Wait that long and continue; retrying hot spends the
next window on refusals too. See the error catalog.
- Every authenticated response carries the numbers —
RateLimit-Limit, RateLimit-Remaining and
RateLimit-Reset (seconds until the window rolls). Read
RateLimit-Remaining and slow down before it reaches zero.
The three always describe one ceiling: the one closest to refusing you
next. On a write that is usually the write ceiling, so the number drops when you
write and rises again when you read — it steps between two ceilings rather than
counting down as one budget. Treat each response's trio on its own; do not infer
your rate from the difference between two of them.
- Two further headers name the ceiling those numbers belong to.
RateLimit-Policy lists every ceiling that applies to your
key, each one named, with its quota (q) and its window in seconds
(w). RateLimit reports the state of exactly one of them —
the one closest to refusing you next — and says which, with r for
what is left and t for the seconds until it resets. There are two
names, general and write.
- Read them this way.
RateLimit-Policy is built from
configuration alone, so it is the same on every authenticated response: a
GET advertises the write ceiling too, which is how you find out a
tighter one exists before your first write rather than on it. r and
t are the same two numbers as RateLimit-Remaining and
RateLimit-Reset — what RateLimit adds is the name, which
is what makes the step above self-describing instead of looking like the server
contradicting itself. On a 429 it names the ceiling that refused,
with r=0. All five ride on every authenticated response, so use
whichever pair or trio your HTTP client already understands.
No figure is published here on purpose. The ceilings are deployment
configuration, so a number printed on this page would be wrong the day one is tuned. The
trade is real and worth stating: sizing a nightly sync means reading
RateLimit-Limit off one call rather than copying a rate out of the
documentation.
Resolving ids
Reports return raw Storekeeper ids. Resolve them to names with the reference endpoints:
| id emitted by reports | resolver endpoint |
shop_id | GET /api/shops |
tax_rate_id | GET /api/tax-rates |
product_group_id | GET /api/turnover-groups |
provider_method_type_id | GET /api/payment-methods |
location_id | GET /api/locations |
Endpoint catalog
Orders
GET/api/orders
GET/api/orders/{id}
GET/api/orders/{id}/items
GET/api/discount-orders
Catalog & stock
GET/api/products
GET/api/products/{id}
GET/api/product-prices
GET/api/stock
Customers
GET/api/customers
GET/api/customers/{id}
GET/api/customer-segments
Reports
GET/api/financial-report
GET/api/reports/payments
GET/api/reports/daily-close
GET/api/reports/product-sales
Reference data
GET/api/shops
GET/api/locations
GET/api/tax-rates
GET/api/turnover-groups
GET/api/payment-methods
Try it live in the interactive reference
Storekeeper API
Een strak, token-geverifieerd REST-oppervlak over Storekeeper. Lees je orders,
assortiment, voorraad, klanten en boekhoudrapporten zonder de low-level
Storekeeper-protocollen aan te raken.
Open de interactieve referentie
OpenAPI-spec
Wat is Storekeeper?
Storekeeper is een commerceplatform voor retail en horeca: een kassa
(POS), een webshop, een backoffice, voorraadbeheer, klantbeheer en facturatie, allemaal op
een account. Winkels, bakkerijen, delicatessenzaken en franchises met meerdere locaties
gebruiken het om hun dagelijkse verkoop te draaien.
Storekeeper draait de volledige retailstack voor een onderneming. Deze API is hoe je de data
van dat account van buitenaf leest en bewerkt, via gewone HTTPS en JSON, op je eigen moment.
Wat is deze API?
api-dev.storekeeper.software is de publieke API van Storekeeper: de
ondersteunde, aanbevolen en enige toegestane manier om programmatisch met een
Storekeeper-account te verbinden. Geen enkele andere integratiemethode is toegestaan. Je
verifieert een keer, daarna geeft hij platte, voorspelbare JSON terug. Hij heeft geen eigen
database: je bearer-token is een versleutelde blob van je Storekeeper-inloggegevens,
dus elke aanroep handelt als jou, op jouw account.
Je abonnement moet zowel het aantal aanroepen dat je doet als de scope die je gebruikt
toestaan. Binnen die limieten blijven houdt je integratie ondersteund.
Toepassingen
Boekhouding en BI
Financieel rapport, btw-uitsplitsing, betalingsreconciliatie, dagafsluiting
(Z-rapport) en bestsellers. Koppel aan AFAS, Exact of een dashboard.
Orderbeheer
Orders lijsten en zoeken op datum, winkel en status. Lees een enkele order met
adressen en orderregels.
Assortiment en voorraad
Doorzoek het assortiment, lees een product, haal prijsregels op en controleer
voorraad per locatie voor sync of beschikbaarheid.
CRM
Zoek klanten, lees een klant met contactgegevens en lijst klantsegmenten voor
marketing of prijsstelling.
Aan de slag
Elk endpoint (behalve auth en deze documentatie) heeft een bearer-token nodig. Drie stappen:
1. Een token halen
Een wachtwoord-endpoint is er niet. Vraag je Storekeeper-beheerder om een API-sleutel:
die maakt er een aan in de Storekeeper-app en geeft je een client_id en een
client_secret. Die wissel je in voor een kortlevend access token bij het
OAuth-endpoint van je eigen account — client_credentials, RFC 6749 §4.4, dat
elke standaard OAuth-clientbibliotheek al spreekt. Die uitwisseling gebeurt bij
Storekeeper, niet hier: deze API ziet je secret nooit en bemiddelt er nooit in.
# hier komt je token vandaan — één aanroep, herhaalbaar, niets verbruikt
curl -s -X POST https://api-$ACCOUNT.storekeepercloud.com/oauth/token \
-u "$SK_CLIENT_ID:$SK_CLIENT_SECRET" \
-d grant_type=client_credentials
# { "access_token": "...", "token_type": "Bearer", "expires_in": 900 }
2. Een endpoint aanroepen
TOKEN="..." # het token uit stap 1
curl -s "https://api-dev.storekeeper.software/api/orders?from=2026-07-01&to=2026-07-22&limit=25" \
-H "Authorization: Bearer $TOKEN"
3. Weten wie je bent
curl -s https://api-dev.storekeeper.software/api/me -H "Authorization: Bearer $TOKEN"
# geeft account, tokenvervaltijd, gebruikersidentiteit, rollen, vaste locatie
Access tokens zijn kortlevend — vertrouw op de expires_in die je kreeg, niet
op een constante. Er is hier geen ververs-endpoint. Een browsersessie
duurt één token: is die op, open de app dan opnieuw vanuit de backoffice — en let op:
het token van een browsersessie is sowieso geen credential voor deze API, want
/api neemt alleen een token dat uit een API-sleutel is aangemaakt en
antwoordt op dat van een sessie met een 401.
POST /logout trekt beide tokens in en wist de cookie.
Server-naar-server-integraties gebruiken helemaal geen browsersessie.
Vraag je Storekeeper-beheerder om een API-sleutel en wissel die bij het OAuth
token-endpoint van je eigen account in voor een token — client_credentials,
RFC 6749 §4.4, dat elke standaard OAuth-client al spreekt. Die uitwisseling gebeurt bij
Storekeeper, niet hier: deze API bemiddelt nooit in je credential.
Conventies
- Datums zijn
JJJJ-MM-DD in de tijdzone Europe/Amsterdam. Bereiken zijn inclusief.
- Bedragen zijn decimale euro's.
_wt is incl. btw (met). Zonder of _ex is excl. btw.
- Lijsten geven
{count, total, data} terug. Gepagineerde lijsten voegen {start, limit} toe, dus jij bepaalt de paginering (geef start en limit mee). Een truncated-vlag verschijnt waar een harde limiet is bereikt.
- Fouten zijn JSON
{error, message, status}: 400 foute invoer, 401 auth, 404 niet gevonden, 429 boven de limiet, 502 fout bij Storekeeper.
Limieten
Elke /api-aanroep authenticeert met een access token dat uit een API-sleutel is
aangemaakt — het token van een browsersessie wordt geweigerd met een 401 die de
oplossing noemt — en op die sleutel wordt je verzoekvolume geteld.
- De limiet geldt per API-sleutel, per minuut. Eén sleutel is voor één
koppeling: een sleutel delen over een vloot workers betekent één budget samen opmaken,
en workers bijzetten op dezelfde sleutel maakt je langzamer, niet sneller.
- Schrijfacties zijn apart en strenger begrensd dan leesacties. Een
POST, PUT, PATCH of DELETE wordt
zowel op de schrijfteller als op de algemene teller afgeschreven.
- Boven een plafond is het antwoord
429 Too Many Requests met
Retry-After in seconden. Wacht die tijd af en ga verder; heet herhalen
verbruikt ook het volgende venster aan weigeringen. Zie de
foutencatalogus.
- Elk geauthenticeerd antwoord draagt de getallen —
RateLimit-Limit, RateLimit-Remaining en
RateLimit-Reset (seconden tot het venster rolt). Lees
RateLimit-Remaining en schakel terug voordat hij nul bereikt.
De drie beschrijven altijd één plafond: het plafond dat je als eerste
gaat weigeren. Bij een schrijfactie is dat meestal het schrijfplafond, dus het
getal zakt als je schrijft en loopt weer op als je leest — het stapt tussen twee
plafonds in plaats van als één budget af te tellen. Neem de drie per antwoord
zoals ze zijn; leid je tempo niet af uit het verschil tussen twee antwoorden.
- Twee extra headers noemen bij welk plafond die getallen horen.
RateLimit-Policy somt elk plafond op dat voor jouw sleutel
geldt, elk met een naam, een quotum (q) en een venster in seconden
(w). RateLimit geeft de stand van precies één daarvan —
het plafond dat je als eerste gaat weigeren — en zegt welk, met r
voor wat er over is en t voor de seconden tot het opnieuw begint. Er
zijn twee namen, general en write.
- Zo lees je ze.
RateLimit-Policy wordt alleen uit de
configuratie opgebouwd en is dus op elk geauthenticeerd antwoord hetzelfde: ook
een GET kondigt het schrijfplafond aan, en zo kom je erachter dat er
een strenger plafond bestaat vóór je eerste schrijfactie in plaats van erop.
r en t zijn dezelfde twee getallen als
RateLimit-Remaining en RateLimit-Reset — wat
RateLimit toevoegt is de naam, en die maakt de stap hierboven
zelfverklarend in plaats van dat het lijkt of de server zichzelf tegenspreekt.
Bij een 429 noemt hij het plafond dat weigerde, met r=0.
Alle vijf zitten op elk geauthenticeerd antwoord, dus gebruik het drietal of het
paar dat je HTTP-client al kent.
Hier staat bewust geen getal. De plafonds zijn deployment-configuratie, dus
een getal op deze pagina zou fout zijn op de dag dat er één wordt bijgesteld. De keerzijde is
echt en verdient het om genoemd te worden: een nachtelijke sync maat je door
RateLimit-Limit van één aanroep af te lezen, niet door een tempo uit de
documentatie over te nemen.
Ids vertalen
Rapporten geven ruwe Storekeeper-ids terug. Vertaal ze naar namen met de referentie-endpoints:
| id uit rapporten | referentie-endpoint |
shop_id | GET /api/shops |
tax_rate_id | GET /api/tax-rates |
product_group_id | GET /api/turnover-groups |
provider_method_type_id | GET /api/payment-methods |
location_id | GET /api/locations |
Endpoint-overzicht
Orders
GET/api/orders
GET/api/orders/{id}
GET/api/orders/{id}/items
GET/api/discount-orders
Catalog & stock
GET/api/products
GET/api/products/{id}
GET/api/product-prices
GET/api/stock
Customers
GET/api/customers
GET/api/customers/{id}
GET/api/customer-segments
Reports
GET/api/financial-report
GET/api/reports/payments
GET/api/reports/daily-close
GET/api/reports/product-sales
Reference data
GET/api/shops
GET/api/locations
GET/api/tax-rates
GET/api/turnover-groups
GET/api/payment-methods
Probeer het live in de interactieve referentie