Code-Beispiele: PHP, Python, JavaScript, PowerShell, curl
Hier findest du Programmbeispiele zum Lesen, Anlegen, Ändern und Löschen eines Kontakts. Lass sie bei Bedarf von der Person einrichten, die dein anderes Programm betreut.
Wer hat Zugriff?
- Administratoren verwalten Zugänge unter System → API-Schlüssel und System → MCP.
- Programme brauchen einen aktiven, gültigen Schlüssel. Seine Stufe und Bereichsfreigaben begrenzen den Zugriff. Ein zugeordneter Kontakt muss aktiv sein; seine Rechte können zusätzlich einschränken.
- Schreibzugriffe brauchen zusätzlich Schreiben erlauben. Jeder Bereich hat eigene Lese- und Schreibrechte. Die Schnittstelle bietet nicht jede Funktion der Oberfläche an.
- Die Schnittstelle selbst legt keine API-Schlüssel an. Details: Rollen und Rechte.
Welche E-Mails werden verschickt?
- Lesen sowie Schlüssel anlegen, ändern oder sperren: keine E-Mails.
- Spezielle Assistent-Aktionen für Termine, Kunden und Tiere: keine Kundenmail und kein Rechnungsversand. Termine entstehen ohne Benachrichtigung.
- Andere Schreibaktionen: Beachte die Beschreibung des jeweiligen Bereichs. Eine Newsletter-Abmeldung kann beispielsweise eine Meldung an den Betreiber auslösen.
Kurz gesagt: Ersetze zuerst Beispieladresse und Beispielschlüssel. Die vollständigen Dateien findest du unter /api/beispiele/ deiner Instanz. Die Schreibbeispiele verändern echte Einträge in dieser Instanz.
Begriffe: API bedeutet Schnittstelle für andere Programme. Ein Endpunkt ist eine bestimmte Abrufadresse. JSON ist das Textformat der Antworten; eine Kennung (id) bezeichnet einen Eintrag.
In allen Beispielen sind zwei Werte einzutragen:
- die Adresse, zum Beispiel
https://deine-adresse.de/api/v1 - der Schlüssel aus System → API-Schlüssel
So bereitest du ein Beispiel vor
- Lege einen passenden Schlüssel unter System → API-Schlüssel an.
- Ersetze im gewählten Beispiel Adresse und Schlüssel.
- Führe zuerst nur den Leseaufruf aus.
- Prüfe die Antwort unter
data. - Verwende für Schreibversuche ausschließlich eigene Testeinträge.
1. So rufst du Daten im Terminal ab
curl ist ein Programm für Web-Anfragen. Ersetze in den ersten beiden Zeilen Adresse und Schlüssel.
Der schnellste Weg zum Ausprobieren. Denk an -g, sobald Filter mit eckigen Klammern vorkommen.
Was macht das? Diese Terminalbefehle lesen Daten und legen einen Testkontakt an. Verwende beim Ändern und Löschen ausschließlich dessen zurückgegebene Kennung.
BASE="https://deine-adresse.de/api/v1"
KEY="DEIN-API-SCHLUESSEL"
# Was darf mein Schlüssel?
curl -sg -H "Authorization: Bearer $KEY" "$BASE/me"
# Die ersten fünf Kontakte, nach Nachname sortiert
curl -sg -H "Authorization: Bearer $KEY" \
"$BASE/contacts?per_page=5&sort=lastname&fields=id,lastname,firstname,email"
# Filtern: Nachname enthält "Mül", angelegt seit 2026
curl -sg -H "Authorization: Bearer $KEY" \
"$BASE/contacts?lastname[like]=M%C3%BCl&created[gte]=2026-01-01"
# Anlegen
curl -sg -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: mein-einmaliger-test-001" \
-d '{"lastname":"Müller","firstname":"Erika","email":"erika@example.de"}' \
"$BASE/contacts"
# Trage hier ausschließlich data.id aus der eben erhaltenen Anlege-Antwort ein.
CONTACT_ID="KENNUNG-DES-NEUEN-TESTKONTAKTS"
# Ändern
curl -sg -X PATCH -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"city":"Neustadt"}' \
"$BASE/contacts/$CONTACT_ID"
# Löschen (Papierkorb)
curl -sg -X DELETE -H "Authorization: Bearer $KEY" "$BASE/contacts/$CONTACT_ID"
Übernimm nach dem Anlegen die neue Kennung aus data.id in CONTACT_ID. Erst danach führst du Ändern und Löschen aus. Für einen neuen Anlegeversuch verwendest du einen neuen Idempotency-Key.
Mit jq wird die Ausgabe lesbar:
Was macht das? Diese Befehle zeigen die Antwort übersichtlich und lesen die Gesamtzahl der Kontakte aus.
curl -sg -H "Authorization: Bearer $KEY" "$BASE/contacts?per_page=3" | jq .
# Nur die Gesamtzahl
curl -sg -H "Authorization: Bearer $KEY" \
"$BASE/contacts?per_page=1" | jq '.meta.total'
2. PHP
Was macht das? Dieses PHP-Beispiel liest und filtert Kontakte. Anschließend legt es einen Kontakt an, ändert ihn und löscht ihn.
<?php
$BASE = 'https://deine-adresse.de/api/v1';
$KEY = '3f7c9a2e-4b81-4d6f-9a03-7c5e12f8ab44';
function api(string $method, string $path,
array $query = [], ?array $body = null): array
{
global $BASE, $KEY;
$url = $BASE . $path;
if ($query) {
// http_build_query kodiert auch verschachtelte Filter richtig:
// ['lastname' => ['like' => 'Mül']] -> lastname%5Blike%5D=M%C3%BCl
$url .= '?' . http_build_query($query);
}
$headers = ['Authorization: Bearer ' . $KEY, 'Accept: application/json'];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_TIMEOUT => 30,
]);
if ($body !== null) {
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_POSTFIELDS,
json_encode($body, JSON_UNESCAPED_UNICODE));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$raw = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$data = json_decode((string) $raw, true) ?: [];
if ($status >= 400) {
throw new RuntimeException(sprintf('[%d %s] %s',
$status,
$data['error']['code'] ?? 'unknown',
$data['error']['message'] ?? 'Unbekannter Fehler'));
}
return $data;
}
// Lesen
$result = api('GET', '/contacts', [
'per_page' => 5,
'sort' => 'lastname',
'fields' => 'id,lastname,firstname,email',
]);
foreach ($result['data'] as $contact) {
printf("#%d %s, %s\n", $contact['id'],
$contact['lastname'], $contact['firstname']);
}
// Filtern
$treffer = api('GET', '/contacts', [
'lastname' => ['like' => 'Mül'],
'created' => ['gte' => '2026-01-01'],
]);
printf("%d Treffer\n", $treffer['meta']['total']);
// Anlegen
$neu = api('POST', '/contacts', [], [
'lastname' => 'Müller',
'firstname' => 'Erika',
'email' => 'erika.mueller@example.de',
]);
$id = $neu['data']['id'];
// Ändern und löschen
api('PATCH', '/contacts/' . $id, [], ['city' => 'Neustadt']);
api('DELETE', '/contacts/' . $id);
3. Python
Ohne Fremdbibliotheken — nur die Standardbibliothek.
Was macht das? Dieses Python-Beispiel liest und filtert Kontakte. Anschließend legt es einen Kontakt an, ändert ihn und löscht ihn.
#!/usr/bin/env python3
import json
import urllib.parse
import urllib.request
BASE = "https://deine-adresse.de/api/v1"
KEY = "3f7c9a2e-4b81-4d6f-9a03-7c5e12f8ab44"
def encode_query(query):
"""Verschachtelte Filter zur Klammerschreibweise der Schnittstelle."""
parts = []
for key, value in query.items():
if isinstance(value, dict):
for op, operand in value.items():
parts.append((f"{key}[{op}]", str(operand)))
else:
parts.append((key, str(value)))
return urllib.parse.urlencode(parts)
def api(method, path, query=None, body=None):
url = BASE + path
if query:
url += "?" + encode_query(query)
headers = {"Authorization": f"Bearer {KEY}", "Accept": "application/json"}
data = None
if body is not None:
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
headers["Content-Type"] = "application/json"
request = urllib.request.Request(url, data=data,
headers=headers, method=method)
try:
with urllib.request.urlopen(request, timeout=30) as response:
raw = response.read().decode("utf-8")
return json.loads(raw) if raw else {}
except urllib.error.HTTPError as exc:
error = json.loads(exc.read().decode("utf-8")).get("error", {})
raise RuntimeError(
f"[{exc.code} {error.get('code')}] {error.get('message')}"
) from None
# Lesen
result = api("GET", "/contacts", {
"per_page": 5,
"sort": "lastname",
"fields": "id,lastname,firstname,email",
})
for contact in result["data"]:
print(f"#{contact['id']} {contact['lastname']}, {contact['firstname']}")
# Filtern
treffer = api("GET", "/contacts", {
"lastname": {"like": "Mül"},
"created": {"gte": "2026-01-01"},
})
print(f"{treffer['meta']['total']} Treffer")
# Anlegen, ändern, löschen
neu = api("POST", "/contacts", body={
"lastname": "Müller",
"firstname": "Erika",
"email": "erika.mueller@example.de",
})
record_id = neu["data"]["id"]
api("PATCH", f"/contacts/{record_id}", body={"city": "Neustadt"})
api("DELETE", f"/contacts/{record_id}")
# Alle Einträge durchlaufen — Seite für Seite, speicherschonend
def alle(resource, **query):
page = 1
while True:
result = api("GET", f"/{resource}",
{**query, "page": page, "per_page": 200})
for row in result["data"]:
yield row
if page >= result["meta"]["pages"]:
return
page += 1
for kontakt in alle("contacts", fields="id,lastname"):
pass # hier verarbeiten
4. JavaScript (Node.js)
Braucht Node 18 oder neuer — dort ist fetch eingebaut.
Was macht das? Dieses JavaScript-Beispiel liest und filtert Kontakte. Anschließend legt es einen Kontakt an, ändert ihn und löscht ihn.
const BASE = 'https://deine-adresse.de/api/v1';
const KEY = '3f7c9a2e-4b81-4d6f-9a03-7c5e12f8ab44';
function buildQuery(query) {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value === null || value === undefined) continue;
if (typeof value === 'object' && !Array.isArray(value)) {
// Verschachtelte Filter: { lastname: { like: 'Mül' } }
for (const [op, operand] of Object.entries(value)) {
params.append(`${key}[${op}]`, String(operand));
}
} else {
params.append(key, String(value));
}
}
const s = params.toString();
return s ? `?${s}` : '';
}
async function api(method, path, { query = {}, body = null } = {}) {
const headers = { Authorization: `Bearer ${KEY}`, Accept: 'application/json' };
const init = { method, headers };
if (body !== null) {
headers['Content-Type'] = 'application/json';
init.body = JSON.stringify(body);
}
const response = await fetch(BASE + path + buildQuery(query), init);
const text = await response.text();
const data = text ? JSON.parse(text) : {};
if (!response.ok) {
const e = data.error ?? {};
throw new Error(`[${response.status} ${e.code}] ${e.message}`);
}
return data;
}
// Lesen
const result = await api('GET', '/contacts', {
query: { per_page: 5, sort: 'lastname',
fields: 'id,lastname,firstname,email' },
});
for (const c of result.data) {
console.log(`#${c.id} ${c.lastname}, ${c.firstname}`);
}
// Filtern
const treffer = await api('GET', '/contacts', {
query: { lastname: { like: 'Mül' }, created: { gte: '2026-01-01' } },
});
console.log(`${treffer.meta.total} Treffer`);
// Anlegen, ändern, löschen
const neu = await api('POST', '/contacts', {
body: { lastname: 'Müller', firstname: 'Erika',
email: 'erika.mueller@example.de' },
});
const id = neu.data.id;
await api('PATCH', `/contacts/${id}`, { body: { city: 'Neustadt' } });
await api('DELETE', `/contacts/${id}`);
Im Browser läuft derselbe Code — dann muss allerdings deine Adresse unter api.cors.origins freigegeben sein, und der Schlüssel stünde im Seitenquelltext. Lass eine Website-Anbindung deshalb über deinen eigenen Server ausführen. Die Schnittstelle erlaubt fremde Browser-Adressen nur nach entsprechender Freigabe.
5. PowerShell
Läuft unter Windows PowerShell 5.1 und unter PowerShell 7.
Was macht das? Dieses PowerShell-Beispiel liest und filtert Kontakte. Anschließend legt es einen Kontakt an, ändert ihn und löscht ihn.
$BaseUrl = 'https://deine-adresse.de/api/v1'
$ApiKey = '3f7c9a2e-4b81-4d6f-9a03-7c5e12f8ab44'
# Windows PowerShell 5.1 spricht standardmäßig noch TLS 1.0 — hier umstellen
if ($PSVersionTable.PSVersion.Major -lt 6) {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}
function ConvertTo-PwQuery {
param([hashtable] $Query)
if (-not $Query -or $Query.Count -eq 0) { return '' }
$parts = @()
foreach ($key in $Query.Keys) {
$value = $Query[$key]
if ($value -is [hashtable]) {
foreach ($op in $value.Keys) {
$name = [System.Uri]::EscapeDataString("$key[$op]")
$wert = [System.Uri]::EscapeDataString([string]$value[$op])
$parts += "$name=$wert"
}
} else {
$name = [System.Uri]::EscapeDataString($key)
$wert = [System.Uri]::EscapeDataString([string]$value)
$parts += "$name=$wert"
}
}
return '?' + ($parts -join '&')
}
function Invoke-PwApi {
param(
[Parameter(Mandatory)][string] $Path,
[string] $Method = 'GET',
[hashtable] $Query = @{},
[object] $Body = $null
)
$params = @{
Uri = $script:BaseUrl.TrimEnd('/') + $Path +
(ConvertTo-PwQuery -Query $Query)
Method = $Method
Headers = @{
'Authorization' = "Bearer $script:ApiKey"
'Accept' = 'application/json'
}
TimeoutSec = 30
ErrorAction = 'Stop'
}
if ($null -ne $Body) {
# Die Umwandlung nach UTF-8 ist nötig, sonst kommen Umlaute falsch an.
$json = $Body | ConvertTo-Json -Depth 10 -Compress
$params['Body'] = [Text.Encoding]::UTF8.GetBytes($json)
$params['ContentType'] = 'application/json; charset=utf-8'
}
Invoke-RestMethod @params
}
# Lesen
$result = Invoke-PwApi -Path '/contacts' -Query @{
per_page = 5; sort = 'lastname'; fields = 'id,lastname,firstname,email'
}
$result.data | Format-Table -AutoSize id, lastname, firstname, email
# Filtern
$treffer = Invoke-PwApi -Path '/contacts' -Query @{
lastname = @{ like = 'Mül' }
created = @{ gte = '2026-01-01' }
}
Write-Host "$($treffer.meta.total) Treffer"
# Anlegen, ändern, löschen
$neu = Invoke-PwApi -Path '/contacts' -Method POST -Body @{
lastname = 'Müller'; firstname = 'Erika'; email = 'erika.mueller@example.de'
}
$id = $neu.data.id
Invoke-PwApi -Path "/contacts/$id" -Method PATCH `
-Body @{ city = 'Neustadt' } | Out-Null
Invoke-PwApi -Path "/contacts/$id" -Method DELETE | Out-Null
Variablennamen in PowerShell unterscheiden nicht zwischen Groß- und Kleinschreibung. Eine Variable $raw ist dieselbe wie ein Schalter -Raw. Verwende für Inhalt und Schalter eindeutig verschiedene Namen; im vollständigen Beispiel ist das so gelöst.
6. Und in anderen Sprachen?
Die Schnittstelle ist gewöhnliches REST mit JSON. Jede Sprache, die HTTP sprechen kann, kommt damit klar — Java, C#, Go, Ruby, Rust, Swift, ABAP.
Am schnellsten geht es über die Beschreibungsdatei:
Was macht das? Dieser Befehl speichert die Schnittstellenbeschreibung als Datei für andere Entwicklungswerkzeuge.
curl -sg -H "Authorization: Bearer $KEY" "$BASE/openapi.json" -o petworkers.json
Daraus erzeugen die üblichen Werkzeuge (openapi-generator, NSwag, oapi-codegen) einen fertigen Client in der Sprache deiner Wahl.
Die vollständigen Fassungen findest du unter https://deine-adresse.de/api/beispiele/, auch als ZIP-Paket. Ersetze auch dort die Beispieladresse.
Wenn etwas nicht klappt
- 401 unauthorized: Ersetze den Beispielschlüssel überall durch deinen gültigen Schlüssel.
- 403 forbidden beim Anlegen: Aktiviere Schreiben erlauben und prüfe das Schreibrecht für contacts.
- Eine Liste endet nach der ersten Seite: Die kurzen Beispiele zeigen nur eine Seite. Ergänze den auf der Leseseite erklärten Seitenabruf.
- curl lehnt Filter ab: Verwende -g gegen die Klammerauswertung. Kodiere außerdem Leerzeichen und Sonderzeichen in der URL.