Lists API
Read the reading lists of any user of your store, and manage the publications they hold, from your own platform.
This is an administrator API: every endpoint operates on another user's data, identified by the target user parameters described below. There is no self-service mode.
Features
- Read any list of any user of your store, favorites and named lists, private ones included
- Read the publications a list holds, with the publica.la id, the ISBN and the reader URL of each one
- Add and remove publications in any list of a user, favorites included, addressed by internal id or by ISBN
- Idempotent writes: replaying a call never undoes what the previous one did
- Cursor-based pagination on every collection
Authentication
| Header | Example | Description |
|---|---|---|
| X-User-Token | api-abc123... | API token generated in your dashboard. Header-only for security. |
All requests must be performed over HTTPS. The token is not accepted as a query parameter or in the request body.
curl -X GET "https://yourstore.publica.la/api/v3/lists?user_external_id=student-1042" \
-H "X-User-Token: api-abc123..." \
-H "Accept: application/json"
The token must belong to an administrator
Unlike the rest of the v3 API, where the token itself is the authorization, the Lists API also checks the role of the user who owns the token. The token must belong to a store administrator, because these endpoints read and write other users' data.
A token that belongs to a regular user is rejected on every endpoint of this resource:
{
"message": "This token cannot operate on other users' lists."
}
The store also needs the integrations API enabled. If it is not, every endpoint of this resource answers:
{
"message": "This store cannot access the integrations API."
}
Both responses use status 403. Contact support if your store needs the integrations API enabled.
Identifying the target user
Every endpoint requires exactly one of these parameters. Send them in the query string on GET and DELETE, and in the JSON body on POST.
| Parameter | Type | Description |
|---|---|---|
user_external_id | string | Your own identifier for the user, the one you send as external_id when the account is provisioned |
user_email | string | Email address of the user in your store |
- Sending neither returns
422. - Sending both returns
422. - A user who does not exist in the store, or whose account was deactivated, returns
404withThe specified user does not exist in this store.
This API never provisions the user it was pointed at. An unknown identifier is an error, not an invitation to create the account. Use the Users API or the Auth Token integration to create accounts.
The user is required even when a list is addressed by its id: a list is only reachable through the user it belongs to, so an id that is not one of that user's lists is a 404.
Addressing a list
Every endpoint under /api/v3/lists/{list} accepts two forms of {list}, both resolved among the target user's lists:
| Form | Example | Description |
|---|---|---|
| Slug | favorites | The user's default list of that kind. No prior lookup needed |
| List id | 9f2d4b1e-6c3a-4f58-9b77-1d0a2e5c8b34 | Any list of the user, as returned by the list index |
Default lists have a fixed slug. The only one exposed today is favorites. Custom lists carry the name the user gave them, so they are addressed by id only.
# The favorites of a user, without knowing the list id
GET /api/v3/lists/favorites/items?user_external_id=student-1042
# A custom list of the same user, by id
GET /api/v3/lists/9f2d4b1e-6c3a-4f58-9b77-1d0a2e5c8b34/items?user_external_id=student-1042
An id that is not a list of the target user, a slug other than favorites, or a reserved storefront collection returns 404 with Not found.
Identifying the publication
The item write endpoints address a publication with content_id plus an optional id_type.
| Parameter | Type | Default | Description |
|---|---|---|---|
content_id | string | (required) | The publication identifier, read according to id_type |
id_type | string | internal | internal reads content_id as the publica.la id; external reads it as one of the publication identifiers (ISBN among them) |
- Any other value of
id_typereturns422. - The two modes are exclusive: with
id_type=external, a publica.la id is not accepted and returns404. - A publication that does not exist, belongs to another store, or is no longer available in your store, returns
404withThe specified publication does not exist in this store.
Only publications are addressable. Subscription plans, which a user can also add to a list from the storefront, are outside the scope of this API.
API Endpoints
| Operation | Method | Endpoint | Description |
|---|---|---|---|
| List a user's lists | GET | /api/v3/lists | Every list the store shows the target user, filterable by type, id or name |
| Get list items | GET | /api/v3/lists/{list}/items | The publications a list holds |
| Add an item | POST | /api/v3/lists/{list}/items | Add a publication to a list of the user |
| Remove an item | DELETE | /api/v3/lists/{list}/items/{content_id} | Remove a publication from a list of the user |
{list} is a slug or a list id, see Addressing a list. Every endpoint takes the target user parameters.
List management is not available through the API
This API reads lists and writes their contents. It does not manage lists themselves. The following operations exist only in the storefront, where the owner of the list performs them, and have no endpoint in the API:
| Operation | Available in the API |
|---|---|
| Create a named list | No |
| Rename a list | No |
| Change a list between public and private | No |
| Delete a list | No |
| Add, remove or change collaborators on a list | No |
The favorites list is the exception: it is created on first use by POST /api/v3/lists/favorites/items, so no create operation is needed for it.
List types
type | name | Addressable by | Description |
|---|---|---|---|
favorites | favorites | The favorites slug, or its id | The single favorites list of the user, created on first use |
custom | The name the user gave | Its id only | A named list the user created in the storefront, public or private |
The shopping cart and the save-for-later list are reserved storefront collections and are not part of this API. They never appear in the list index, and addressing one by its id returns 404.
Private lists are visible to an administrator token: reading them is the purpose of this API.
Cursor pagination
Every collection endpoint pages by cursor:
{
"data": [],
"links": {
"next": "https://yourstore.publica.la/api/v3/lists?user_external_id=student-1042&cursor=eyJsaXN0cy5pZCI6MTQ...",
"prev": null
},
"meta": {
"has_more": true
}
}
| Parameter | Type | Default | Description |
|---|---|---|---|
per_page | integer | 100 | Items per page, between 1 and 500 |
cursor | string | - | Cursor token, taken from links.next |
Pagination workflow
- Make the initial request without
cursor. - Follow
links.nextas-is: it already carries your other parameters. - Continue until
meta.has_moreisfalse.
Use meta.has_more to detect the end of the results. These responses carry no total count.
Common response format
List object
{
"data": {
"id": "9f2d4b1e-6c3a-4f58-9b77-1d0a2e5c8b34",
"name": "favorites",
"type": "favorites",
"private": false,
"role": "owner"
}
}
| Field | Type | Description |
|---|---|---|
id | string | Stable id of the list, used in the list endpoints |
name | string | favorites for the favorites list, the user's own name for a named list |
type | string | favorites or custom |
private | boolean | true when the list is not visible to other users in the storefront |
role | string | The target user's role on the list: owner, editor or viewer |
Lists carry no timestamps of their own, so no creation date is exposed.
A user's lists include the ones they were invited to as a collaborator. role tells the two apart: owner for the lists the user created, editor or viewer for lists someone else shared with them. The favorites list is always owner.
List item object
{
"data": {
"content_id": "468166",
"isbn": "9781234567890",
"title": "Conversations with Donald Hall",
"reader_url": "https://yourstore.publica.la/reader/conversations-with-donald-hall",
"added_at": "2026-08-27T12:00:00.000000Z"
}
}
| Field | Type | Description |
|---|---|---|
content_id | string | publica.la id of the publication |
isbn | string or null | Primary identifier of the publication, null when it has none |
title | string | Title of the publication |
reader_url | string | URL that opens the publication in the reader |
added_at | string or null | ISO 8601 timestamp of when the publication was added to the list, null for publications added before the platform started recording it |
The items endpoints return items in a stable order, the same order on every request, so a cursor pages through a list without repeating or skipping an item. That order is not chronological.
Error handling
| Status | Message | Cause |
|---|---|---|
401 | Unauthenticated. | Missing or invalid X-User-Token |
403 | This store cannot access the integrations API. | The store does not have the integrations API enabled |
403 | This token cannot operate on other users' lists. | The token does not belong to a store administrator |
404 | The specified user does not exist in this store. | No user matches user_external_id or user_email, or the account was deactivated |
404 | The specified publication does not exist in this store. | No publication matches content_id under the given id_type |
404 | Not found | A list id that is not one of the target user's lists, an unknown slug, or a reserved storefront collection (cart, save for later) |
422 | First validation error | Missing, duplicated or invalid parameters |
429 | Too Many Requests | Rate limit or daily read quota exceeded |
Authentication error (401)
{
"message": "Unauthenticated."
}
Validation error (422)
The message repeats the first error; errors carries all of them, keyed by parameter.
{
"message": "The user external id field is required when user email is not present.",
"errors": {
"user_external_id": [
"The user external id field is required when user email is not present."
],
"user_email": [
"The user email field is required when user external id is not present."
]
}
}
Sending both identifiers at once:
{
"message": "The user external id field prohibits user email from being present.",
"errors": {
"user_external_id": [
"The user external id field prohibits user email from being present."
]
}
}
Not found (404)
{
"message": "The specified user does not exist in this store."
}
Request logging
Every call to this resource is recorded in your store's API log, with the token that made the call, the endpoint and method, the response status, and the parameters sent (the target user among them). Logs are retained for 6 months.
Rate limiting
Two limits apply: a per-token burst of 60 requests per minute and a daily read quota of 2,000 reads per token plus 5,000 per store (GET and HEAD only; writes are not counted). Exceeding either returns 429 Too Many Requests with a Retry-After header.
Reading a user's favorites on every page view of your own platform exhausts the daily quota quickly. Read the list once per session and cache it on your side. See the API Overview for full details.
Quick start examples
Read a user's lists
curl -X GET "https://yourstore.publica.la/api/v3/lists?user_external_id=student-1042" \
-H "X-User-Token: your-api-token" \
-H "Accept: application/json"
Add a publication to a user's favorites by ISBN
curl -X POST "https://yourstore.publica.la/api/v3/lists/favorites/items" \
-H "X-User-Token: your-api-token" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"user_external_id": "student-1042",
"content_id": "9781234567890",
"id_type": "external"
}'
Next steps
- List Lists - Read the lists of a user, filterable by type, id or name
- Get List Items - Read the publications a list holds
- Manage List Items - Add and remove publications in a user's favorites