curl -H "Authorization: Bearer $GEO_API_KEY" \
"https://geodata.bitfertig.de/api/v1/geocode?q=Neue+Promenade+5,+10178+Berlin&limit=3"
const res = await fetch(
"https://geodata.bitfertig.de/api/v1/geocode?" + new URLSearchParams({
q: "Neue Promenade 5, 10178 Berlin",
limit: "3",
}),
{ headers: { Authorization: `Bearer ${process.env.GEO_API_KEY}` } },
);
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status}: ${error?.message ?? "Unbekannter Fehler"}`);
}
const { results } = await res.json();
console.log(results[0].location); // { lat: 52.52297, lon: 13.40171 }
import os
import httpx
client = httpx.Client(
base_url="https://geodata.bitfertig.de/api/v1",
headers={"Authorization": f"Bearer {os.environ['GEO_API_KEY']}"},
timeout=10.0,
)
response = client.get("/geocode", params={"q": "Neue Promenade 5, 10178 Berlin", "limit": 3})
response.raise_for_status()
for place in response.json()["results"]:
print(place["name"], place["location"])
use Illuminate\Support\Facades\Http;
$response = Http::withToken(config('services.geodata.key'))
->baseUrl('https://geodata.bitfertig.de/api/v1')
->get('/geocode', ['q' => 'Neue Promenade 5, 10178 Berlin', 'limit' => 3])
->throw();
$first = $response->json('results.0');
curl -H "Authorization: Bearer $GEO_API_KEY" \
"https://geodata.bitfertig.de/api/v1/reverse?lat=53.550341&lon=10.000654&limit=3"
async function nearestAddress(lat, lon) {
const params = new URLSearchParams({
lat: String(lat),
lon: String(lon),
limit: "3",
});
const res = await fetch(`https://geodata.bitfertig.de/api/v1/reverse?${params}`, {
headers: { Authorization: `Bearer ${process.env.GEO_API_KEY}` },
});
const { results } = await res.json();
// Ergebnisse kommen bereits nach Entfernung sortiert
return results.map((r) => ({
address: `${r.address.street} ${r.address.house_number}`,
city: `${r.address.postal_code} ${r.address.city}`,
distance: r.distance_m,
}));
}
response = client.get("/reverse", params={
"lat": 53.550341,
"lon": 10.000654,
"limit": 3,
})
for hit in response.json()["results"]:
print(f"{hit['distance_m']:>5} m {hit['name']}")
$nearest = Http::withToken(config('services.geodata.key'))
->baseUrl('https://geodata.bitfertig.de/api/v1')
->get('/reverse', [
'lat' => 53.550341,
'lon' => 10.000654,
'limit' => 3,
])
->throw()
->collect('results');
curl -X POST "https://geodata.bitfertig.de/api/v1/geocode/batch" \
-H "Authorization: Bearer $GEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"queries":["Neue Promenade 5, 10178 Berlin","Jakobikirchhof 21, 20095 Hamburg"],"limit":1}'
const res = await fetch("https://geodata.bitfertig.de/api/v1/geocode/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GEO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
queries: addresses.slice(0, 50), // max. 50 pro Aufruf
limit: 1,
}),
});
const { items } = await res.json();
# Adressen in Blöcke von 50 aufteilen – ein Aufruf kostet 5 Einheiten
def chunks(seq, size=50):
for i in range(0, len(seq), size):
yield seq[i:i + size]
for batch in chunks(addresses):
response = client.post("/geocode/batch", json={"queries": batch, "limit": 1})
response.raise_for_status()
for item in response.json()["items"]:
hit = item["results"][0] if item["results"] else None
print(item["query"], "→", hit["location"] if hit else "kein Treffer")
foreach (collect($addresses)->chunk(50) as $chunk) {
$items = Http::withToken(config('services.geodata.key'))
->baseUrl('https://geodata.bitfertig.de/api/v1')
->post('/geocode/batch', [
'queries' => $chunk->values()->all(),
'limit' => 1,
])
->throw()
->collect('items');
}
curl -H "Authorization: Bearer $GEO_API_KEY" "https://geodata.bitfertig.de/api/v1/me"
const res = await fetch("https://geodata.bitfertig.de/api/v1/me", {
headers: { Authorization: `Bearer ${process.env.GEO_API_KEY}` },
});
const { quota } = await res.json();
if (quota.remaining / quota.limit < 0.1) {
console.warn(`Nur noch ${quota.remaining} Einheiten bis ${quota.resets_at}`);
}
quota = client.get("/me").json()["quota"]
if quota["remaining"] < 1000:
raise RuntimeError(f"Kontingent knapp: {quota['remaining']} von {quota['limit']}")
$quota = Http::withToken(config('services.geodata.key'))
->baseUrl('https://geodata.bitfertig.de/api/v1')
->get('/me')
->throw()
->json('quota');
// Alternativ ohne Extra-Request: Header jeder Antwort auswerten
// $response->header('X-Quota-Remaining')
Über den MCP-Server entfällt der Code komplett – die KI ruft die Werkzeuge selbst auf. Wer die API stattdessen als eigenes Tool in einen Agenten einbaut, kann diese Werkzeugdefinition übernehmen:
{
"name": "geocode",
"description": "Löst eine Adresse, PLZ oder einen Ortsnamen in Koordinaten auf. Mit Hausnummer wird die exakte Position geliefert, sonst die Straßen- bzw. Ortsmitte. Der Bestand enthält Adressen, keine Points of Interest.",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"country": { "type": "string", "description": "ISO 3166-1 alpha-2, z. B. DE" },
"limit": { "type": "integer", "default": 5 }
},
"required": ["query"]
}
}
Jeder Treffer nennt in type seine Genauigkeit
(address, street oder city) – für einen Agenten
ist das der Hinweis, ob eine Angabe präzise genug war. Genau so arbeitet auch der
MCP-Server.