Zwei Zusagen, auf die man sich verlassen kann — und muss
1. Die Antwort ist immer eine Liste, nie ein einzelnes Amt
Von den — Postleitzahlen im Verzeichnis führen — zu mehreren Ämtern, von den — Gemeindeschlüsseln —. In Berlin sind 17 Ämter für dieselbe Gemeinde zuständig, in Hamburg 10, in Köln 7. Was dort entscheidet — Bezirk, Straße oder Anfangsbuchstabe des Nachnamens — steht im amtlichen Verzeichnis nicht. Eine Schnittstelle, die in diesen Fällen eines der Ämter zurückgibt, liefert mit hoher Wahrscheinlichkeit das falsche.
Deshalb: aemter ist stets ein Array, und eindeutig sagt ausdrücklich, ob genau ein Amt zuständig ist. Bei eindeutig: false erklärt hinweis den Grund. Aufrufende Programme sollten in diesem Fall nachfragen statt zu wählen — die verlässliche Auskunft steht im letzten Steuerbescheid.
2. Der Gemeindeschlüssel ist der belastbare Schlüssel, die Postleitzahl ein Behelf
Der achtstellige amtliche Gemeindeschlüssel bezeichnet die Gemeinde eindeutig. Eine Postleitzahl kann dagegen bis zu 39 Gemeinden umfassen und über Gemeindegrenzen hinweg reichen. Wer den Schlüssel führt, sollte ags benutzen; plz ist für die Fälle gedacht, in denen nur die Anschrift bekannt ist.
Zugang
| Basisadresse | https://tools.steuersoft.de/api |
|---|---|
| Verfahren | GET für die Auskunft, HEAD zum reinen Nachsehen (Kopfzeilen mit ETag, kein Rumpf). Antwort application/json; charset=utf-8. |
| Zugangsschlüssel | Erforderlich. Jeder Aufruf führt ihn als Kopfzeile mit — entweder X-API-Key: <Schlüssel> oder Authorization: Bearer <Schlüssel>. Ohne Schlüssel antwortet die Schnittstelle mit 401, mit gesperrtem Schlüssel mit 403. Schlüssel werden von Steuersoft vergeben und sind jederzeit widerrufbar. |
| Nicht in der Adresse | Der Schlüssel wird ausschließlich als Kopfzeile angenommen, ?key=… wird abgewiesen. Grund: das Zugriffsprotokoll führt die angefragte Adresse mit — ein Schlüssel darin wäre dauerhaft mitgeschrieben. |
| Aufruf aus dem Browser | Technisch erlaubt (Access-Control-Allow-Origin: *), aber nicht empfohlen: der Schlüssel läge dann im Seitenquelltext der aufrufenden Anwendung und wäre damit für jeden lesbar. Die Schnittstelle gehört auf die Serverseite des aufrufenden Programms. |
| Zwischenspeicher | Cache-Control: public, max-age=3600. Das Verzeichnis wird monatlich fortgeschrieben; stand in jeder Antwort taugt zum Abgleich. |
| Häufigkeit | 20 Anfragen je Sekunde und Adresse, kurzzeitig mehr. Darüber antwortet der Dienst mit 429. Wer viele Fälle zuordnen muss, holt nicht einzeln ab, sondern einmal den Gesamtbestand und schlägt örtlich nach. |
| Fehler | 400 fehlende oder unbrauchbare Angabe · 401 Schlüssel fehlt oder unbekannt · 403 Schlüssel gesperrt · 404 Amtsnummer unbekannt · 429 zu häufig · 503 Verzeichnis vorübergehend nicht verfügbar. Der Text steht in detail. |
Ein Nachschlagen ohne Treffer ist kein Fehler: es antwortet mit 200, anzahl_aemter: 0 und einem hinweis.
Endpunkte
| Parameter | Bedeutung |
|---|---|
ags | Amtlicher Gemeindeschlüssel, achtstellig. Ein kürzerer Wert wirkt als Vorsatz und liefert alle Gemeinden darunter (z. B. 10 für das Saarland, 10044 für den Landkreis Saarlouis). |
plz | Postleitzahl, fünfstellig. |
ort | Gemeinde- oder Ortsname, mindestens drei Zeichen. Teiltreffer genügen, Groß-/Kleinschreibung und Umlaute sind unerheblich (gross-gerau findet Groß-Gerau). Genaue Namensgleichheit steht oben. |
gemeinden | 1 = zu jedem Amt gleich den vollständigen Zuständigkeitsbezirk mitliefern. Vorgabe 0. |
limit | Höchstzahl berücksichtigter Gemeindetreffer, 1–200, Vorgabe 20. |
GET /api/finanzamt?ags=10044111
{
"stand": "03.08.2026",
"stand_quelle": "20260803_0846",
"frage": { "ags": "10044111" },
"eindeutig": true,
"anzahl_aemter": 1,
"anzahl_gemeinden": 1,
"hinweis": null,
"aemter": [ { "bufa_nr": "1010", "name": "Saarlouis", … } ],
"gemeinden": [
{ "ags": "10044111", "name": "Dillingen/ Saar, Stadt",
"plz": "66763", "kreis": "Saarlouis", "bufa_nr": "1010" }
]
}
?gemeinden=1 zusätzlich der Bezirk.GET /api/finanzamt/1010
{ "stand": "03.08.2026", …, "amt": { "bufa_nr": "1010", … } }
GET /api/finanzamt/1010/bezirk
{ "stand": "03.08.2026", "bufa_nr": "1010",
"bezeichnung": "Finanzamt Saarlouis", "bundesland": "Saarland",
"ohne_bezirk": false, "anzahl": 13,
"gemeinden": [ { "ags": "10044111", "name": "Dillingen/ Saar, Stadt",
"plz": "66763", "kreis": "Saarlouis" }, … ] }
gemeinden=1 zusätzlich je Amt sein Zuständigkeitsbezirk, also die komplette Zuordnung Gemeinde → Amt (— Zuordnungen). Zum Vorhalten im aufrufenden Programm.| Parameter | Bedeutung |
|---|---|
| — keiner — | Der ganze Bestand. vollstaendig: true bestätigt, dass nichts abgeschnitten wurde. |
gemeinden | 1 = je Amt den vollständigen Zuständigkeitsbezirk mitliefern. Das ist der Abruf für eine eigene Zuordnungstabelle. |
bundesland | Name des Landes, Teiltreffer genügt. |
name | Teil des Amtsnamens, z. B. Körperschaften oder Bewertung. |
ohne_bezirk | 1 = nur Ämter ohne regionalen Bezirk, 0 = nur Ämter mit Bezirk. |
limit / offset | Nur nötig, wenn seitenweise abgeholt werden soll. limit 1–600; 0 oder weggelassen heißt: alle. gesamt nennt stets die Zahl aller Treffer, vollstaendig sagt, ob die Antwort sie alle enthält. |
GET /api/finanzaemter?gemeinden=1
{
"stand": "03.08.2026",
"vollstaendig": true,
"gesamt": 593,
"anzahl": 593,
"anzahl_gemeinden": 10818,
"aemter": [
{ "bufa_nr": "1010", "bezeichnung": "Finanzamt Saarlouis", …,
"gemeinden": [ { "ags": "10044111", "name": "Dillingen/ Saar, Stadt",
"plz": "66763", "kreis": "Saarlouis" }, … ] }, …
]
}
Den ganzen Bestand abholen
Für eine eigene Zuordnungstabelle genügt ein Abruf. Er liefert alle — Ämter samt ihren — Gemeinden:
curl --compressed -H "X-API-Key: $STS_FA_KEY" \
"https://tools.steuersoft.de/api/finanzaemter?gemeinden=1"
Etwa 1,4 MB unkomprimiert, rund 200 KB übertragen. Ohne gemeinden=1 sind es nur die Ämter ohne Bezirke (deutlich kleiner). Die Antwort ist je Datenstand unverändert dieselbe und wird in wenigen Millisekunden ausgeliefert.
Nachsehen, ob sich etwas geändert hat — ohne alles erneut zu laden
Der Bestand wird monatlich fortgeschrieben. Die Antwort trägt ein ETag; wer es beim nächsten Mal mitschickt, bekommt bei unverändertem Stand 304 Not Modified ohne Rumpf zurück:
# erster Abruf: ETag merken
curl -sD - -o bestand.json -H "X-API-Key: $STS_FA_KEY" \
"https://tools.steuersoft.de/api/finanzaemter?gemeinden=1" | grep -i ^etag
# ETag: W/"20260803_0846-mit_gemeinden"
# spaeter: nur pruefen, ob es etwas Neues gibt
curl -s -o /dev/null -w "%{http_code}\n" -H "X-API-Key: $STS_FA_KEY" \
-H 'If-None-Match: W/"20260803_0846-mit_gemeinden"' \
"https://tools.steuersoft.de/api/finanzaemter?gemeinden=1"
# 304 -> unveraendert, nichts zu tun
# 200 -> neuer Stand, Rumpf enthaelt ihn
Noch einfacher geht es mit HEAD: das liefert nur die Kopfzeilen samt ETag und Content-Length, ganz ohne Rumpf — auch ohne dass der Aufrufer bedingte Anfragen beherrschen muss.
curl -sI -H "X-API-Key: $STS_FA_KEY" \
"https://tools.steuersoft.de/api/finanzaemter?gemeinden=1" | grep -i etag
# ETag: W/"20260803_0846-mit_gemeinden"
Wer kein ETag auswerten will, vergleicht das Feld stand (bzw. stand_quelle) mit dem selbst vorgehaltenen Wert — es steht in jeder Antwort der Schnittstelle.
PowerShell
$kopf = @{ "X-API-Key" = $env:STS_FA_KEY }
$b = Invoke-RestMethod "https://tools.steuersoft.de/api/finanzaemter?gemeinden=1" -Headers $kopf
"{0} Ämter, {1} Gemeinden, Stand {2}" -f $b.gesamt, $b.anzahl_gemeinden, $b.stand
# Zuordnungstabelle Gemeindeschlüssel -> Amtsnummer aufbauen
$zuordnung = @{}
foreach ($amt in $b.aemter) {
foreach ($g in $amt.gemeinden) { $zuordnung[$g.ags] = $amt.bufa_nr }
}
$zuordnung["10044111"] # 1010
Achtung bei der eigenen Tabelle: ein Gemeindeschlüssel kann zu mehreren Ämtern gehören (Berlin, Hamburg, Köln und weitere Großstädte). Wer wie oben stumpf zuweist, behält je Schlüssel nur das zuletzt gesehene Amt. Für diese Fälle eine Liste je Schlüssel führen — oder sie an der Zahl der Ämter erkennen und zurückfragen.
Selbst ausprobieren
Noch nichts abgerufen.
Beispiele zum Einsetzen: /api/finanzamt?ags=10044111 · /api/finanzamt?plz=10178 (mehrere Ämter) · /api/finanzamt?ort=Dillingen · /api/finanzamt/1010?gemeinden=1 · /api/finanzamt/1010/bezirk · /api/finanzaemter?bundesland=Saarland · /api/finanzaemter (ganzer Bestand) · /api/finanzaemter?gemeinden=1 (mit allen Gemeinden)
Felder eines Amtes
| Feld | Inhalt |
|---|---|
bufa_nr | Vierstellige Amtsnummer (Bundesfinanzamtsnummer). Eindeutig, geeignet als Schlüssel in eigenen Datenbeständen. |
name · bezeichnung | Name des Amtes ohne bzw. mit vorangestelltem „Finanzamt“. |
bundesland | Land, in dem das Amt liegt. |
anschrift.art | dienstsitz oder zentraler_posteingang. Wichtig: — der Ämter teilen ihre Anschrift mit anderen — in Kiel 28, in Berlin 24, in Dresden 23. Diese Adresse ist der Posteingang des Landes, nicht das Dienstgebäude. anschrift.geteilt_mit_aemtern nennt die Zahl der Mitnutzer. Post dorthin muss die Amtsnummer tragen. |
postfach | Postfach des Amtes oder null. Für den Schriftwechsel meist die genauere Angabe als die Anschrift. |
kontakt.mail_art | poststelle oder datenschutzbeauftragter. Wichtig: Von den hinterlegten Adressen gehören die meisten dem Datenschutzbeauftragten, nicht der Poststelle. Wer kontakt.mail ungeprüft für Steuerpost verwendet, schreibt an die falsche Stelle — dieses Feld sagt, welche Art vorliegt. |
bankverbindung | IBAN, BIC, Kreditinstitut und Kontoinhaber der Finanzkasse, oder null. IBAN ohne Leerzeichen. |
oeffnungszeiten | Array von Freitextzeilen, wie im Verzeichnis geführt. Nicht strukturiert und nicht für maschinelle Auswertung geeignet. |
ohne_bezirk | true, wenn keine Gemeinden zugeordnet sind. Dahinter stehen überwiegend besondere Aufgaben — Grundsteuer-Bewertung und Groß-/Konzernbetriebsprüfung sind die größten Gruppen, dazu Körperschaften, Fahndung und Strafsachen, Verkehrsteuern, Erbschaft- und Schenkungsteuer —, teils Verwaltungsstellen anderer Ämter. Solche Ämter sind über den Wohnsitz nicht erreichbar, nur über Nummer oder Name. |
anzahl_gemeinden | Größe des Zuständigkeitsbezirks. |
Felder können hinzukommen; vorhandene Felder werden nicht umbenannt und nicht in ihrer Bedeutung geändert. Ein aufrufendes Programm sollte unbekannte Felder überlesen.
Beispiele
Kommandozeile
curl -s -H "X-API-Key: $STS_FA_KEY" \
"https://tools.steuersoft.de/api/finanzamt?ags=10044111"
Den Schlüssel nicht in Skripte schreiben, sondern aus einer Umgebungsvariablen oder einer nur für den Dienstnutzer lesbaren Datei ziehen.
PowerShell
$kopf = @{ "X-API-Key" = $env:STS_FA_KEY }
$a = Invoke-RestMethod "https://tools.steuersoft.de/api/finanzamt?plz=66740" -Headers $kopf
if ($a.eindeutig) { $a.aemter[0].bezeichnung } else { Write-Warning $a.hinweis }
Delphi
uses System.Net.HttpClient, System.JSON, System.NetEncoding;
function ZustaendigesAmt(const AGemeindeschluessel: string): string;
var
Client: THTTPClient;
Antwort: IHTTPResponse;
Wurzel: TJSONObject;
Aemter: TJSONArray;
begin
Result := '';
Client := THTTPClient.Create;
try
// Zugangsschluessel als Kopfzeile, nie als Adressbestandteil.
Client.CustomHeaders['X-API-Key'] := SchluesselAusKonfiguration;
Antwort := Client.Get('https://tools.steuersoft.de/api/finanzamt?ags=' +
TNetEncoding.URL.Encode(AGemeindeschluessel));
if Antwort.StatusCode <> 200 then Exit;
Wurzel := TJSONObject.ParseJSONValue(Antwort.ContentAsString(TEncoding.UTF8)) as TJSONObject;
try
// Mehrere zustaendige Aemter sind moeglich (Berlin, Hamburg, Koeln).
// Dann NICHT eines auswaehlen, sondern zurueckfragen.
if not Wurzel.GetValue<Boolean>('eindeutig') then Exit;
Aemter := Wurzel.GetValue<TJSONArray>('aemter');
if Aemter.Count > 0 then
Result := (Aemter.Items[0] as TJSONObject).GetValue<string>('bufa_nr');
finally
Wurzel.Free;
end;
finally
Client.Free;
end;
end;
JavaScript
// Serverseitig aufrufen — im Browser waere der Schluessel oeffentlich.
const r = await fetch('https://tools.steuersoft.de/api/finanzamt?plz=66740',
{ headers: { 'X-API-Key': process.env.STS_FA_KEY } });
const a = await r.json();
if (!a.eindeutig) console.warn(a.hinweis);
console.log(a.aemter.map(x => `${x.bufa_nr} ${x.bezeichnung}`));
Was das Verzeichnis nicht enthält
- Die Zuordnung innerhalb einer Großstadt. Welcher Bezirk, welche Straße oder welcher Anfangsbuchstabe zu welchem der 17 Berliner Ämter gehört, ist nicht Bestandteil des Verzeichnisses und kann über diese Schnittstelle nicht beantwortet werden.
- Die Aufgabenverteilung. Das Verzeichnis führt kein befülltes Aufgabenfeld. Welches Amt die Grundsteuer-Bewertung oder die Erbschaftsteuer eines Bezirks bearbeitet, lässt sich daher nur über den Amtsnamen erschließen, nicht sicher zuordnen.
- Ortsteile. Zugeordnet sind Gemeinden; Ortsteile stehen unter dem Namen ihrer Gemeinde.
- Postleitzahlen gemeindefreier Gebiete. Fünf Einträge — unbewohnte Gutsbezirke und Forstflächen — haben keine Postleitzahl; das Feld ist dort
null. - Zuständigkeit für andere Steuerarten. Die Zuordnung gibt den regionalen Bezirk wieder. Für Umsatzsteuer, Körperschaften, Lohnsteuer, gesonderte Feststellungen und Realsteuermessbeträge gelten eigene Vorschriften — siehe unten.
Rechtlicher Rahmen
Der zurückgegebene Bezirk gibt die örtliche Zuständigkeit nach dem Wohnsitz wieder — für die Einkommensteuer natürlicher Personen also § 19 Abs. 1 AO. Abweichend gelten: § 20 AO für Körperschaften (Ort der Geschäftsleitung), § 21 AO für die Umsatzsteuer (mit eigener Verordnung für Unternehmer ohne Sitz im Inland), § 41a Abs. 1 EStG für die Lohnsteuer (Betriebsstättenfinanzamt), § 18 AO für gesonderte Feststellungen, § 22 AO für Realsteuermessbeträge und § 35 ErbStG für die Erbschaft- und Schenkungsteuer. Der Zuständigkeitswechsel beim Umzug tritt nach § 26 AO ein, sobald das neue Amt von den maßgeblichen Umständen erfährt. Die sachliche Zuständigkeit richtet sich nach § 17 AO in Verbindung mit dem Finanzverwaltungsgesetz.
Die Angaben stammen aus dem amtlichen Verzeichnis der Finanzämter (Bundeszentralamt für Steuern) und enthalten keine personenbezogenen Daten. Zuständigkeiten und Erreichbarkeiten können sich seit dem angegebenen Stand geändert haben; im Zweifel gilt die Auskunft des Amtes. Die Schnittstelle wird ohne Zusage einer Verfügbarkeit bereitgestellt.