Exporting reservations via the REST API
How to authenticate against the ZEMTU REST API and export reservation data — including filtering, pagination, and a ready-to-use export script.
Written By Zemtu
Last updated About 11 hours ago
Introduction
This article is aimed at developers who want to make use of the ZEMTU REST API. It explains authentication, pagination, filtering, and ordering — using the export of reservation data as a complete, working example. The same pattern applies to all other endpoints (vehicles, users, invoices, etc.).
General info
Base URL: We use
https://carsharing.zemtu.comin this documentation. Always replacecarsharingwith your subdomain:https://YOUR_ORGANIZATION.zemtu.comSerialization format: JSON is used for data exchange.
Versioning:
/api/v2/— new API versions are offered under a distinct version number to allow backward-incompatible API evolution.Methods: HTTP GET is used for fetching data, POST for creating, PUT/PATCH for updating and DELETE for deleting data.
Documentation: The auto-generated, interactive API documentation with all available endpoints, fields, and filters can be accessed at
https://YOUR_ORGANIZATION.zemtu.com/docs
Authentication
TokenAuthentication: for clients to authenticate, the token key should be included in the Authorization HTTP header. The key should be prefixed by the string literal Token, with whitespace separating the two strings:
Authorization: Token 9944b19199c62bgf9418td846dd0e4bzdfc6de9bThere are several possibilities to obtain the token:
Read token from admin interface (only for users with org admin role):
https://YOUR_ORGANIZATION.zemtu.com/admin/authtoken/token/Get direct access to the token, e.g. via secure email from your organization administrator.
Obtain the token with username & password by posting the credentials to
/api-token-auth/:
curl --data "username=USERNAME&password=PASSWORD" https://YOUR_ORGANIZATION.zemtu.com/api-token-auth/The JSON response looks like {"token": "9844b19129c62bhf9418td746cd0e4bzdfc6de9b"} — the token is then used for all API communication.
Setup recommendation
Best practice is to create a dedicated machine user at https://YOUR_ORGANIZATION.zemtu.com/admin/main/customuser/ and get a token for it via https://YOUR_ORGANIZATION.zemtu.com/admin/authtoken/token/?user=PRIMARY_KEY_OF_THE_USER.
The rule of thumb for permissions: the API user sees the same objects via the API as in the backend administration. To export all reservations of an organization, the machine user needs to be assigned to the organization with a suitable role, e.g. "Accountant" or "OrgAdmin".
Pagination
All list endpoints return a paginated envelope:
{"count": 1234, "next": "https://...&page=2", "previous": null, "results": [ ... ]}/api/v2/reservation/— default page size: 100, maximum: 500/api/v2/reservationaccounting/and/api/v2/reservationreporting/— default page size: 50, maximum: 250A custom page size can be set with
?page_size=250; a specific page with&page=2. The simplest export strategy is to follow thenextURL until it isnull.
Endpoints for exporting reservations
Hint: the trailing / is important for detail endpoints, e.g. /api/v2/reservation/{ID}/.
Filtering
The reservation and reservation accounting endpoints support these filters:
Hint: gte stands for "greater than or equal to", lte for "less than or equal to". Datetimes are given in ISO 8601 format, e.g. 2026-01-01T00:00:00Z.
/api/v2/reservationreporting/ additionally offers start__gte/__lte (reservation start) and billed_end__gte/__lte (the end relevant for billing).
Filtering examples
All reservations starting in January 2026:
https://YOUR_ORGANIZATION.zemtu.com/api/v2/reservationaccounting/?reservation_start__gte=2026-01-01T00:00:00Z&reservation_start__lt=2026-02-01T00:00:00ZClosed or deleted reservations:
?state__in=closed,deletedReservations of vehicle 201:
?vehicle=201Reservations owned by user 1550:
?owner=1550Reservations changed since the last export run:
?modified__gte=2026-10-01T00:00:00Z
Ordering
Results can be sorted with the ordering parameter, e.g. ?ordering=-reservation_start for descending order by reservation start. Available ordering fields are created, reservation_start, reservation_end, and vehicle (reservation API); reservation_start (accounting and reporting APIs).
Result structure (reservation accounting)
Example result
"note": "",
"reservation_start": "2014-09-04T05:30:00Z",
"start_real": null,
"reservation_end": "2014-09-05T12:30:00Z",
"end_real": null,
"note_billing": null,
"purpose": 0,
"owner": {
"id": 2275,
"external_user_id": ""
},
"vehicle": {
"id": 201,
"type": "ZEMTU Renault Twizy",
"address1": "Färbergasse 17b",
"city": "Dornbirn",
"zip": "6850"
},
"created": "2014-09-02T18:02:48.740223Z",
"modified": "2014-09-02T18:03:00.274824Z",
"distance": null,
"lift_offer": false,
"state": "deleted",
"versions": [
{
"lift_offer": false,
"state": "deleted",
"modified": "2014-09-02T18:03:00.274Z",
"note": "",
"reservation_start": "2014-09-04T05:30:00Z",
"purpose": 0,
"reservation_end": "2014-09-05T12:30:00Z"
},
{
"lift_offer": false,
"state": "opened",
"modified": "2014-09-02T18:02:56.804Z",
"note": "",
"reservation_start": "2014-09-04T05:30:00Z",
"purpose": 0,
"reservation_end": "2014-09-05T12:30:00Z"
},
{
"lift_offer": false,
"state": "opened",
"modified": "2014-09-02T18:02:48.740Z",
"note": "",
"reservation_start": "2014-09-04T05:30:00Z",
"purpose": 0,
"reservation_end": "2014-09-05T09:30:00Z"
}
],
"odometer_start": null,
"odometer_end": nullExample: exporting reservations
cURL
curl -H "Authorization: Token XXXX" \ "https://YOUR_ORGANIZATION.zemtu.com/api/v2/reservationaccounting/?format=json&page_size=250"Python 3 — export all reservations of a month to CSV
#!/usr/bin/env python3
import os
import sys
import urllib.request
import urllib.error
# Use the provided demo token; in real use, prefer: TOKEN = os.environ.get("DRF_TOKEN")
TOKEN = "XXXX"
def main():
url = "https://carsharing.zemtu.com/api/v2/reservationaccounting/?format=json"
# url = "https://carsharing.zemtu.com/api/billing/v2/invoice/?format=json"
data = None
method = "GET"
headers = {
"Authorization": f"Token {TOKEN}",
"Accept": "application/json",
}
if data:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, method=method, headers=headers)
try:
with urllib.request.urlopen(req) as resp:
body = resp.read().decode("utf-8", errors="replace")
print(body)
sys.exit(0)
except urllib.error.HTTPError as e:
err = e.read().decode("utf-8", errors="replace")
print(f"HTTP {e.code}: {err}", file=sys.stderr)
sys.exit(e.code)
except urllib.error.URLError as e:
print(f"Request error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Usage hints
The field
reservation_endis the end displayed in the user interface only. The fieldsend_bookedandend_realare the ones to consider for clearing.When
end_real>end_bookedthe reservation was overdrawn — extra fees might apply.The field
versionscontains the whole history of a reservation. Loop over it to detect whether it was deleted or changed in a time span where extra fees might apply.Deleted reservations stay in the result set — use
state=deletedorstate__in=...to handle them explicitly.For recurring exports, filter on
modified__gtewith the timestamp of the last run instead of re-fetching everything.
Reservation states
Further reading
Interactive API documentation (all endpoints, fields and filters):
https://YOUR_ORGANIZATION.zemtu.com/docsTokenAuthentication: https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication