Glossary
A reference for the domain concepts and project-specific terms used throughout
dip-bundestag-cli. The DIP domain is German; this glossary gives the English
term used in the CLI/API (where one exists) alongside the original German.
Translation table. The CLI keeps the original German resource names (they are the API’s own paths), with the English meaning given here:
German English Vorgang procedure / legislative process Vorgangsposition procedure step / position within a procedure Drucksache printed paper Plenarprotokoll plenary protocol (minutes) Aktivität activity Person person (member / actor) Wahlperiode electoral term / legislative period Bundestag the federal parliament Bundesrat the federal council (states’ chamber)
DIP
DIP — Dokumentations- und Informationssystem für Parlamentsmaterialien.
(“Documentation and information system for parliamentary materials.”) The
Bundestag’s catalogue of parliamentary process data: who proposed what, the
documents involved, the plenary debates, and the people acting. Browsable at
dip.bundestag.de; the machine API this tool wraps
lives at search.dip.bundestag.de/api/v1.
Bundestag. The German federal parliament (the elected chamber). Bundesrat is the chamber representing the sixteen federal states (Länder). DIP covers materials from both bodies.
API key. DIP requires an API key, sent as the HTTP header
Authorization: ApiKey <key>. The key is not bundled — supply it via
--api-key or the DIP_API_KEY environment variable, else the header is omitted
and the API returns 401. Flag, variable and library (apiKey) trim the key the
same way (normaliseApiKey) and reject one an HTTP header cannot carry (exit 2,
DipValidationError). The Bundestag publishes a public key on its
DIP API help page (stated there in
2026 as valid until the end of May 2027); a personal key can be requested from
parlamentsdokumentation@bundestag.de. The CLI’s obtain-key command reads that
page’s key from the JSON document DIP’s content service serves behind it, falls back
to the bundesAPI README, and verifies the key against the live API before printing
it — so it fails rather than hand over a key the API rejects.
Resources (endpoints)
Each resource is exposed under /api/v1/<resource> with a list endpoint
(/<resource>) and a single-item endpoint (/<resource>/<id>). The CLI mirrors
this as <resource> list and <resource> get <id>.
Vorgang (procedure). A legislative or parliamentary process — e.g. a bill,
a motion (Antrag), an interpellation (Anfrage) — tracked from introduction to
conclusion. The central entity that ties documents, activities and people
together. CLI: vorgang. Client: client.vorgaenge.
Vorgangsposition (procedure step). A single step within a Vorgang — one
event in its history (e.g. a first reading, a committee referral, a vote). A
Vorgang has many Vorgangspositionen. CLI: vorgangsposition. Client:
client.vorgangspositionen.
Drucksache (printed paper). A formal parliamentary document — bills,
motions, reports, answers to questions, etc. — identified by a paper number
within an electoral term. CLI: drucksache. Client: client.drucksachen.
Drucksache-Text. The same Drucksachen, but with the extracted full text
of the document included in the payload (a separate, heavier endpoint).
CLI: drucksache-text. Client: client.drucksacheText.
Plenarprotokoll (plenary protocol). The stenographic minutes of a plenary
sitting of the Bundestag or Bundesrat. CLI: plenarprotokoll. Client:
client.plenarprotokolle.
Plenarprotokoll-Text. The Plenarprotokolle with the extracted full text
of the minutes included. CLI: plenarprotokoll-text. Client:
client.plenarprotokollText.
Aktivität (activity). A recorded action by a person within a procedure —
e.g. a speech, a question, a signature on a motion. Links a Person to a Vorgang.
CLI: aktivitaet. Client: client.aktivitaeten.
Person. A person appearing in the materials — typically a member of
parliament (Abgeordnete:r), but also other actors. CLI: person. Client:
client.personen.
Identifiers, filters & pagination
id. Every resource document has a numeric id, used by the get endpoint
(drucksache get 123456) and by the f.id filter. The CLI’s --id flag is
shorthand for f.id and is repeatable (a whole number, see below). The get id is
trimmed first, so a copy-pasted "282486 " finds the document. A blank id, . or .. is rejected (CLI
exit 2, library DipValidationError, same message) before any request: get("")
or get(".") would otherwise request the collection endpoint and return the list,
and get("..") the API root.
f.* filters (Filter). DIP filters are query parameters prefixed with f.,
e.g. f.titel (title), f.id, f.wahlperiode, f.datum.start /
f.datum.end (date range), f.vorgangstyp, f.dokumentart,
f.aktualisiert.start (last-updated range). Each list endpoint has its own set
(LIST_FILTERS, from DIP’s OpenAPI description): f.vorgang exists only on
vorgangsposition, f.person only on person and aktivitaet. DIP ignores an
unknown filter and returns the whole unfiltered list, so the library’s list()
and the CLI reject a key that is not in the resource’s set (the CLI with exit 2);
list(params, { allowUnknownFilters: true }) sends one anyway, for a filter DIP
added later. It passes the rest verbatim via
--filter key=value (repeatable); only the first = splits key from value, so a
value may itself contain =. Repeating the same key sends repeated query keys
(?f.id=1&f.id=2), which DIP treats as an OR set. A blank filter value, --id
or --cursor is rejected too (exit 2), and the library’s list() rejects a blank
parameter name or value, an empty array and a blank array element with
DipValidationError before any request: DIP treats an empty parameter as no
filter. The integer filters (INTEGER_FILTERS: f.id, f.wahlperiode, f.vorgang, f.drucksache, f.plenarprotokoll, f.aktivitaet, f.person_id, f.vorgangsposition_id and f.vorgangstyp_notation, all
integers in DIP’s OpenAPI description) take a non-negative whole number — plain digits
on the command line (--id too), a string of digits or a number in the library; the
CLI rejects anything else with exit 2 and list() with DipValidationError
(integerFilterProblem), since DIP answers f.wahlperiode=abc with a misleading
400 … Invalid cursor.
cursor. DIP list endpoints are cursor-paginated. A list response carries
a cursor; pass it back via --cursor (CLI) or { cursor } (library) to fetch
the next page. The cursor is opaque — treat it as a token, not a number. When the
returned cursor stops changing, you have reached the end. Send the same filters
again with every cursor: DIP does not bind a cursor to the query it came from, so
a cursor sent without them pages through the whole unfiltered list. So a cursor
without any filter is refused: the CLI’s --cursor without a --filter or --id
is a usage error (exit 2), and the library’s list() throws DipValidationError
(cursorWithoutFiltersProblem) unless { allowUnfilteredCursor: true } is passed to
page the unfiltered list on purpose.
numFound. The total number of documents matching a list query (across all
pages), returned in the list envelope alongside the current page’s documents.
documents. The array of resource documents in the current page of a list
response. Each is exposed as a faithful raw JsonObject (the per-resource
payloads are large and resource-specific, so they are not narrowed to typed
fields).
Wahlperiode (electoral term). The numbered legislative period of the
Bundestag (e.g. the 20th Wahlperiode). Most resources carry a wahlperiode
field and can be filtered with f.wahlperiode; document numbers are scoped to a
Wahlperiode.
Vorgangstyp (procedure type). The classification of a Vorgang (e.g.
Gesetzgebung — legislation, Antrag — motion, Kleine Anfrage — minor
interpellation). Filterable via f.vorgangstyp.
Dokumentart (document type). Whether a document is a Drucksache or a
Plenarprotokoll. Filterable via f.dokumentart on vorgang, vorgangsposition
and aktivitaet. The kind of Drucksache (Antrag, Gesetzentwurf, Antwort, …) is
drucksachetyp, filterable via f.drucksachetyp.
Datum (date). The date a document/activity is dated. Date-range filtering
uses f.datum.start and f.datum.end (ISO YYYY-MM-DD). The query builder
serialises Date values to full ISO-8601 instants in UTC
(2024-01-01T00:00:00.000Z), which DIP accepts for f.aktualisiert.* but not
for f.datum.* (HTTP 400 Invalid date). Library users pass f.datum.* as a
"YYYY-MM-DD" string: a Date is an instant, and a local midnight such as
new Date(2024, 0, 1) in Berlin is 2023-12-31T23:00:00.000Z, the day before.
Zuordnung (assignment). Some resources can be filtered by chamber/assignment
(f.zuordnung), distinguishing Bundestag (BT) from Bundesrat (BR) materials.
API behaviour & errors
Base URL. Defaults to https://search.dip.bundestag.de; override with
--base-url (CLI) or baseUrl (library). All resource paths are under
/api/v1, which the client adds: the base URL is the host (plus any mirror path
prefix) without it. The CLI and the library (validateBaseUrl) reject one that
does not parse, is not http:/https:, ends in /api/v1, has surrounding
whitespace or whitespace inside, has a query or fragment, or has a % in the user
name or password that is not an escape (a literal % is written %25), before any request,
with the same reason (library: DipValidationError, not DipNetworkError). Userinfo in it (user:password@) is sent, but redacted to ***@ in
everything the CLI prints and in the library’s errors.
Rate limiting. DIP limits the request rate; the API returns 429 when it is
exceeded. The client retries 429 and 503, and a connection reset mid-request,
automatically (--max-retries, 0–10, default 2), backing off linearly, or waiting
the response’s Retry-After when that is longer (never shorter, so Retry-After: 0
causes no burst). A Retry-After over 30 s is not retried, and the error names the
requested wait. Each retry is a WARN record of dip.http (HTTP 503 from host: retry 1 of 3
in 2 s); the library offers it as onRetry. The library holds the same bound
(MAX_RETRIES, maxRetries) and rejects a value outside it with
DipValidationError, as it does a negative or fractional timeout or size cap.
Credential stripping on redirect. The API key (and X-API-Key / Cookie, and
a base URL’s user:password@) goes only to the base URL’s origin. A redirect to
another origin — another host, port or scheme, http→https included — drops it, so
the key is never leaked to a host other than the one you targeted. Same-origin
redirects keep it, with an absolute Location too. A 401 after such a redirect is
not about the key, and the error says so (“use an https base URL” for http→https).
Error types. errors.ts: DipApiError (non-2xx,
carries status/detail/url/method/body, with isRetryable for 429/503),
DipNetworkError (transport failure/timeout, never a bad base URL), DipParseError (bad JSON, or a 2xx body that is not a DIP list or document: null, [], an error object),
DipUsageError (a usage error, no request made) and its subclass
DipValidationError (the library rejected an input before any request:
Invalid <name>: <reason>), all extending DipError. Exit codes: 0 success, 2 usage errors, 4 on a 404, 1 for
any other runtime error (including 401 when the key is missing/expired).
Log record. Every diagnostic line the CLI writes to stderr: a timestamp, a level
(ERROR, WARN, INFO) and a topic dip.<area>, as text (log4j style) or with
--log-format jsonl as one JSON object per line. The areas: cli (usage errors,
commander’s messages, unexpected errors), api (the API’s answers: an error status, and
a malformed answer — bad JSON, the wrong shape, an empty body), http (the connection,
the cleartext warning, and one WARN per retry before it waits), config, obtain-key and output (the -o file, stdout
failures). A record is always one line; control characters in it are escaped.
Library & internals. Terms for the TypeScript client and its internals —
DipClient, resource groups, the request engine, transport, retry/backoff, error types, query builder — now live in DEVELOPING.md.