← Alle Artikel

So Findest du die Versteckte JSON-API Hinter Einer JavaScript-Website

Du lädst eine Produktseite mit requests herunter, gibst das HTML aus, und der Preis ist nicht da. Der Titel auch nicht, die Bewertung nicht, der Lagerbestand nicht. Die Seite sah vor einer Sekunde im Browser noch gut aus. Wo sind also die Daten hin?

Sie stecken in einer zweiten Anfrage. Moderne Websites liefern eine fast leere HTML-Hülle, holen dann den echten Inhalt als JSON von einer internen API und malen ihn mit JavaScript ein. Dein HTTP-Client hört nach dem ersten Schritt auf. Der Browser macht weiter.

Die gute Nachricht: Diese interne API ist meist direkt erreichbar. Wenn du sie findest, umgehst du das gesamte Rendering-Problem. Kein Headless-Browser, kein Warten auf das DOM, kein Parsen von brüchigem HTML. Du bekommst sauberes JSON, das oft besser strukturiert ist als alles auf der Seite. Diese Anleitung zeigt dir, wie du sie findest, wie du die Anfrage nachbaust und was zu tun ist, wenn die Website sich wehrt.

Warum die Daten Nicht im HTML Stehen

Die meisten interaktiven Websites sind Single-Page-Anwendungen, gebaut mit React, Vue, Angular oder einem Framework wie Next.js oder Nuxt. Wenn du die Seite lädst, liefert der Server ein Gerüst. Dann läuft clientseitiger Code im Browser und ruft einen oder mehrere Backend-Endpunkte für die eigentlichen Daten auf.

Du kannst das in zwei Sekunden bestätigen. Rechtsklick auf die Seite und “Seitenquelltext anzeigen” wählen (nicht “Untersuchen”). Das zeigt das rohe HTML, das dein HTTP-Client erhalten würde. Wenn du ein leeres <div id="root"></div>, eine Wand aus <script>-Tags oder eine Meldung siehst, die dich bittet, JavaScript zu aktivieren, wird der Inhalt später geladen. Die gewünschten Daten liegen hinter einem API-Aufruf, nicht in diesem Dokument.

Es gibt drei übliche Orte, aus denen diese Daten stammen:

  • XHR/Fetch-Aufrufe an einen JSON- oder GraphQL-Endpunkt, nachdem die Seite geladen ist.
  • Inline-JSON, eingebettet im ursprünglichen HTML, oft in einem <script id="__NEXT_DATA__" type="application/json">-Block oder einer Zuweisung window.__INITIAL_STATE__ = {...}.
  • Eine Mischung: Ein Teil der Daten inline, der Rest wird beim Scrollen oder Klicken nachgeladen.

Deine Aufgabe ist herauszufinden, womit du es zu tun hast, und dann die Daten von der Quelle zu holen statt von der gerenderten Seite.

Schritt 1: Öffne den Network-Tab und Filtere nach Fetch/XHR

Öffne die Seite in Chrome oder Firefox, öffne die DevTools (F12) und gehe zum Network-Tab (Netzwerk). Lade die Seite mit geöffnetem Tab neu, damit von Anfang an alles erfasst wird.

Du siehst Dutzende Einträge: Bilder, Schriften, CSS, Tracking-Pixel. Ignoriere sie. Klicke auf den Filter Fetch/XHR, um nur die Anfragen anzuzeigen, die JavaScript im Hintergrund gestellt hat. Hier leben die API-Aufrufe.

Interagiere jetzt mit der Seite wie ein Nutzer. Scrolle nach unten, klicke auf “Mehr laden”, öffne ein Produkt, ändere einen Filter. Beobachte, wie im Network-Tab neue Zeilen erscheinen, während du das tust. Jede Zeile ist eine Anfrage, die die Website für dich gestellt hat. Eine davon trägt deine Daten.

Um die richtige zu finden, klicke auf eine Anfrage und schau in den Response- oder Preview-Tab. Du suchst eine Antwort, die die Werte enthält, die du auf der Seite gesehen hast: den Preis, die Artikelliste, den Rezensionstext. Wenn du JSON mit deinen Feldern darin siehst, hast du den Endpunkt gefunden.

Ein paar Tipps, die Zeit sparen:

  • Sortiere nach Antwortgröße. Datenendpunkte sind meist größer als Pings und Konfigurationsaufrufe.
  • Tippe einen bekannten Wert in das Filterfeld von Network (einen Produktnamen, einen Preis). Chrome kann mit dem Suchpanel (Strg+Umschalt+F innerhalb der DevTools) auch in Antwortkörpern suchen.
  • Achte auf Name und Pfad der Anfrage. Endpunkte wie /api/v2/products, /graphql oder /_next/data/...json sind starke Signale.

Schritt 2: Lies die Anfrage, Bevor du Sie Kopierst

Sobald du den Endpunkt hast, klicke ihn an und studiere die Anfrage selbst, nicht nur die Antwort. Du musst sie genau genug reproduzieren, um dieselbe Antwort zu erhalten.

Prüfe diese Teile im Headers-Tab (Header):

  • Methode und URL. Ist es GET oder POST? Notiere jeden Query-Parameter. Parameter wie page, limit, offset, cursor und sort sind deine Steuerung für Paginierung und Sortierung.
  • Anfrage-Header. Viele APIs prüfen Header, die der Browser automatisch sendet. Häufige, die zählen: Accept: application/json, ein Referer, ein X-Requested-With und manchmal ein eigener Header wie X-Api-Key oder X-Client-Version.
  • Anfragekörper. Bei POST- und GraphQL-Aufrufen enthält der Körper die Abfrage und die Variablen. Kopiere ihn zunächst unverändert.
  • Cookies und Tokens. Manche Endpunkte brauchen ein Session-Cookie oder ein Bearer-Token, das die Website beim Laden der Seite ausgestellt hat.

Der schnellste Weg zu einer funktionierenden Basis ist “Copy as cURL”. Rechtsklick auf die Anfrage, wähle Kopieren > Als cURL kopieren und füge es in dein Terminal ein. Wenn es dasselbe JSON zurückgibt, hast du eine getreue Reproduktion. Von dort aus kannst du sie eindampfen.

Schritt 3: Baue Sie in Code Nach und Kürze den Ballast

Beginne mit der vollständigen kopierten Anfrage, prüfe, dass sie funktioniert, und entferne dann Header einen nach dem anderen, bis es bricht. Was übrig bleibt, ist das Minimum, das du wirklich brauchst. Das hält deinen Scraper einfach und weniger brüchig.

Hier ein minimales Python-Beispiel, das einen typischen JSON-Endpunkt aufruft:

import requests

url = "https://example.com/api/v2/products"
params = {"category": "shoes", "page": 1, "limit": 48}
headers = {
    "Accept": "application/json",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    "Referer": "https://example.com/category/shoes",
}

resp = requests.get(url, params=params, headers=headers, timeout=20)
data = resp.json()

for item in data["products"]:
    print(item["title"], item["price"])

Für einen GraphQL-Endpunkt sende ein POST mit der Abfrage und den Variablen, die du kopiert hast:

import requests

payload = {
    "query": "query Products($page:Int!){ products(page:$page){ title price } }",
    "variables": {"page": 1},
}
resp = requests.post("https://example.com/graphql", json=payload, timeout=20)
print(resp.json()["data"]["products"])

In Node mit fetch sieht es fast identisch aus:

const res = await fetch("https://example.com/api/v2/products?page=1&limit=48", {
  headers: { Accept: "application/json", Referer: "https://example.com/category/shoes" },
});
const data = await res.json();
data.products.forEach((p) => console.log(p.title, p.price));

Schritt 4: Behandle die Paginierung Über die API, Nicht die Seite

Hier zahlt sich der direkte API-Zugriff aus. Die Seite zeigt vielleicht einen “Mehr laden”-Button oder Infinite Scroll, aber die API stellt fast immer eine saubere Paginierungssteuerung bereit. Schau, wie sich die Parameter zwischen der ersten und zweiten Anfrage im Network-Tab ändern.

Meist siehst du eines von drei Mustern:

  • Seitenzahlen: ?page=1, ?page=2. Iteriere, bis du eine leere Liste bekommst.
  • Offset und Limit: ?offset=0&limit=48, dann ?offset=48. Erhöhe um das Limit.
  • Cursor-basiert: Jede Antwort enthält ein nextCursor- oder endCursor-Token, das du an die nächste Anfrage übergibst. Folge ihm, bis es null ist.

Cursor-Paginierung ist bei großen Feeds üblich und die zuverlässigste der drei. Sie umgeht außerdem oft die Deep-Pagination-Limits, die seitenzahlbasierte Crawler bei großen Katalogen brechen.

Schritt 5: Wenn das Inline-JSON Genügt

Manchmal brauchst du nicht einmal eine zweite Anfrage. Framework-Websites betten den vollständigen Datensatz oft in die erste HTML-Antwort ein. Sieh dir den Seitenquelltext an und suche nach __NEXT_DATA__, __NUXT__, __INITIAL_STATE__ oder application/ld+json. Wenn deine Daten dort sind, parse das HTML einmal, hol das JSON aus diesem Script-Tag heraus, und fertig. Kein API-Replay nötig.

import json, re, requests

html = requests.get("https://example.com/product/123").text
match = re.search(r'<script id="__NEXT_DATA__"[^>]*>(.*?)</script>', html, re.S)
state = json.loads(match.group(1))
print(state["props"]["pageProps"]["product"]["price"])

Das ist schnell und stabil, weil du dieselben Daten liest, die das Framework zum Bauen der Seite verwendet hat. Strukturierte Daten in application/ld+json sind besonders praktisch für Produkte, Artikel und Rezensionen.

Wenn die API Sich Wehrt

Nicht jeder Endpunkt gibt einem schlichten Skript Daten heraus. Dieselben Anti-Bot-Schichten, die die HTML-Seite schützen, sitzen oft auch vor der API. Anzeichen, dass du auf eine gestoßen bist:

  • Ein 403 oder 429, obwohl dein Browser den Endpunkt problemlos lädt.
  • Ein HTML-Körper mit einer Challenge oder einem CAPTCHA statt JSON.
  • Ein kurzlebiges Token, ein signierter Parameter oder ein Zeitstempel, der in Sekunden abläuft.
  • Eine 200-Antwort, die eine leere oder gefälschte Nutzlast zurückgibt. Prüfe immer die Form des JSON, nicht nur den Statuscode.

Einiges davon kannst du selbst lösen. Rotiere einen realistischen User-Agent, sende die Header, die der Browser sendet, und verwende ein einmal erfasstes Session-Cookie wieder, statt bei jedem Aufruf ein neues aufzuwärmen. Eine Bibliothek wie curl_cffi hilft, wenn die Sperre auf deinem TLS-Handshake beruht statt auf deinen Headern, weil sie den Fingerabdruck eines echten Browsers nachahmt.

Aber wenn ein Endpunkt ein signiertes Token verlangt, das von obfuskiertem clientseitigem JavaScript erzeugt wird, oder die gesamte Domain hinter einem aggressiven Anti-Bot-Anbieter liegt, wird das Nachbauen der Anfrage von Hand zu einem verlorenen Reverse-Engineering-Spiel, das bei jedem neuen Build der Website kaputtgeht. An diesem Punkt verdient sich eine Scraping-API ihren Platz: Du sendest die Ziel-URL, sie übernimmt das Browser-Rendering, den Fingerabdruck und die Proxy-Rotation, und du bekommst die Antwort zurück. ScrapeUnblocker ist genau für diesen Fall gebaut, und seine geparsten Endpunkte können dir strukturiertes JSON liefern, sodass du dir auch das HTML-Parsen sparst.

Häufige Fragen

Woran erkenne ich, ob eine Website eine versteckte API nutzt? Sieh dir den rohen Seitenquelltext an. Wenn die gewünschten Werte fehlen und du einen leeren Container oder einen Hinweis “JavaScript aktivieren” siehst, werden die Daten separat geholt. Bestätige es, indem du beim Laden der Seite den Fetch/XHR-Tab in den DevTools beobachtest.

Ist es legal, die interne API einer Website aufzurufen? Du rufst denselben Endpunkt auf, den dein Browser aufruft, es ist also eine öffentliche Anfrage. Dennoch: Respektiere die Nutzungsbedingungen der Website, die Robots-Hinweise und die Rate-Limits, und sammle nur öffentlich sichtbare Daten. Die rechtliche Grundlage hängt von Land und Anwendungsfall ab, betrachte das also als technische Antwort, nicht als Rechtsberatung.

Warum die API statt eines Headless-Browsers nutzen? Geschwindigkeit und Stabilität. Ein API-Aufruf dauert Millisekunden und liefert saubere, typisierte Daten. Ein Headless-Browser startet bei jeder Anfrage ein vollständiges Seiten-Rendering und gibt dir HTML, das du noch parsen musst. Nutze die API, wenn du kannst, und greife nur dann auf Rendering zurück, wenn es sein muss.

Die API braucht ein Token, das ich nicht erzeugen kann. Was nun? Wenn das Token aus obfuskiertem JavaScript stammt, erfasse einen frischen Seitenaufruf, um das aktuelle Token zu lesen, oder rendere die Seite einmal, um es zu erhalten, und verwende die Session wieder. Ist das zu brüchig zu pflegen, leite die Anfrage über eine Scraping-API, die rendert und das Ergebnis für dich zurückgibt.

Fazit

Die gerenderte Seite ist die langsame, brüchige Oberfläche einer Website. Darunter liegt eine saubere Datenleitung, die die Website für ihr eigenes Frontend gebaut hat. Zu lernen, den Network-Tab zu lesen, die Anfrage nachzubauen und der Paginierung zu folgen, verwandelt die meisten “JavaScript-lastigen, unmöglich zu scrapenden” Websites in einen einfachen JSON-Abruf.

Beginne bei deinem nächsten Ziel mit Seitenquelltext anzeigen und dem Fetch/XHR-Filter. Wenn du auf einen Endpunkt triffst, der hinter ernsthaften Anti-Bot-Abwehren verriegelt ist, ist das der Moment, zu einem Werkzeug wie ScrapeUnblocker zu greifen, statt von Hand mit dem Fingerabdruck zu kämpfen.

ScrapeUnblocker kostenlos testen

Über 95 % Erfolgsquote · ab 0,55 € pro 1.000 Aufrufe · 500 kostenlose Anfragen bei der Registrierung.

Kostenlos testen → Preise ansehen