Guides
The REST API
The JSON endpoints every model gets - listing, searching, filtering, sorting, creating, changing, deleting, and errors.
Everything in the admin can also be done by a program, over HTTP with JSON. Every model gets
these endpoints under /api, with the model's table name in the path:
| Request | What it does |
|---|---|
GET /api/post | list posts |
POST /api/post | create one; answers 201 with the new record and a Location header |
GET /api/post/1 | read one |
PUT /api/post/1 | replace one: every required field must be sent |
PATCH /api/post/1 | change only the fields sent |
DELETE /api/post/1 | delete one; answers 204 |
Every request needs a login token or an API key; see Logging in. The API follows the same permissions as the admin.
#Listing
curl -H "Authorization: Bearer $TOKEN" "http://localhost:3000/api/post?q=rust&sort=-id&page=2&per_page=50"
{
"data": [{ "id": 12, "title": "Rust tips", "published": true, "views": 40 }],
"meta": { "total": 31, "page": 2, "per_page": 50 }
}
| Parameter | Meaning |
|---|---|
q | search in the model's search_fields (ignores case) |
sort | a field to sort by; - in front for highest first (sort=-views) |
page | which page, from 1 |
per_page | how many per page, at most 100 |
#Filtering
Any visible field can be filtered on, with the same choices as the admin's filters:
| Example | Matches |
|---|---|
?published=true | an exact value |
?author_id=3&published=false | several at once (all must match) |
?views__gt=100, __gte, __lt, __lte | comparisons |
?created_at__gte=2026-01-01 | dates and date-times compare too |
?summary__isnull=true | empty (or false: not empty) |
?created_at__range=7d | today, 7d, 30d, month or year (UTC) |
An unknown field, or a value the field cannot hold, is refused with 422. A field named
page, per_page, q or sort cannot be filtered on.
#Creating and changing
curl -X POST http://localhost:3000/api/post \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"title": "Hello", "body": "First post", "published": false, "views": 0}'
- Dates and times are ISO 8601:
"2026-03-04","2026-03-04T05:06:07". - Links to other models are sent as the id:
"author_id": 3. - Fields that are unknown, hidden or read-only are refused rather than silently ignored.
PUTneeds every required field;PATCHchanges only what is sent.
#Errors
Errors come back as JSON:
{ "error": { "code": "invalid_field", "message": "title is required", "field": "title" } }
| Status | Code | Meaning |
|---|---|---|
400 | bad_request | the request is malformed |
401 | unauthorized | no valid token or key was sent |
403 | forbidden | the account may not do this |
404 | not_found | no such record or model |
409 | conflict | conflicts with other data: a duplicate, or a record others point to |
413 | too_large | an upload over the size limit |
422 | invalid_field | a field is missing, unknown, read-only or of the wrong type |
429 | rate_limited | too many login attempts; wait for the Retry-After seconds |
#Calling it from a browser app
A web app on another domain needs permission to call the API: set
server.cors_origins: vec!["https://app.example.com".into()] in the settings. Such apps
send a token or an API key; the admin's login cookie is never accepted by the API.
#Documentation
Every project documents its own API at /api/docs (Swagger UI), /api/redoc and
/api/openapi.json. See API documentation.