Zum Inhalt springen
Dokumentation
Als Markdown ansehen

REST-API

Kein Web Scraping

Bitte verwenden Sie für automatisierte Zugriffe ausschließlich die REST-API. Das Scraping der Weboberfläche wird nicht unterstützt und kann zu unerwarteten Fehlern führen. Die API bietet alle notwendigen Endpunkte für eine programmatische Integration.

Die Librario REST-API ermöglicht Ihnen die programmatische Integration und Automatisierung Ihrer Bibliotheksprozesse. Sie können damit alle wichtigen Funktionen von Librario in Ihre bestehende IT-Infrastruktur bzw. in Ihre Prozesse einbinden.

Zwei API-Versionen

Es gibt zurzeit zwei Versionen der API. Sie sind unabhängig voneinander erreichbar und unterscheiden sich sowohl im Datenmodell als auch in der Anmeldung.

V3 V2
Status Preview Veraltet, Abschaltung zum Jahresende 2026
Basis-URL /api/v3 /api/v2
Anmeldung OAuth 2.1 Basic Authentication oder Session-Cookie
Identifikatoren typisierte identifiers-Liste flache Felder doi, isbn, pmid, …

V3 räumt Altlasten der V2 auf: eigene Endpunkte für verschachtelte Ressourcen, dedizierte DELETE-Endpunkte statt _destroy-Markern und ein vereinheitlichtes PATCH /users/:id. Die Details stehen im Changelog-Eintrag API V3 als Preview verfügbar.

V3 ist eine Preview

Die Schnittstelle kann sich bis zur stabilen Freigabe noch ändern. Für den Produktivbetrieb empfehlen wir, die Migration jetzt zu planen und erst nach dem stabilen V3-Release umzusetzen. V2 bleibt bis zur Abschaltung unverändert verfügbar.

Zugriff auf die API

Die API ist unter folgender URL erreichbar:

https://<ihre-firma>.mylibrar.io/api, zum Beispiel: https://app.mylibrar.io/api

Falls Sie eine eigene Subdomain zugewiesen bekommen haben, können Sie diese in der URL verwenden.

API-Dokumentation

Screenshot der Swagger UI Dokumentation der Librario API. Links sind die verschiedenen API-Endpunkte nach Ressourcen gruppiert zu sehen. Rechts werden die Details des ausgewählten Endpunkts mit Parametern und Beispielantworten angezeigt.
Die interaktive API-Dokumentation mittels Swagger UI

Die vollständige API-Dokumentation steht Ihnen in zwei Formaten zur Verfügung:

  1. Swagger UI: Eine interaktive Web-Oberfläche zum Erkunden der API

    • Erreichbar unter https://<ihre-firma>.mylibrar.io/api
    • Ermöglicht das direkte Testen von API-Aufrufen
    • Zeigt Beispielanfragen und -antworten
    • Bietet Download der OpenAPI-Spezifikation als swagger.yaml
  2. OpenAPI-Spezifikation (swagger.yaml): Eine maschinenlesbare Beschreibung der API

    • Basis für die automatische Generierung von API-Clients
    • Kann in API-Entwicklungswerkzeuge importiert werden
    • Hilfreich für die Verwendung mit KI-Tools wie ChatGPT oder Claude

Authentifizierung

Die beiden API-Versionen melden sich unterschiedlich an.

V3: OAuth 2.1

V3 akzeptiert ausschließlich OAuth 2.1 mit dem Authorization-Code-Flow. Eine Anmeldung mit Benutzername und Passwort gibt es nicht. Ein Zugriffstoken ist eine Stunde gültig und bringt ein Refresh-Token mit, das sich bei jeder Verwendung erneuert.

Jedes Token gilt für genau eine Zielressource, nämlich https://<ihre-firma>.mylibrar.io/api/v3. Diese Adresse geben Sie beim Autorisieren im Parameter resource an. Fehlt er bei einem selbst registrierten Client, antwortet der Autorisierungsserver mit error=invalid_target, denn ein Token ohne festgelegte Zielressource ließe sich gegen eine andere Schnittstelle wiederverwenden. Eine Ausnahme gilt für Clients, die mit keinem Scope außer library:read und assets:read registriert sind. Das gilt auch für eine Registrierung ganz ohne scope, denn Librario speichert dann diese beiden Scopes. Fehlt bei einem solchen Client resource, bindet Librario das Token an den MCP-Server. /api/v3 lehnt dieses Token mit 401 ab. Der Header WWW-Authenticate enthält dann error="invalid_token" und error_description="audience mismatch". Senden Sie resource in Skripten für die REST-API deshalb immer mit. Bei Clients, die die Administration ausgestellt hat, ergänzt Librario die beim Anlegen gewählte Schnittstelle. Beim Einlösen des Codes übernimmt das Token die Zielressource der Autorisierung; senden Sie resource dort trotzdem mit, muss der Wert übereinstimmen.

Zum Ausprobieren genügt ein Klick

Öffnen Sie https://<ihre-firma>.mylibrar.io/api und klicken Sie auf Authorize. Client, PKCE und Zielressource sind dort bereits hinterlegt. Nach der Anmeldung können Sie jeden Endpunkt direkt in der Swagger UI aufrufen.

Öffentliche und vertrauliche Clients

Ein Client ist öffentlich, wenn er sich am Token-Endpunkt nicht ausweist, und vertraulich, wenn er das mit einem Client-Secret tut. Librario stellt beide Arten aus. Welche Sie erhalten, entscheidet sich auf einem von zwei Wegen.

Selbstregistrierung (Dynamic Client Registration). Ihr Programm meldet sich unter POST /oauth/clients selbst an. Das Feld token_endpoint_auth_method bestimmt die Art: Der Wert none ergibt einen öffentlichen Client ohne Secret. Lassen Sie das Feld weg, gilt nach RFC 7591 der Vorgabewert client_secret_basic, und Sie erhalten einen vertraulichen Client mit Secret. Unterstützt werden none, client_secret_basic und client_secret_post. Das client_secret steht ausschließlich in der Antwort auf die Registrierung; ein zweites Mal wird es nicht ausgegeben.

Von der Administration ausgestellt. Soll der Client dem Konto gehören statt der Person, die ihn registriert hat, stellt eine Person mit Administrator-Rolle einen Client für die Schnittstelle REST-API aus. Der Client mit Registrierung, Client Secret, Wechsel und Widerruf gehört dem Konto und bleibt bestehen, wenn Mitarbeitende wechseln. Jede Verbindung gehört der Person, die sich angemeldet hat, und läuft mit deren Rollen. Verlässt diese Person das Team, meldet sich eine andere einmal an, und die Integration läuft mit demselben Client weiter. Dasselbe gilt für Connectoren, die Client ID und Client Secret im Voraus verlangen. Die Administration sieht den Client in der Konto-Verwaltung, wechselt sein Client Secret mit Übergangsfrist und beendet mit einem Widerruf alle Verbindungen auf einmal. Diese Clients sind immer vertraulich; sie weisen sich mit client_secret_basic oder client_secret_post aus. Mit client_secret_basic senden Sie Client ID und Client Secret als HTTP-Basic-Authentifizierung an POST /oauth/token; code und code_verifier stehen wie bei einem öffentlichen Client im Anfragekörper. Das Client Secret erscheint genau einmal, und es gelten höchstens zwei gleichzeitig, damit sich ein Wechsel ohne Unterbrechung durchführen lässt. Anlegen, Wechseln und Widerrufen beschreibt die Seite OAuth-Clients für Integrationen. Dieser Weg gehört zur erweiterten KI-Verwaltung und steht nur in Tarifen mit diesem Bestandteil offen; die Selbstregistrierung ist davon nicht betroffen.

PKCE (Verfahren S256) verlangt Librario von jedem selbst registrierten Client, gleich ob öffentlich oder vertraulich. Bei Clients, die die Administration ausstellt, ist PKCE beim Erstellen wählbar und standardmäßig eingeschaltet; schalten Sie es nur ab, wenn der Connector keinen code_challenge sendet. Registriert sich ein Werkzeug selbst und sendet dabei keinen code_challenge, kann der Support die Anforderung für die vertraulichen Clients Ihres Kontos aufheben. Für öffentliche Clients bleibt PKCE immer erforderlich.

Für Skripte übernimmt das folgende Beispiel den vollständigen Ablauf: Es registriert sich mit token_endpoint_auth_method: "none" selbst als öffentlicher OAuth-Client (Dynamic Client Registration), erzeugt den PKCE-Nachweis, nimmt die Weiterleitung auf einem lokalen Port entgegen und tauscht den Code gegen ein Token.

Ruby

# frozen_string_literal: true

require 'base64'
require 'digest'
require 'json'
require 'net/http'
require 'securerandom'
require 'socket'
require 'uri'

# Obtains a Librario v3 access token from a script, with no client secret anywhere.
#
# The whole OAuth 2.1 dance, in four steps:
#
#   1. Register this script as an OAuth client (RFC 7591). No pre-arrangement with Librario, and
#      `token_endpoint_auth_method: 'none'` asks for a public client, so there is no secret to
#      store; omitting that field would get one issued. Keep the returned client_id;
#      re-registering on every run is wasteful and each Librario tenant only accepts a bounded
#      number of registrations per day.
#   2. Generate a PKCE verifier and its S256 challenge (RFC 7636). Librario accepts S256 only.
#   3. Send the user to /oauth/authorize and catch the redirect on a loopback listener
#      (RFC 8252). Loopback is the one place Librario permits a plain-http callback.
#   4. Exchange the code for a token, proving possession of the verifier.
#
# `resource` (RFC 8707) appears in steps 3 and 4. It names which Librario surface the token is
# for. Omit it and the authorization server refuses with `invalid_target`, because a token has
# to be bound to exactly one audience.
#
#   token = OAuthPkceLogin.new(base_url: 'https://acme.mylibrar.io').call
#   client = LibrarioClient.new(base_url: 'https://acme.mylibrar.io', access_token: token['access_token'])
#
# Access tokens live one hour. `token['refresh_token']` renews them; it rotates on every use.
class OAuthPkceLogin
  class Error < StandardError
  end

  DEFAULT_SCOPE = 'library:read library:write users:read users:write tasks:read tasks:write'

  # @param base_url [String] e.g. "https://acme.mylibrar.io"
  # @param client_id [String, nil] from a previous run; registers a new client when nil
  # @param scope [String] space-delimited
  # @param port [Integer] loopback port for the callback listener
  def initialize(base_url:, client_id: nil, scope: DEFAULT_SCOPE, port: 8765)
    @base_url = base_url.to_s.chomp('/')
    @client_id = client_id
    @scope = scope
    @port = port
  end

  # Runs the full flow and returns the parsed token response.
  #
  # Yields the authorization URL when a block is given, instead of opening a browser. That is
  # what the test suite uses; a real script wants the default.
  def call(&open_url)
    client_id = @client_id || register_client.fetch('client_id')
    verifier = generate_code_verifier
    state = SecureRandom.urlsafe_base64(16)

    server = TCPServer.new('127.0.0.1', @port)
    url = authorization_url(client_id:, verifier:, state:)
    open_url ? yield(url) : open_browser(url)

    code = await_callback(server, expected_state: state)
    exchange_code(code:, client_id:, verifier:)
  ensure
    server&.close
  end

  # RFC 7591. The redirect URI must be a loopback IP with an explicit port: Librario rejects
  # `http://localhost/...` without one, because Doorkeeper's any-port loopback matching only
  # applies to IP literals and such a client could never complete a login.
  def register_client
    post_form(
      "#{@base_url}/oauth/clients",
      client_name: 'Librario example script',
      redirect_uris: [redirect_uri],
      grant_types: %w[authorization_code refresh_token],
      response_types: %w[code],
      token_endpoint_auth_method: 'none',
      scope: @scope
    )
  end

  def authorization_url(client_id:, verifier:, state:)
    query = {
      response_type: 'code',
      client_id:,
      redirect_uri:,
      scope: @scope,
      state:,
      code_challenge: code_challenge(verifier),
      code_challenge_method: 'S256',
      resource: api_v3_audience
    }
    "#{@base_url}/oauth/authorize?#{URI.encode_www_form(query)}"
  end

  def exchange_code(code:, client_id:, verifier:)
    post_form(
      "#{@base_url}/oauth/token",
      grant_type: 'authorization_code',
      code:,
      redirect_uri:,
      client_id:,
      code_verifier: verifier,
      resource: api_v3_audience
    )
  end

  private

  def post_form(url, **params)
    uri = URI.parse(url)
    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['Accept'] = 'application/json'
    request.body = JSON.generate(params)

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
    body = response.body.to_s.empty? ? {} : JSON.parse(response.body)
    raise Error, "#{uri.path} failed: HTTP #{response.code} #{body}" unless response.is_a?(Net::HTTPSuccess)

    body
  end

  def redirect_uri = "http://127.0.0.1:#{@port}/callback"
  def generate_code_verifier = SecureRandom.urlsafe_base64(32)

  def code_challenge(verifier)
    Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false)
  end

  def api_v3_audience = "#{@base_url}/api/v3"

  def open_browser(url)
    opener = %w[xdg-open open].find { |cmd| system('which', cmd, out: File::NULL, err: File::NULL) }
    opener ? system(opener, url, out: File::NULL, err: File::NULL) : warn("Open this URL to authorize:\n\n  #{url}\n")
  end

  # Blocks until the browser follows the redirect back to the loopback listener.
  def await_callback(server, expected_state:)
    socket = server.accept
    query = callback_query(socket.gets.to_s)

    respond(socket, query['error'] ? "Authorization failed: #{query['error']}" : 'Authorized. You can close this tab.')
    socket.close

    raise Error, "authorization failed: #{query['error']}" if query['error']
    raise Error, 'state mismatch — possible CSRF, aborting' unless query['state'] == expected_state

    query.fetch('code')
  end

  # "GET /callback?code=...&state=... HTTP/1.1" -> { "code" => ..., "state" => ... }
  def callback_query(request_line)
    path = request_line.split[1].to_s
    URI.decode_www_form(URI.parse(path).query.to_s).to_h
  end

  def respond(socket, message)
    socket.print("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: #{message.bytesize}\r\n\r\n#{message}")
  end
end

Python

"""Obtains a Librario v3 access token from a script, with no client secret anywhere.

The whole OAuth 2.1 dance, in four steps:

1. Register this script as an OAuth client (RFC 7591). No pre-arrangement with Librario, and
   ``token_endpoint_auth_method: "none"`` asks for a public client, so there is no secret to
   store; omitting that field would get one issued. Keep the returned ``client_id``:
   re-registering on every run is wasteful, and each Librario tenant only accepts a bounded
   number of registrations per day.
2. Generate a PKCE verifier and its S256 challenge (RFC 7636). Librario accepts S256 only.
3. Send the user to ``/oauth/authorize`` and catch the redirect on a loopback listener (RFC 8252).
   Loopback is the one place Librario permits a plain-http callback.
4. Exchange the code for a token, proving possession of the verifier.

``resource`` (RFC 8707) appears in steps 3 and 4. It names which Librario surface the token is
for. Omit it and the authorization server refuses with ``invalid_target``, because a token has to
be bound to exactly one audience.

    token = OAuthPkceLogin(base_url="https://acme.mylibrar.io").call()
    client = LibrarioClient(base_url="https://acme.mylibrar.io", access_token=token["access_token"])

Access tokens live one hour. ``token["refresh_token"]`` renews them; it rotates on every use.
"""

from __future__ import annotations

import base64
import hashlib
import http.server
import secrets
import threading
import urllib.parse
import webbrowser
from collections.abc import Callable
from typing import Any

import httpx

DEFAULT_SCOPE = "library:read library:write users:read users:write tasks:read tasks:write"


class OAuthError(Exception):
    pass


class OAuthPkceLogin:
    def __init__(
        self,
        base_url: str,
        client_id: str | None = None,
        scope: str = DEFAULT_SCOPE,
        port: int = 8765,
    ) -> None:
        self.base_url = base_url.rstrip("/")
        self.client_id = client_id
        self.scope = scope
        self.port = port

    @property
    def redirect_uri(self) -> str:
        return f"http://127.0.0.1:{self.port}/callback"

    @property
    def api_v3_audience(self) -> str:
        return f"{self.base_url}/api/v3"

    def call(self, open_url: Callable[[str], None] | None = None) -> dict[str, Any]:
        """Runs the full flow and returns the parsed token response.

        ``open_url`` receives the authorization URL instead of a browser being opened. That is
        what the test suite uses; a real script wants the default.
        """
        client_id = self.client_id or self.register_client()["client_id"]
        verifier = secrets.token_urlsafe(32)
        state = secrets.token_urlsafe(16)

        with _CallbackListener(self.port) as listener:
            url = self.authorization_url(client_id=client_id, verifier=verifier, state=state)
            (open_url or webbrowser.open)(url)
            query = listener.wait()

        if "error" in query:
            raise OAuthError(f"authorization failed: {query['error']}")
        if query.get("state") != state:
            raise OAuthError("state mismatch — possible CSRF, aborting")

        return self.exchange_code(code=query["code"], client_id=client_id, verifier=verifier)

    def register_client(self) -> dict[str, Any]:
        """RFC 7591. The redirect URI must be a loopback IP with an explicit port: Librario
        rejects ``http://localhost/...`` without one, because Doorkeeper's any-port loopback
        matching only applies to IP literals and such a client could never complete a login.
        """
        return self._post_json(
            f"{self.base_url}/oauth/clients",
            {
                "client_name": "Librario example script",
                "redirect_uris": [self.redirect_uri],
                "grant_types": ["authorization_code", "refresh_token"],
                "response_types": ["code"],
                "token_endpoint_auth_method": "none",
                "scope": self.scope,
            },
        )

    def authorization_url(self, client_id: str, verifier: str, state: str) -> str:
        query = urllib.parse.urlencode(
            {
                "response_type": "code",
                "client_id": client_id,
                "redirect_uri": self.redirect_uri,
                "scope": self.scope,
                "state": state,
                "code_challenge": _code_challenge(verifier),
                "code_challenge_method": "S256",
                "resource": self.api_v3_audience,
            }
        )
        return f"{self.base_url}/oauth/authorize?{query}"

    def exchange_code(self, code: str, client_id: str, verifier: str) -> dict[str, Any]:
        return self._post_json(
            f"{self.base_url}/oauth/token",
            {
                "grant_type": "authorization_code",
                "code": code,
                "redirect_uri": self.redirect_uri,
                "client_id": client_id,
                "code_verifier": verifier,
                "resource": self.api_v3_audience,
            },
        )

    @staticmethod
    def _post_json(url: str, payload: dict[str, Any]) -> dict[str, Any]:
        response = httpx.post(url, json=payload, headers={"Accept": "application/json"}, timeout=10.0)
        body = response.json() if response.content else {}
        if not response.is_success:
            raise OAuthError(f"{urllib.parse.urlparse(url).path} failed: HTTP {response.status_code} {body}")
        return body


def _code_challenge(verifier: str) -> str:
    digest = hashlib.sha256(verifier.encode()).digest()
    return base64.urlsafe_b64encode(digest).rstrip(b"=").decode()


class _CallbackListener:
    """Serves exactly one request on the loopback port and hands back its query string."""

    def __init__(self, port: int) -> None:
        self._query: dict[str, str] = {}
        listener = self

        class Handler(http.server.BaseHTTPRequestHandler):
            def do_GET(self) -> None:  # noqa: N802 - name fixed by BaseHTTPRequestHandler
                parsed = urllib.parse.urlparse(self.path)
                listener._query = dict(urllib.parse.parse_qsl(parsed.query))
                body = b"Authorized. You can close this tab."
                self.send_response(200)
                self.send_header("Content-Type", "text/plain")
                self.send_header("Content-Length", str(len(body)))
                self.end_headers()
                self.wfile.write(body)

            def log_message(self, *_args: Any) -> None:
                pass

        self._server = http.server.HTTPServer(("127.0.0.1", port), Handler)
        self._thread = threading.Thread(target=self._server.handle_request, daemon=True)

    def __enter__(self) -> _CallbackListener:
        self._thread.start()
        return self

    def __exit__(self, *_exc: object) -> None:
        self._server.server_close()

    def wait(self, timeout: float = 300.0) -> dict[str, str]:
        self._thread.join(timeout)
        if self._thread.is_alive():
            raise OAuthError("timed out waiting for the authorization redirect")
        return self._query

Client-ID aufbewahren

Die Registrierung ist einmalig. Speichern Sie die zurückgegebene client_id und geben Sie sie beim nächsten Lauf mit, statt sich erneut zu registrieren. Registrieren Sie sich vertraulich, gehört das client_secret aus derselben Antwort dazu. Jede Bibliothek nimmt pro Tag nur eine begrenzte Zahl an Registrierungen entgegen.

V3: Berechtigungen

Ein Token trägt die Schnittmenge aus den angefragten Berechtigungen, dem, was der Client bei der Registrierung angemeldet hat, und dem, was die anmeldende Person auf der Zustimmungsseite freigegeben hat.

Scope Erlaubt Endpunkte
library:read Publikationen, Exemplare, Sammlungen und Normdaten lesen GET /publications, /hard_copies, /authorities, /collections, /hard_copies/locations
library:write Bibliotheksdaten anlegen und ändern, bei Publikationen, Exemplaren und Sammlungen auch löschen POST/PATCH/DELETE /publications, /hard_copies, /collections; POST/PATCH /authorities (ohne Löschen, stattdessen merge); PATCH /hard_copies/locations (nur Ändern und merge, kein Anlegen und kein Löschen); GET /publications/cover/presign
assets:read Angehängte Dateien und Download-Links lesen GET /assets/:id; entscheidet außerdem, ob url, thumb_url und preview_url in Antworten von /assets und /publications überhaupt erscheinen
assets:write Angehängte Dateien hochladen, ändern und löschen POST/PATCH/DELETE /assets, GET /assets/presign; gilt auch für Dateiinhalt, der über assets/assets_attributes beim Anlegen oder Ändern einer Publikation mitgeschickt wird; dafür reicht library:write allein nicht
users:read Benutzerverzeichnis lesen GET /users, GET /users/:id; GET /users/me liefert den eigenen Datensatz und verlangt keinen Scope
users:write Benutzer:innen verwalten POST/PATCH/DELETE /users, GET /users/avatar/presign
tasks:read Aufgaben und Qualitätsprüfungen lesen GET /tasks
tasks:write Aufgaben anlegen, bearbeiten und abschließen POST/PATCH /tasks

Fehlt einem Token eine Berechtigung, antwortet die API mit 403 InsufficientScope und nennt im Header WWW-Authenticate den fehlenden Scope. Tokens, die vor Einführung von assets:read und assets:write ausgestellt wurden, haben diese Berechtigungen automatisch erhalten, soweit sie library:read beziehungsweise library:write trugen.

V2 kennt zwei Verfahren:

  1. Basic Authentication mit den normalen Librario-Zugangsdaten.
  2. Session-Cookie (_library_session), wenn der Aufruf aus einer bereits angemeldeten Browser-Sitzung erfolgt.

Die Berechtigungen entsprechen in beiden Fällen den in der Anwendung konfigurierten Rollen.

Technische Benutzer für Integrationen

Es wird empfohlen, für automatisierte Prozesse dedizierte technische Zugänge anzulegen. Dies macht die Integration unabhängig von personenbezogenen Accounts und vereinfacht die Verwaltung von Zugriffsrechten.

Beispiele für V2 führen wir nicht mehr auf. Die vollständige Beschreibung finden Sie in der OpenAPI-Spezifikation unter https://<ihre-firma>.mylibrar.io/api/v2/swagger.yaml.

Datenformat

Die API nutzt das JSON-Format für den Datenaustausch. Dieses Format ist in allen gängigen Programmiersprachen einfach zu verarbeiten.

Paginierung

Bei Listen-Endpoints wird die Ausgabe auf 25 Einträge pro Seite begrenzt, maximal sind 50 möglich. Um alle Datensätze zu erhalten, rufen Sie die Seiten nacheinander ab.

Die Navigation wird durch spezielle Response-Header erleichtert:

  • X-Total: Gesamtanzahl der verfügbaren Einträge
  • Link: URLs für erste/vorherige/nächste/letzte Seite

Beispiele

Lauffähige Ruby- und Python-Skripte für einen minimalen Client, den Abgleich von Publikationslisten, den Datenimport mit Dateien und Backups stehen auf der Seite REST-API: Beispiele.

Best Practices

Anfragerate

Bitte begrenzen Sie automatisierte Zugriffe auf etwa 60 Anfragen pro Minute, verwenden Sie bei Listenabfragen die Paginierung und verarbeiten Sie große Datenmengen in kleineren Batches.

Fehlerbehandlung

Die API verwendet folgende HTTP-Statuscodes:

  • 200: Anfrage erfolgreich
  • 401: Nicht autorisiert, etwa weil das Token abgelaufen ist
  • 403: Zugriff verweigert oder Berechtigung fehlt (InsufficientScope)
  • 404: Ressource nicht gefunden. Beim Anlegen oder Ändern einer Publikation auch, wenn eine id für Beitragende, eine Kategorie oder eine Organisation in Ihrem Konto nicht existiert (Kategorie und Organisation nur in der API v3)
  • 422: Eingabe konnte nicht verarbeitet werden
  • 429: Zu viele Anfragen
  • 500: Interner Serverfehler

Fehlerantworten enthalten zusätzliche Details im JSON-Format:

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