---
type: Documentation
title: REST API
description: The Librario REST API integrates and automates your library processes
  programmatically. v3 signs in with OAuth 2.1; v2 is deprecated and shuts down at
  the end of 2026.
resource: https://www.librario.de/en/docs/integration/api
language: en
revision: e114aa541d24c57e452957a2313c85d2014b716b
tags:
- docs
- integration
---

# REST API

> **Plan option:**
> 
> Available in plans with **REST API**. [Compare plans→](/en/preise)



> **No web scraping:**
> 
> Please use the REST API for all automated access. Scraping the web interface is not supported and can fail in unexpected ways. The API offers every endpoint a programmatic integration needs.


The Librario REST API lets you integrate and automate your library processes programmatically.
It exposes the functions of Librario that matter for your existing IT infrastructure and your workflows.

## Two API versions

There are currently two versions of the API. They are reachable independently of one another and differ in both their data model and their sign-in.

| | **v3** | **v2** |
| --- | --- | --- |
| Status | Preview | Deprecated, shutting down at the end of 2026 |
| Base URL | `/api/v3` | `/api/v2` |
| Sign-in | OAuth 2.1 | Basic authentication or session cookie |
| Identifiers | typed `identifiers` list | flat `doi`, `isbn`, `pmid`, … fields |

v3 clears out the legacy of v2: dedicated endpoints for nested resources, dedicated `DELETE` endpoints instead of `_destroy` markers, and a unified `PATCH /users/:id`.
The details are in the changelog entry [API v3 available as a preview](/en/changelog/2026-03-api-v3-preview).

> **v2 shuts down at the end of 2026:**
> 
> Until then v2 stays available unchanged, and individual fields (`import_state`, the flat identifier parameters, two of the user endpoints) already carry `X-API-Deprecation` headers. If you run v2 in production, plan the move to v3 now.


> **v3 is a preview:**
> 
> The interface can still change before the stable release. For production use we recommend planning the migration now and carrying it out once v3 is stable.


## Reaching the API

The API is available at:

`https://<your-company>.mylibrar.io/api`, for example: <https://app.mylibrar.io/api>

If you have been assigned your own subdomain, use it in the URL.

## The complete reference

The full API documentation is available in two formats:

1. **Swagger UI**:
   An interactive web interface for exploring the API

   - Available at `https://<your-company>.mylibrar.io/api`
   - Lets you call endpoints directly
   - Shows example requests and responses
   - Offers the OpenAPI specification as `swagger.yaml` for download

2. **OpenAPI specification** (`swagger.yaml`):
   A machine-readable description of the API

   - The basis for generating API clients automatically
   - Can be imported into API development tools
   - Useful with AI tools such as ChatGPT or Claude

> **Runnable examples:**
> 
> Ruby and Python scripts for a minimal client, availability checks against a reference-manager export and a data import with file uploads are on [REST API: examples](/en/docs/integration/api-beispiele). The script for the OAuth sign-in is on the [German reference](/de/docs/integration/api).


## Authentication

The two API versions sign in differently.

### v3: OAuth 2.1

v3 accepts OAuth 2.1 with the authorization code flow and nothing else.
There is no sign-in with a username and password.
An access token is valid for one hour and comes with a refresh token that renews itself on every use.

Every token is valid for exactly one **target resource**, namely `https://<your-company>.mylibrar.io/api/v3`.
You pass that address in the `resource` parameter when authorising.
For a self-registered client without the parameter, the authorization server answers `error=invalid_target`, because a token with no fixed target resource could be replayed against a different interface.
The exception is a client registered with no scope beyond `library:read` and `assets:read`.
This includes a registration with no `scope` at all, which Librario stores as those two.
If such a client omits `resource`, Librario binds the token to the MCP server.
`/api/v3` refuses that token with `401`.
The `WWW-Authenticate` header then carries `error="invalid_token"` and `error_description="audience mismatch"`.
So always send `resource` in scripts for the REST API.
For a client an administrator issued, Librario fills in the surface chosen at creation.
When you redeem the code, the token inherits the target resource of the authorization; if you send `resource` there anyway, it has to match.

> **One click is enough to try it out:**
> 
> Open `https://<your-company>.mylibrar.io/api` and click **Authorize**. Client, PKCE, and target resource are already filled in there. Once signed in you can call every endpoint straight from the Swagger UI.


#### Public and confidential clients

A client is **public** when it does not authenticate itself at the token endpoint, and **confidential** when it does, with a client secret.
Librario issues both. Which one you get is decided on one of two paths.

**Self-registration (Dynamic Client Registration).**
Your program registers itself at `POST /oauth/clients`.
The `token_endpoint_auth_method` field decides the type: the value `none` yields a public client with no secret.
Leave the field out and RFC 7591's default `client_secret_basic` applies, so you get a confidential client with a secret.
The supported methods are `none`, `client_secret_basic`, and `client_secret_post`.
The `client_secret` appears in the registration response and nowhere else; it is never handed out a second time.

**Issued by an administrator.**
If the client should belong to the account rather than to the person who registered it, an administrator issues a client for the **REST API** surface.
The client, with its registration, client secret, rotation and revocation, belongs to the account and stays in place when people come and go. Each connection belongs to the person who signed in and runs with their roles. When that person leaves the team, someone else signs in once and the integration carries on with the same client.
The same applies to connectors that ask for a client ID and client secret up front.
Administrators see the client in the account administration, rotate its client secret with a changeover window, and end every connection at once by revoking it.
Those clients are always confidential; they authenticate with `client_secret_basic` or `client_secret_post`.
With `client_secret_basic`, send the client ID and client secret as HTTP Basic authentication to `POST /oauth/token`; `code` and `code_verifier` go in the request body as for a public client.
The client secret is shown exactly once, and at most two are valid at a time so a rotation runs without interruption.
Creating, rotating and revoking are described on [OAuth clients for integrations](/en/docs/integration/oauth).
This path is part of advanced AI controls and is open only on plans that include them; self-registration is not affected.

Librario requires PKCE (method `S256`) from every self-registered client, public or confidential.
For a client an administrator issues, PKCE is a choice at creation and is on by default; turn it off only when the connector sends no `code_challenge`.
If a tool registers itself and sends no `code_challenge`, support can lift the requirement for the confidential clients of your account.
For public clients PKCE always stands.

### v3: Permissions

A token carries the intersection of the requested permissions, what the client registered for, and what the person signing in released on the consent screen.

| Scope | Allows | Endpoints |
| --- | --- | --- |
| `library:read` | Read publications, hard copies, collections, and authorities | `GET /publications`, `/hard_copies`, `/authorities`, `/collections`, `/hard_copies/locations` |
| `library:write` | Create and change library data; for publications, hard copies, and collections also delete it | `POST/PATCH/DELETE /publications`, `/hard_copies`, `/collections`; `POST/PATCH /authorities` (no delete, use `merge` instead); `PATCH /hard_copies/locations` (change and `merge` only, no create and no delete); `GET /publications/cover/presign` |
| `assets:read` | Read attached files and download links | `GET /assets/:id`; also decides whether `url`, `thumb_url`, and `preview_url` appear in responses from `/assets` and `/publications` at all |
| `assets:write` | Upload, change, and delete attached files | `POST/PATCH/DELETE /assets`, `GET /assets/presign`; also covers file content sent along in `assets`/`assets_attributes` when creating or changing a publication; `library:write` alone is not enough for that |
| `users:read` | Read the user directory | `GET /users`, `GET /users/:id`; `GET /users/me` returns your own record and requires no scope |
| `users:write` | Manage users | `POST/PATCH/DELETE /users`, `GET /users/avatar/presign` |
| `tasks:read` | Read tasks and quality reviews | `GET /tasks` |
| `tasks:write` | Create, edit, and complete tasks | `POST/PATCH /tasks` |

If a token is missing a permission, the API answers `403 InsufficientScope` and names the missing scope in the `WWW-Authenticate` header.
Tokens issued before `assets:read` and `assets:write` existed received those permissions automatically wherever they carried `library:read` or `library:write`.

### v2: Basic authentication or session cookie

v2 knows two methods:

1. **Basic authentication** with the usual Librario credentials.
2. **Session cookie** (`_library_session`), when the call comes from a browser session that is already signed in.

In both cases the permissions match the [roles configured in the application](/en/docs/administration/rollen).

> **Technical users for integrations:**
> 
> We recommend setting up dedicated technical accounts for automated processes. That keeps the integration independent of personal accounts and makes access rights easier to manage.


Examples for v2 are not documented here. The complete description is in the OpenAPI specification at `https://<your-company>.mylibrar.io/api/v2/swagger.yaml`.

## Data format

The API exchanges data as JSON, a format that every common programming language handles easily.

## Pagination

List endpoints return 25 entries per page, 50 at most.
To retrieve every record, request the pages one after another.

Two response headers help you navigate:

- `X-Total`: the total number of available entries
- `Link`: URLs for the first, previous, next, and last page

## Best practices

### Request rate

Please limit automated access to roughly 60 requests per minute, use pagination for list queries, and process large volumes in smaller batches.

### Error handling

The API uses the following HTTP status codes:

- `200`: request succeeded
- `401`: not authorised, for example because the token has expired
- `403`: access denied or permission missing (`InsufficientScope`)
- `404`: resource not found. When creating or updating a publication, also when an `id` for a contributor, category or organization does not exist in your account (category and organization in API v3 only)
- `422`: input could not be processed
- `429`: too many requests
- `500`: internal server error

Error responses carry additional detail as JSON:

```json
{
  "error": {
    "code": "RecordNotFound",
    "message": "Requested record not found"
  }
}
```
