Was kann die ImmoScout24-API?
Die Plattform stellt zwei API-Welten bereit. Im Tutorial geht es um die ältere, aber wichtigere Importexport-API (auch Restapi genannt), über die Makler-Software, CRMs und eigene Anwendungen Inserate auf IS24 publizieren. Der Endpoint hat das Basis-Schema https://rest.immobilienscout24.de/restapi/api/offer/v1.0 und exponiert Ressourcen wie /realestate, /realestate/{id}/attachment und /publish.
Daneben existiert die Search-API für Lese-Zugriffe auf das öffentliche Inventar — relevant für Vergleichs-, Bewertungs- oder Analyse-Tools. Beide laufen über denselben OAuth-1.0a-Mechanismus, unterscheiden sich aber in Berechtigungsmodell und Rate-Limit.
Die wichtigsten Use-Cases, für die wir die Anbindung bauen:
- CRM-Sync — Listings aus Propstack, OnOffice, FlowFact oder einem eigenen System automatisch zu IS24 spiegeln.
- Bulk-Onboarding — initialer Import eines Bestands beim Wechsel zwischen CRMs, idempotent mit eigenen Referenzen (
externalId). - Veröffentlichungs-Workflows — Inserate je nach Lagerstatus, Kampagnen-Logik oder Zahlung freischalten und wieder zurückziehen.
- Reporting — Performance-Daten (Aufrufe, Anfragen) zurück ins eigene CRM holen.
Zugang beantragen: Welche Rolle brauchst du?
Bevor eine einzige Zeile Code läuft, brauchst du vier Geheimnisse: consumerKey, consumerSecret, accessToken und tokenSecret. Das Consumer-Paar bekommst du nach Antrag im ImmoScout24 Partner Portal; das Token-Paar erzeugst du anschließend pro Kunde im Rahmen eines klassischen OAuth-1.0a-Drei-Schritt-Flows (request token → user authorization → access token).
Praxis-Hinweis: Für die meisten B2B-Anbindungen reicht der Rollen-Typ „Customer-Side Application". Wenn du eine Multi-Tenant-SaaS für viele Maklerbüros betreibst, brauchst du zusätzlich die „Partner-Side"-Freischaltung — damit kannst du im Namen verschiedener Kunden-Accounts publishen, ohne dass jeder Kunde sein eigenes Consumer-Pair sieht.
Faustregel: Wenn dein Projekt mehr als zwei Maklerbüros bedient, lohnt der Aufwand für den Partner-Status spätestens ab Monat 2. Vorher kommst du mit einzelnen Customer-Tokens schneller voran.
OAuth 1.0a in Node.js — Live-Code
OAuth 1.0a verlangt eine kryptographische Signatur pro Request. Wir signieren mit HMAC-SHA1 über einen normalisierten Parameter-String. Das Paket oauth-1.0a erspart einem die meisten Fallstricke; in Kombination mit node-fetch oder dem nativen fetch ab Node 18 entsteht ein schlanker Client:
// is24-client.js — minimaler IS24-Client
import OAuth from 'oauth-1.0a';
import crypto from 'node:crypto';
const oauth = OAuth({
consumer: {
key: process.env.IS24_CONSUMER_KEY,
secret: process.env.IS24_CONSUMER_SECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base, key) {
return crypto.createHmac('sha1', key).update(base).digest('base64');
},
});
const token = {
key: process.env.IS24_ACCESS_TOKEN,
secret: process.env.IS24_TOKEN_SECRET,
};
const BASE = 'https://rest.immobilienscout24.de/restapi/api/offer/v1.0';
export async function is24Request(path, { method = 'GET', body, contentType = 'application/json' } = {}) {
const url = `${BASE}${path}`;
const requestData = { url, method };
const authHeader = oauth.toHeader(oauth.authorize(requestData, token));
const res = await fetch(url, {
method,
headers: {
...authHeader,
'Accept': 'application/json',
...(body ? { 'Content-Type': contentType } : {}),
},
body: body && contentType === 'application/json' ? JSON.stringify(body) : body,
});
if (!res.ok) {
const text = await res.text();
throw new Error(`IS24 ${res.status}: ${text}`);
}
return res.status === 204 ? null : res.json();
}
Das Skelett ist bewusst klein gehalten — kein Retry, kein Throttling. Beides bauen wir später als Wrapper drumherum. Wichtig ist nur: Die Signatur muss genau die finale URL abdecken, inklusive Query-Parameter. Wer hier mit URL-Builder-Bibliotheken jongliert, riskiert 401 Invalid signature-Fehler, weil die Encoding-Reihenfolge nicht stimmt.
Inserate lesen, anlegen, aktualisieren
Im IS24-Datenmodell gibt es eine Vielzahl von Realestate-Typen — Wohnung kaufen, Haus mieten, Anlageobjekt, Gastronomie usw. Jeder Typ hat ein eigenes Schema mit Pflicht- und Kann-Feldern. Wir bauen ein einfaches Beispiel mit einer Eigentumswohnung zum Kauf (ApartmentBuy):
// create-listing.js
import { is24Request } from './is24-client.js';
const apartment = {
'realEstate.apartmentBuy': {
title: 'Lichtdurchflutete 3-Zimmer-Wohnung in Eppendorf',
externalId: 'EXT-2026-1042',
address: {
street: 'Kümmellstraße',
houseNumber: '12',
postcode: '20249',
city: 'Hamburg',
internationalCountryRegion: { country: 'GERMANY', region: 'Hamburg' },
},
numberOfRooms: 3,
livingSpace: 78.5,
baseRent: null,
price: { value: 645000.0, currency: 'EUR', marketingType: 'PURCHASE', priceIntervalType: 'ONE_TIME_CHARGE' },
condition: 'WELL_KEPT',
constructionYear: 1928,
energyCertificate: {
energyCertificateAvailability: 'AVAILABLE',
energyConsumptionContainsWarmWater: false,
buildingEnergyRatingType: 'ENEV_2014',
energyPerformanceCertificate: true,
thermalCharacteristic: 89.0,
energyEfficiencyClass: 'C',
},
descriptionNote: 'Saniert 2019, eigener Balkon, kein Provisionsteilung.',
showAddress: true,
},
};
const created = await is24Request('/user/me/realestate', { method: 'POST', body: apartment });
console.log('Created realestate ID:', created['realEstate.apartmentBuy'].id);
Drei Dinge, die in der Doku gerne untergehen:
externalIdist dein Anker für Idempotenz. Setze ihn auf einen stabilen Wert aus deinem CRM (z. B. Propstack-Property-ID), damit du beim erneuten Lauf das passende Realestate findest, ohne IS24-IDs lokal zu pflegen.- Das Wurzel-Objekt ist genau ein Property mit dem Realestate-Typ als Key. Schiebst du mehrere Typen oder schreibst du
realEstatestattrealEstate.apartmentBuy, antwortet IS24 mit415 Unsupported Media Type. - Update läuft per
PUT /user/me/realestate/ext-{externalId}— das ist der Trick, mit dem du ohne IS24-ID arbeiten kannst, solange deinexternalIdeindeutig ist.
Bilder und Anhänge hochladen
Bilder gehen nicht im JSON-Payload, sondern als separater Multipart-Request gegen /realestate/{id}/attachment. Reihenfolge: Erst das Realestate-Objekt anlegen, dann pro Bild ein Multipart-POST. Beachte den titlePicture-Flag — nur eines pro Listing.
// upload-image.js
import fs from 'node:fs';
import FormData from 'form-data';
export async function uploadAttachment(realestateId, filePath, { title, titlePicture = false } = {}) {
const form = new FormData();
const metadata = {
attachment: { '@xsi.type': 'common:Picture', title, titlePicture },
};
form.append('metadata', JSON.stringify(metadata), { contentType: 'application/json' });
form.append('attachment', fs.createReadStream(filePath));
const path = `/user/me/realestate/${realestateId}/attachment`;
return is24Request(path, {
method: 'POST',
body: form,
contentType: `multipart/form-data; boundary=${form.getBoundary()}`,
});
}
IS24 akzeptiert JPEG, PNG und PDF — Bilder werden serverseitig auf maximal 1.500 × 1.000 px verkleinert. Wer das Hochladen parallelisiert, läuft schnell ins Rate-Limit; sequenziell oder mit einem kleinen Konkurrenz-Pool (max. drei gleichzeitig) ist die sichere Variante.
Status- und Veröffentlichungs-Updates
Ein Inserat existiert in zwei Welten: als Realestate-Objekt und als Veröffentlichung. Das Anlegen eines Realestate alleine ist noch nicht live. Du musst es separat publishen — und zwar pro Channel:
// publish.js
const publishRequest = {
'publish.publishObject': {
realEstate: { '@id': realestateId },
publishChannel: { '@id': 10000 /* IS24-Hauptkanal */ },
},
};
await is24Request('/user/me/publish', { method: 'POST', body: publishRequest });
Für das De-Listing schickst du einen DELETE-Request gegen denselben Endpoint mit der publishObject-ID. Wichtig: Inserate, die noch eine aktive Vermarktungslaufzeit haben, kannst du nicht hart löschen — nur depublishen. Dauerhafte Löschung erfolgt frühestens nach Ablauf der Laufzeit.
Statusabfragen laufen über GET /publish?realestate={id}. Die Plattform bietet keine Realtime-Webhooks für Veröffentlichungsstatus, daher pollen wir alle paar Minuten. Bei großen Beständen lohnt eine zentrale Job-Queue (BullMQ, AWS SQS), die Updates priorisiert.
Typische Stolpersteine — und ihre Lösung
Aus über drei Jahren Praxis mit IS24-Anbindungen die Klassiker, die jedem Team mindestens einmal passieren:
1. 401 Invalid signature trotz korrekter Keys
Fast immer ein Encoding-Problem in der Signatur-Basis-String. Prüfe, ob du den Body bei POST aus der Signatur ausschließt (richtig) und nur Query-Parameter sowie OAuth-Parameter einbeziehst.
2. 415 Unsupported Media Type beim Realestate-POST
Du hast den Realestate-Typ-Key falsch gesetzt oder im JSON-Wurzel-Element fehlt der Namensraum-Prefix. Validiere das Payload-Schema gegen die offizielle XSD — IS24 ist hier penibel.
3. Idempotente Updates schlagen fehl
Häufige Ursache: externalId mit Sonderzeichen oder Whitespace. Verwende nur [A-Za-z0-9_-], maximal 60 Zeichen. Wir hashen die CRM-ID zur Sicherheit nochmal vor dem Senden.
4. Bilder-Upload bricht ab
Achte auf den korrekten boundary-String in der Content-Type-Header-Zeile. Bibliotheken wie form-data setzen den, manche HTTP-Clients überschreiben den Header bei nachträglichem Setzen.
5. Rate-Limit-Hits unter Last
IS24 limitiert pro Consumer auf etwa 60 Requests pro Minute. Implementiere ein Token-Bucket-Throttle in deinem Client; sonst bekommst du intermittierende 503-Responses, die wie zufällige Fehler aussehen.
Wann sich die direkte Anbindung lohnt
Wenn du weniger als ein paar Dutzend Inserate verwaltest und ein CRM nutzt, das die Schnittstelle bereits eingebaut hat (Propstack, OnOffice, FlowFact, Justimmo), nimmst du das CRM. Eigene Anbindung lohnt sich, wenn:
- du mehrere Maklerbüros bedienst und einen einheitlichen Workflow brauchst,
- dein Inseratsfluss eigene Geschäftslogik trägt (z. B. Zahlungs-Gate, automatische Reaktivierung, Multi-Portal-Spiegelung),
- du Reporting-Daten in eine BI-Pipeline ziehen willst,
- du eine SaaS für Makler baust und IS24 als einer von mehreren Veröffentlichungskanälen dient.
Bei DevNest bauen wir solche Anbindungen seit Jahren — als Stand-Alone-Bridge zwischen CRM und Portal oder als Teil einer kompletten Propstack-Integration. Wenn du an einer ähnlichen Lösung arbeitest, schreib uns: Projekt starten.
Weitere Themen: ImmoScout-Portal-Anbindung · Propstack-Entwicklung · API-Entwicklung Hamburg · Backend-API-Entwicklung: REST vs. GraphQL