Wie ist eigentlich der Jugendschutz möglich, wenn das Kind neben einem sitzt?

Verfasst von

in

,

Neulich saß ich mit den Kindern (beide 5 Jahre) auf der Couch, und ich wollte mit den beiden etwas zusammen spielen. Die Kids direkt danebensitzen zu lassen, während ich suche, war rückblickend nicht meine klügste Idee – aber das ist eine andere Geschichte.

Ich hatte LEGO im Kopf. LEGO-Spiele gibt es im PlayStation Store einige, und ein neues hätte ich sogar gekauft. Also habe ich eins nach dem anderen geöffnet. USK 12. USK 12. USK 12. USK 12. Bis ich endlich eins gefunden habe, das USK 6 oder sogar USK 0 hatte. Cool, das kann ich installieren, das können wir spielen.

Was mich daran so genervt hat: Ich musste jedes Spiel einzeln aufmachen. Auf den Kacheln im Store gibt es nicht mal ein kleines Alters-Icon, so wie auf der Hülle im Laden. Bei einem Film im Regal sehe ich die Freigabe sofort. Ich weiß, Sammler finden solche Badges auf dem Cover schrecklich. Aber für mich als Papa ist das in diesem Moment die einzige Information, die ich brauche.

Was ich eigentlich will

Ganz einfach: Ich setze mich an die Konsole – da sitze ich ja sowieso – und filtere nach Altersfreigabe. Punkt. Und das in meiner Bibliothek, und wenn die Tasche lockerer ist auch gerne im Shop.

Wenn ich für mich selbst spiele, ist mir das egal. Ich sehe einen Trailer, das Spiel gefällt mir, ich kaufe es (nach erfolgter Freigabe des Finanzministeriums). Aber wenn die Kinder mitspielen, ist die Altersfreigabe doch für mich die einzig kritische Information. Und selbst bei Spielen, die ich gut kenne, liege ich mit meinem Bauchgefühl manchmal daneben. Astro Bot zum Beispiel – ist das USK 0? USK 6? USK 12? Ich hätte es nicht sicher sagen können (es ist USK 6 btw).

Mir geht es dabei nicht um eine Empfehlung. Ob ein Spiel gut ist oder zu unseren Kindern passt, suchen meine Frau und ich selbst aus, wir arbeiten immerhin in der Branche und haben da ein ganz gutes Gefühl für. Ich will nur den ersten, einfachen Schritt: Zeig mir, was USK 0 oder 6 hat. Das ist meine Erwartung an eine Plattform – und ich hoffe, ich bin damit nicht allein.

Ja, es gibt Wege – nur nicht dort, wo ich sitze

Natürlich kann ich jedes Spiel einzeln aufrufen. Natürlich kann ich im Internet nach Empfehlungen suchen. Und es gibt dafür gute Anlaufstellen:

  • USK-Titelsuche – die offizielle Freigabe für jedes geprüfte Spiel. Leider ist die Seite ziemlich langsam
  • Stiftung Digitale Spielekultur – eine Datenbank mit Spielen, die pädagogisches Potenzial haben
  • Family Gaming Database – englischsprachig, lässt sich nach System, Altersfreigabe und Spielerzahl filtern und hat Listen mit Spielen, die sich für bestimmte Altersgruppen bewährt haben
  • PEGI-App – filtert nach Altersfreigabe, Genre und Plattform. Aber Achtung: PEGI ist das europäische System, in Deutschland gilt die USK. Die Stufen weichen teils voneinander ab

Das geht alles, das weiß ich. Nur: Wenn ich an der Konsole sitzen, will ich dafür nicht das Handy rausholen. Ich will keinen Browser aufmachen und keine fremde Datenbank durchsuchen, während auf dem Fernseher der Store offen ist und zwei Fünfjährige neben mir sitzen. Keine dieser Seiten kennt meine Bibliothek, und keine sitzt da, wo ich gerade sitze: vor der Konsole. Genau dort will ich die Antwort.

Also habe ich den Cross-Check gemacht und auf allen vier Plattformen nachgesehen, was direkt am Gerät tatsächlich geht. Das Ergebnis hat mich nicht zufriedengestellt – deshalb habe ich mir für eine der vier selbst geholfen. Wer die Lösung zuerst sehen will, springt direkt zu meinem Skript, das die komplette Steam-Bibliothek mit Altersfreigaben als Tabelle exportiert. Fairerweise vorweg: Es löst das Problem für Steam. Für Steam, und sonst nichts.

Was die Plattformen anbieten

PlattformStore nach Alter filternEigene Bibliothek filtern
PlayStation 5❌ Nein❌ Nein
Steam (Steam Machine)❌ Nein❌ Nein
Xbox-App (am PC)✅ Ja✅ Ja, für Xbox- und Game-Pass-Spiele
Nintendo Switch✅ Ja❌ Nein

PlayStation: nichts

Kein Filter im Store, kein Filter in der Bibliothek, kein Icon auf der Kachel. Einen Weg gibt es: ein Kinderprofil mit Altersgrenze. Das ist das richtige Werkzeug, wenn Kinder allein spielen. Für meine Frage hilft es aber nicht – warum, dazu weiter unten mehr.

Steam & Steam Machine: nichts – obwohl die Daten da sind!

An meiner Steam Machine am Fernseher gibt es weder in der Bibliothek noch im Store einen Filter nach Altersfreigabe. Das Absurde: Steam kennt die Freigaben. In den Shop-Daten jedes Spiels steht die Einstufung für Deutschland drin, sauber und maschinenlesbar. Sie wird nur nicht als Filter angeboten.

Xbox: So stelle ich mir das vor

Eine Xbox-Konsole habe ich nicht (mehr), aber die Xbox-App am PC. Und die kann genau das, was ich will (und ich vermute die XBOX kann dies auch). Im Store gibt es unter „Filter“ ein Feld Altersfreigabe mit den USK-Stufen. In Meine Spiele kann ich ebenfalls nach USK filtern – und das kombinieren: Was ist installiert? Was gibt es im Game Pass? Was kann ich per Cloud streamen? Was ist kostenlos? Über die Bibliothek und über den Store hinweg. Also genau die Frage: Was kann ich jetzt mit den Kindern spielen?

Xbox-App, Ansicht Meine Spiele mit aktivem Filter nach USK-Altersfreigabe
So sieht es aus, wenn eine Plattform mitdenkt: Filter nach Altersfreigabe, direkt in der eigenen Bibliothek.

Eine Einschränkung gibt es: Die Xbox-App zeigt auch meine Steam-Spiele an, aber ohne Altersinformation. Der Filter greift nur bei Spielen, die über Xbox kommen. Trotzdem: Microsoft zeigt, dass es geht. Das ist keine technische Frage, sondern eine Frage, ob eine Plattform es umsetzen will.

Nintendo Switch: der eShop kann es

Das war die Überraschung beim Nachschauen. Der eShop hat den Filter, und er ist nicht mal versteckt: links das Trichter-Symbol, dann der Punkt Alterseinstufung. Aus 18.894 Artikeln wird damit in zwei Klicks eine Liste, die ich mit den Kindern durchgehen kann.

Nintendo eShop mit geoeffnetem Filtermenue: Alterseinstufung mit den Stufen Nicht festgelegt, 0 bis 7, 12, 16 und 18
Der Filter im eShop. Die Stufen 0–7, 12, 16 und 18 sind allerdings nicht die der USK.

Eine Eigenheit hat der Filter allerdings. Die Stufen heißen Nicht festgelegt, 0–7, 12, 16 und 18. Eine 7 gibt es in der USK gar nicht – dort sind die Stufen 0, 6, 12, 16 und 18. Im europäischen PEGI-System dagegen schon. Nintendo wirft hier also mehrere Stufen in eine Schublade, statt die deutschen Freigaben einzeln anzubieten. Für meine Frage auf der Couch reicht das trotzdem: Ich will die unterste Schublade sehen, nicht auf ein Jahr genau sortieren.

Zwei Dinge kann die Switch aber auch nicht. Auf der Kachel steht die Freigabe weiterhin nicht, die sehe ich erst auf der Detailseite. Und der Filter lebt nur im Store: Meine bereits gekauften und installierten Spiele auf der Konsole lassen sich nicht nach Alter filtern. Genau der Fall, der mich auf der Couch beschäftigt hat, bleibt also auch hier offen – immerhin habe ich beim Kauf im Store eine Chance.

Und wie bei Sony gibt es auch hier den Weg über ein Kinderprofil. Über die App „Nintendo Switch – Altersbeschränkungen“ stelle ich am Handy ein, welche Altersstufe auf der Konsole gespielt werden darf – wahlweise über eine der vorgegebenen Stufen oder in Handarbeit. Gleiches Werkzeug, gleiche Lücke: Es schützt ein Kind, das selbst spielt. Mir hilft es nicht, wenn ich in meinem eigenen Profil sitze und den Controller in der Hand halte. Warum das zwei völlig verschiedene Dinge sind, dazu gleich mehr.

Damit steht es zwei zu zwei. Microsoft und Nintendo bieten den Filter an, Sony und Valve nicht. Das ist der Punkt, an dem das Argument „zu aufwendig“ nicht mehr trägt: Zwei von vier Plattformen haben es längst gebaut.

Damit das nicht falsch ankommt: Eine Kaufempfehlung für eine Konsole ist das nicht. Wer seine Kinder allein spielen lassen will, ist bei allen vieren gut aufgehoben – der Schutz über ein Kinderprofil funktioniert überall, nur auf unterschiedlichen Wegen. Am Ende ist es gelöst. Mir geht es um den anderen Fall: den mit dem Erwachsenen am Controller und dem Kind daneben. Und der ist bei zweien gelöst und bei zweien nicht.


Mein Workaround – aber nur für Steam

Weil ich nicht 20 Webseiten durchforsten wollte, habe ich mir für Steam selbst geholfen: mit einem kleinen Python-Skript, das meine komplette Bibliothek mit Altersfreigabe als Tabelle exportiert. Damit das klar ist: Das löst das Problem nur für Steam. Für die PlayStation habe ich immer noch dieselbe Situation wie auf der Couch – Sony bietet keine öffentliche Schnittstelle, über die ich an meine Bibliothek käme.

Das kam bei meinen Steam-Spielen raus:

FreigabeAnzahl
USK 0131
USK 6140
USK 12401
USK 16333
USK 18205
Ohne Angabe (nicht mehr im Shop, Betas, Playtests)100
Gesamt1.312

271 Spiele mit USK 0 oder 6 lagen also längst in meiner Bibliothek. 207 davon habe ich noch nie gestartet. Die Spiele waren da – ich konnte sie nur nicht sehen. Mit der Tabelle haben meine Frau und ich jetzt endlich eine Liste, mit der wir uns in Ruhe beschäftigen können.

Tabelle der exportierten Steam-Bibliothek mit Spalten fuer Name, USK-Freigabe, Quelle der Einstufung und Koop-Features
Das Ergebnis des Skripts: eine Zeile pro Spiel, sortiert nach Freigabe – und mit der Spalte, woher die Einstufung stammt.

Achtung: Nicht jede Zahl ist eine USK-Freigabe

Beim Durchschauen ist mir etwas aufgefallen, das man wissen muss. Das Skript schreibt in die Spalte „Quelle“, woher eine Einstufung kommt:

  • 558 Einstufungen stehen als USK-Freigabe drin – eingetragen vom Hersteller selbst. Laut Steams eigenen Regeln darf er das nur, wenn die USK genau diese Version wirklich geprüft hat
  • 654 Einstufungen sind „Steam-DE (generiert)“ – Valves eigenes Einstufungsverfahren für Deutschland, unter anderem auf Basis eines Fragebogens, den der Entwickler ausfüllt. Nicht von der USK geprüft
  • 100 Einträge haben gar keine Angabe

Wie wenig die generierten Werte mit der USK zu tun haben, zeigen zwei Zeilen besonders schön: Endzone – A World Apart und Monster Prom 3 stehen bei Steam auf 14. Diese Altersstufe gibt es bei der USK überhaupt nicht.

Und selbst beim USK-Label lohnt ein zweiter Blick: Warhammer 40,000: Gladius – Relics of War steht bei mir mit USK 6 in der Tabelle. In der USK-Titelsuche habe ich das Spiel aber nicht gefunden. Eine USK-Einstufung, die automatisch über einen anderen Store entstanden ist, dürfte bei Steam laut Valve gar nicht eingetragen werden. Vielleicht gibt es eine Erklärung – nachvollziehen kann ich die 6 so jedenfalls nicht.

Mein Rat deshalb: Nimm die Tabelle als Vorauswahl, nicht als Freigabe. Bevor ein Spiel mit den Kindern auf der Couch landet, kurz in der USK-Titelsuche gegenchecken.

Das Skript: So exportierst du deine eigene Steam-Bibliothek

Das Skript ist in Python geschrieben und braucht keine Zusatzpakete. Es läuft unter Windows, macOS und Linux und schreibt eine CSV-Datei, die du direkt in Excel öffnen und filtern kannst – Umlaute inklusive.

Das komplette Skript steht hier im Beitrag – aufklappen, lesen, kopieren. So siehst du vorher genau, was es tut, und musst keine Datei von einer fremden Seite herunterladen.

Ganzes Skript anzeigen: steam_usk_report.py
#!/usr/bin/env python3
"""
steam_usk_report.py
===================

Listet die eigene Steam-Bibliothek mit USK-Freigabe und Multiplayer-/Koop-Features
als CSV auf. Nur Python-Standardlib, kein pip noetig.

Datenquellen
------------
  * IPlayerService/GetOwnedGames        -> Bibliothek (AppIDs, Namen, Spielzeit)
  * store.steampowered.com/api/appdetails -> ratings.usk.rating + categories

Voraussetzungen
---------------
  1. Web-API-Key:  https://steamcommunity.com/dev/apikey
  2. SteamID64:    https://steamid.io/  (17-stellige Zahl)
  3. Profil-Einstellung "Spieldetails" muss auf Oeffentlich stehen,
     sonst liefert GetOwnedGames eine leere Liste.

Beispiele
---------
  python steam_usk_report.py --key ABC123 --steamid 7656119...
  python steam_usk_report.py --key ABC123 --steamid 7656119... --max-usk 6 --only-coop
  python steam_usk_report.py ... --out kinderspiele.csv --delay 2.0

Der Store-Endpunkt ist rate-limited (grob 200 Anfragen / 5 Minuten pro IP).
Das Script wartet daher standardmaessig 1.6s zwischen den Abfragen und legt
alle Ergebnisse in einer Cache-Datei ab -- ein zweiter Lauf ist dadurch sofort
fertig. Bei einer 400-Spiele-Bibliothek dauert der erste Lauf ca. 11 Minuten.
"""

import argparse
import csv
import json
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path

OWNED_URL = "https://api.steampowered.com/IPlayerService/GetOwnedGames/v1/"
DETAILS_URL = "https://store.steampowered.com/api/appdetails"
STORE_PAGE = "https://store.steampowered.com/app/{}/"

# Steam-Kategorie-IDs, die fuer gemeinsames Spielen relevant sind
CATEGORY_LABELS = {
    1: "Multiplayer",
    9: "Koop",
    24: "Splitscreen",
    27: "Crossplay",
    36: "Online-PvP",
    37: "Splitscreen-PvP",
    38: "Online-Koop",
    39: "Splitscreen-Koop",
    44: "Remote Play Together",
    49: "PvP",
}
COOP_CATEGORY_IDS = {9, 24, 38, 39, 44}

USER_AGENT = "steam-usk-report/1.0"


# ---------------------------------------------------------------- HTTP-Helfer

def http_json(url, timeout=30):
    req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8", errors="replace"))


def http_json_retry(url, tries=5, base_delay=5.0, quiet=False):
    """GET mit Backoff bei 429 / 5xx / Netzwerkhaengern. None bei Dauerfehler."""
    for attempt in range(tries):
        try:
            return http_json(url)
        except urllib.error.HTTPError as exc:
            if exc.code in (429, 500, 502, 503, 504):
                wait = base_delay * (2 ** attempt)
                if not quiet:
                    print(f"    HTTP {exc.code} - warte {wait:.0f}s", file=sys.stderr)
                time.sleep(wait)
                continue
            raise
        except (urllib.error.URLError, TimeoutError, json.JSONDecodeError):
            wait = base_delay * (2 ** attempt)
            if not quiet:
                print(f"    Netzwerkfehler - warte {wait:.0f}s", file=sys.stderr)
            time.sleep(wait)
    return None


# ------------------------------------------------------------------ Bibliothek

def get_owned_games(api_key, steamid):
    params = urllib.parse.urlencode({
        "key": api_key,
        "steamid": steamid,
        "include_appinfo": 1,
        "include_played_free_games": 1,
        "format": "json",
    })
    payload = http_json_retry(f"{OWNED_URL}?{params}")
    if payload is None:
        raise SystemExit("GetOwnedGames nicht erreichbar.")
    games = (payload.get("response") or {}).get("games")
    if not games:
        raise SystemExit(
            "Keine Spiele zurueckbekommen. Pruefe API-Key, SteamID64 und ob die "
            "Profil-Einstellung 'Spieldetails' auf Oeffentlich steht."
        )
    return games


def get_app_details(appid, cc="de", lang="german"):
    params = urllib.parse.urlencode({
        "appids": appid, "cc": cc, "l": lang,
    })
    payload = http_json_retry(f"{DETAILS_URL}?{params}")
    if not payload:
        return None
    entry = payload.get(str(appid)) or {}
    if not entry.get("success"):
        return None            # z.B. regional nicht verfuegbar oder Tool/Soundtrack
    return entry.get("data") or {}


# ------------------------------------------------------------------- Auswertung

def extract_age_rating(data):
    """(Freigabe, Quelle). Leerer String, wenn nichts Belastbares vorliegt."""
    ratings = data.get("ratings") or {}

    usk = (ratings.get("usk") or {}).get("rating")
    if usk not in (None, ""):
        return str(usk), "USK"

    # Valves eigene Selbsteinstufung fuer den deutschen Markt
    steam_de = ratings.get("steam_germany") or {}
    rating = steam_de.get("rating")
    if rating not in (None, ""):
        suffix = " (generiert)" if steam_de.get("rating_generated") in (1, "1", True) else ""
        return str(rating), "Steam-DE" + suffix

    required = data.get("required_age")
    if required not in (None, "", 0, "0"):
        return str(required), "required_age"

    return "", ""


def extract_categories(data):
    ids = set()
    for cat in data.get("categories") or []:
        try:
            ids.add(int(cat.get("id")))
        except (TypeError, ValueError):
            continue
    labels = [CATEGORY_LABELS[i] for i in sorted(ids) if i in CATEGORY_LABELS]
    return ids, labels


def sort_key(row):
    """Nach Freigabe aufsteigend, Unbekanntes ans Ende, dann alphabetisch."""
    raw = row["USK"]
    try:
        rating = int(raw)
    except (TypeError, ValueError):
        rating = 999
    return (rating, row["Name"].lower())


# ------------------------------------------------------------------------- Main

def build_rows(games, cache, args):
    rows = []
    total = len(games)
    fetched = 0

    for index, game in enumerate(sorted(games, key=lambda g: g.get("name", "")), 1):
        appid = game.get("appid")
        name = game.get("name") or f"App {appid}"
        key = str(appid)

        if key in cache:
            data = cache[key]
        else:
            print(f"[{index}/{total}] {name}", file=sys.stderr)
            data = get_app_details(appid, cc=args.cc, lang=args.lang)
            cache[key] = data            # auch None cachen, spart Wiederholungen
            fetched += 1
            if fetched % 25 == 0:
                save_cache(args.cache, cache)
            time.sleep(args.delay)

        if not data:
            rows.append({
                "AppID": appid, "Name": name, "USK": "", "Quelle": "nicht abrufbar",
                "Koop": "", "Features": "", "Spielzeit_h": round(game.get("playtime_forever", 0) / 60, 1),
                "Store": STORE_PAGE.format(appid),
            })
            continue

        if args.games_only and data.get("type") != "game":
            continue

        rating, source = extract_age_rating(data)
        cat_ids, labels = extract_categories(data)

        rows.append({
            "AppID": appid,
            "Name": data.get("name") or name,
            "USK": rating,
            "Quelle": source,
            "Koop": "ja" if cat_ids & COOP_CATEGORY_IDS else "",
            "Features": ", ".join(labels),
            "Spielzeit_h": round(game.get("playtime_forever", 0) / 60, 1),
            "Store": STORE_PAGE.format(appid),
        })

    return rows


def apply_filters(rows, args):
    out = rows
    if args.only_coop:
        out = [r for r in out if r["Koop"] == "ja"]
    if args.max_usk is not None:
        keep = []
        for row in out:
            try:
                value = int(row["USK"])
            except (TypeError, ValueError):
                if args.include_unrated:
                    keep.append(row)
                continue
            if value <= args.max_usk:
                keep.append(row)
        out = keep
    return out


def load_cache(path):
    p = Path(path)
    if p.is_file():
        try:
            return json.loads(p.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            print("Cache unlesbar, wird neu aufgebaut.", file=sys.stderr)
    return {}


def save_cache(path, cache):
    try:
        Path(path).write_text(json.dumps(cache), encoding="utf-8")
    except OSError as exc:
        print(f"Cache konnte nicht geschrieben werden: {exc}", file=sys.stderr)


def write_csv(path, rows):
    fields = ["AppID", "Name", "USK", "Quelle", "Koop", "Features", "Spielzeit_h", "Store"]
    # utf-8-sig, damit Excel die Umlaute nicht zerlegt
    with open(path, "w", newline="", encoding="utf-8-sig") as fh:
        writer = csv.DictWriter(fh, fieldnames=fields, delimiter=";")
        writer.writeheader()
        writer.writerows(rows)


def parse_args(argv=None):
    ap = argparse.ArgumentParser(description="Steam-Bibliothek mit USK-Freigaben als CSV.")
    ap.add_argument("--key", required=True, help="Steam Web-API-Key")
    ap.add_argument("--steamid", required=True, help="SteamID64 (17-stellig)")
    ap.add_argument("--out", default="steam_usk.csv", help="Ziel-CSV")
    ap.add_argument("--cache", default="steam_appdetails_cache.json", help="Cache-Datei")
    ap.add_argument("--delay", type=float, default=1.6, help="Sekunden zwischen Store-Abfragen")
    ap.add_argument("--cc", default="de", help="Store-Region (Standard: de)")
    ap.add_argument("--lang", default="german", help="Store-Sprache")
    ap.add_argument("--max-usk", type=int, default=None, metavar="N",
                    help="Nur Titel mit Freigabe <= N (z.B. 6)")
    ap.add_argument("--include-unrated", action="store_true",
                    help="Titel ohne Freigabe trotz --max-usk mit ausgeben")
    ap.add_argument("--only-coop", action="store_true",
                    help="Nur Titel mit Koop-/Splitscreen-/Remote-Play-Kategorie")
    ap.add_argument("--games-only", action="store_true", default=True,
                    help="DLC, Tools und Soundtracks ausblenden (Standard)")
    ap.add_argument("--all-types", dest="games_only", action="store_false",
                    help="Auch DLC/Tools/Soundtracks aufnehmen")
    return ap.parse_args(argv)


def main(argv=None):
    args = parse_args(argv)

    games = get_owned_games(args.key, args.steamid)
    print(f"{len(games)} Eintraege in der Bibliothek.", file=sys.stderr)

    cache = load_cache(args.cache)
    print(f"{len(cache)} Eintraege aus dem Cache.", file=sys.stderr)

    try:
        rows = build_rows(games, cache, args)
    finally:
        save_cache(args.cache, cache)

    rows = apply_filters(rows, args)
    rows.sort(key=sort_key)
    write_csv(args.out, rows)

    unrated = sum(1 for r in rows if not r["USK"])
    print(f"\n{len(rows)} Zeilen geschrieben nach {args.out} "
          f"({unrated} davon ohne Freigabe-Angabe).", file=sys.stderr)
    return 0


if __name__ == "__main__":
    sys.exit(main())

So speicherst du es: Button „Kopieren“ oben rechts im Code-Block anklicken, einen Texteditor öffnen, einfügen und die Datei als steam_usk_report.py speichern. Unter Windows beim Speichern darauf achten, dass nicht versehentlich .py.txt daraus wird.

Schritt 1: Python installieren

Falls noch nicht vorhanden: Python 3 von python.org herunterladen. Unter Windows bei der Installation unbedingt den Haken bei „Add python.exe to PATH“ setzen, sonst findet die Eingabeaufforderung den Befehl später nicht.

Schritt 2: Steam-Web-API-Key holen

Den Schlüssel bekommst du auf der Steam-Web-API-Key-Seite. Als Domain reicht localhost. Zwei Voraussetzungen: Dein Konto braucht den Steam Guard Mobile Authenticator, und eingeschränkte Konten ohne Käufe bekommen keinen Key.

Behandle den Key wie ein Passwort. Nicht in Screenshots, nicht in Foren, nicht in Discord, nicht fest ins Skript schreiben. API-Keys sind ein beliebtes Ziel von Betrügern auf Steam. Wenn dich jemand „für eine Verifizierung“ nach deinem Key fragt: Das ist Betrug. Falls er doch mal rausgerutscht ist, kannst du ihn auf derselben Seite widerrufen und dir einen neuen holen.

Schritt 3: SteamID64 herausfinden

Das Skript braucht deine SteamID64, eine 17-stellige Zahl. Am schnellsten geht das über steamid.io: Link zu deinem Steam-Profil einfügen, auf „lookup“ klicken und die Zahl bei steamID64 kopieren.

Schritt 4: Spieldetails kurz öffentlich stellen

Steam gibt die Bibliothek nur heraus, wenn deine Spieldetails öffentlich sind. Sonst kommt eine leere Liste zurück. Der Weg: Steam → dein Profil → Profil bearbeiten → Privatsphäre-Einstellungen → „Spieldetails“ auf „Öffentlich“. Damit das geht, muss auch „Mein Profil“ auf „Öffentlich“ stehen.

Mein Tipp: Nur für den Lauf umstellen und danach wieder zurück. Deine Bibliothek muss nicht dauerhaft für alle sichtbar sein.

Schritt 5: Skript starten

Eingabeaufforderung oder Terminal im Ordner mit dem Skript öffnen. Für die komplette Bibliothek:

python steam_usk_report.py --key DEIN_KEY --steamid DEINE_STEAMID64 --out steam_alle.csv

Oder direkt nur die Spiele bis USK 6:

python steam_usk_report.py --key DEIN_KEY --steamid DEINE_STEAMID64 --max-usk 6 --out kinderspiele.csv

Geduld beim ersten Lauf. Steam lässt nur rund 200 Shop-Abfragen in fünf Minuten zu. Bei meinen 1.300 Spielen hat der erste Durchlauf knapp eine Stunde gedauert. Das Skript speichert aber alles in einer Cache-Datei – jeder weitere Lauf, egal mit welchem Filter, ist in Sekunden durch.

Die Filter

  • --max-usk 6 – nur Spiele bis zur angegebenen Freigabe
  • --only-coop – nur Spiele mit Koop, Splitscreen oder Remote Play Together
  • --include-unrated – Spiele ohne Freigabe trotz Altersfilter mit ausgeben
  • --all-types – auch DLCs, Soundtracks und Tools aufnehmen (standardmäßig fliegen die raus)

Was in der Tabelle steht

Pro Spiel eine Zeile: Name, Freigabe, Quelle der Freigabe, ob Koop möglich ist, welche Mehrspieler-Arten es gibt, deine bisherige Spielzeit und ein Link zur Shopseite. Sortiert ist nach Freigabe, die niedrigste zuerst. Wer zusammen spielen will, schaut in der Spalte „Features“ nach Splitscreen-Koop und Remote Play Together.

Ein Blick unter die Haube

Der interessanteste Teil ist die Frage, woher die Freigabe kommt. Das Skript arbeitet eine feste Reihenfolge ab und nimmt die erste Angabe, die es findet:

def extract_age_rating(data):
    ratings = data.get("ratings") or {}

    # 1. USK-Label, vom Hersteller bei Steam eingetragen
    usk = (ratings.get("usk") or {}).get("rating")
    if usk not in (None, ""):
        return str(usk), "USK"

    # 2. Valves eigene Selbsteinstufung für Deutschland
    steam_de = ratings.get("steam_germany") or {}
    rating = steam_de.get("rating")
    if rating not in (None, ""):
        suffix = " (generiert)" if steam_de.get("rating_generated") in (1, "1", True) else ""
        return str(rating), "Steam-DE" + suffix

    # 3. Allgemeines Mindestalter aus dem Shop
    required = data.get("required_age")
    if required not in (None, "", 0, "0"):
        return str(required), "required_age"

    return "", ""

So siehst du in der Tabelle, ob eine Zahl als USK-Freigabe angegeben ist oder nur von Steam generiert wurde. Diese Unterscheidung fehlt mir in den Shops selbst.


Moment mal: Warum nutze ich beim Streaming Kinderprofile – und an der Konsole nicht?

Beim Schreiben dieses Beitrags ist mir selbst etwas aufgefallen. Bei Netflix, Disney+ und Prime Video haben wir Kinderprofile, ganz selbstverständlich. An der PlayStation und an der Steam Machine nicht. Warum eigentlich?

Weil ich an der Konsole der Einzige bin, der spielt. Die Kinder sitzen daneben oder spielen mit, aber den Controller halte ich. Und ich würde auch gar nicht wollen, dass sie dort allein loslegen. Beim Handy ist es genauso: Das ist kein Kinderhandy, sondern meins, ohne Kinderschutz. Wenn ich bei YouTube ein Video anmache, entscheide ich, ob die Kinder das sehen können – weil ich das Handy in der Hand halte.

Da stecken also zwei ganz verschiedene Dinge drin:

  • Schutz: Das Kind bedient selbst, und die Plattform soll verhindern, dass es an Ungeeignetes kommt. Dafür ist ein Kinderprofil genau das richtige Werkzeug
  • Orientierung: Ich bediene selbst, in meinem eigenen Profil, und will schnell sehen, was für die Kinder passt. Die Frage ist nicht „Was darf mein Kind allein?“, sondern „Was suche ich für uns aus?“

Prime Video zeigt, dass Orientierung auch ohne Kinderprofil geht: Ich bin in meinem eigenen Profil, wähle im Kids-Bereich einen Titel an – und die Freigabe steht direkt da. Genau so etwas wünsche ich mir für Spiele.

Und ja, ich hinterfrage mich da auch selbst. Unsere Kinder werden älter und wollen bald selbst bedienen. Spätestens dann bekommen sie auch an der Konsole ein eigenes Profil – und dann filtert die Plattform ja doch für sie. Vielleicht bin ich mit meinem Wunsch also genau in dieser Übergangsphase. Oder vielleicht bin ich am Ende sogar allein damit. Das würde mich ehrlich interessieren.

Was eine Freigabe nicht kann

Damit kein falscher Eindruck entsteht: Eine Altersfreigabe sagt mir nicht, ob ein Spiel gut ist. Nicht, ob es pädagogisch sinnvoll ist. Nicht, ob es sich zu zweit spielen lässt. Und auch nicht, ob es ein gutes erstes Spiel ist, um einem kleinen Kind selbst den Controller in die Hand zu geben.

Das alles finden wir weiterhin selbst heraus – und die Zeit nehmen wir uns gern. Aber der Filter wäre der Anfang. Statt tausend einzelner Shopseiten.

Liebe Plattformen: Das muss einfacher gehen

Als Papa will ich keine Skripte schreiben müssen, um herauszufinden, welche Spiele USK 0 haben. Und für die PlayStation könnte ich es nicht mal. Ich erwarte, dass die Plattformen das direkt anbieten:

  1. Ein Filter nach Altersfreigabe – im Store und in der eigenen Bibliothek, so wie in der Xbox-App
  2. Ein kleines Alters-Icon auf der Kachel – wie auf der Hülle im Laden
  3. Eine klare Kennzeichnung, ob eine Freigabe von der USK geprüft oder nur selbst eingestuft ist

Ich glaube, es gibt einige Eltern, die mit ihren Kindern auf der Couch sitzen und genau diese Frage haben. Falls ich mich irre: Sagt es mir.

Hinweis: Das Skript ist ein privates Hilfsmittel ohne Gewähr und steht in keiner Verbindung zu Valve, Sony, Microsoft, Nintendo oder der USK. Es liest nur öffentlich verfügbare Shop-Daten und deine eigene Bibliothek.

Wie macht ihr das? Wie findet ihr Spiele, die für eure Kinder freigegeben sind – und kennt ihr einen Weg für die PlayStation, den ich übersehen habe? Schreibt es mir in die Kommentare.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert