# Genius — Data Extraction Field-tested against genius.com on 2026-04-18. No authentication required for any approach documented here. --- ## Anti-Bot: http_get Fails, Custom UA Required `http_get` uses `User-Agent: Mozilla/5.0` (bare string). Genius returns HTTP 403 for that UA on both HTML pages and internal API endpoints. Adding any OS token (e.g. `(Macintosh; Intel Mac OS X 10_15_7)`) immediately lifts the block — no cookies, no session, no JavaScript required. ```python from helpers import http_get def genius_get(url, extra_headers=None): """Drop-in replacement for http_get on genius.com endpoints.""" headers = { "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36" ), "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip", } if extra_headers: headers.update(extra_headers) return http_get(url, headers=headers) ``` Use `genius_get` everywhere in this document instead of bare `http_get`. --- ## Approach 1 (Fastest): Internal JSON API — No Auth, No Browser Genius's own website calls `genius.com/api/*` (not `api.genius.com`) from its server-side rendering layer. These endpoints are public and require only a browser-like User-Agent. They return rich structured JSON in ~0.13s. ### Song metadata ```python import json from helpers import http_get def genius_get(url, extra_headers=None): headers = { "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36" ), "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip", } if extra_headers: headers.update(extra_headers) return http_get(url, headers=headers) def genius_song(song_id): """Fetch full song metadata by Genius song ID.""" data = json.loads(genius_get(f"https://genius.com/api/songs/{song_id}")) return data["response"]["song"] song = genius_song(1063) # All fields available in one call (no auth): # song["title"] → "Bohemian Rhapsody" # song["full_title"] → "Bohemian Rhapsody by Queen" # song["artist_names"] → "Queen" # song["primary_artist"]["name"] → "Queen" # song["primary_artist"]["id"] → 563 # song["primary_artist"]["url"] → "https://genius.com/artists/Queen" # song["release_date"] → "1975-10-31" # song["release_date_for_display"] → "October 31, 1975" # song["release_date_components"] → {"year": 1975, "month": 10, "day": 31} # song["stats"]["pageviews"] → 11067562 # song["stats"]["contributors"] → 516 # song["stats"]["accepted_annotations"] → 20 # song["pyongs_count"] → 703 # song["annotation_count"] → 33 # song["comment_count"] → 253 # song["album"]["name"] → "Studio Collection" (varies by region) # song["albums"][0]["name"] → "A Night at the Opera" (first = original) # song["url"] → "https://genius.com/Queen-bohemian-rhapsody-lyrics" # song["path"] → "/Queen-bohemian-rhapsody-lyrics" # song["song_art_image_url"] → "https://images.genius.com/718de9d..." # song["explicit"] → False # song["language"] → "en" # song["lyrics_state"] → "complete" # song["lyrics_verified"] → False # song["spotify_uuid"] → "7tFiyTwD0nx5a1eklYtX2J" # song["youtube_url"] → "https://www.youtube.com/watch?v=fJ9rUzIMcZQ" # song["writer_artists"] → [{"name": "Freddie Mercury", ...}] # song["producer_artists"] → [{"name": "Roy Thomas Baker"}, {"name": "Queen"}] # song["featured_artists"] → [] # Primary album (first in list = original release): primary_album = song["albums"][0]["name"] # "A Night at the Opera" ``` ### Search ```python def genius_search(query, per_page=5): """Search Genius. Returns sections: top_hit, song, lyric, artist, album, video, article, user.""" url = f"https://genius.com/api/search/multi?per_page={per_page}&q={urllib.parse.quote(query)}" data = json.loads(genius_get(url)) return data["response"]["sections"] import urllib.parse sections = genius_search("Bohemian Rhapsody Queen", per_page=5) # sections is a list of dicts with keys: "type", "hits" # Each hit has: "type", "result" # For type="song", result has: id, full_title, url, primary_artist, stats, ... for section in sections: if section["type"] == "song": for hit in section["hits"]: r = hit["result"] print(r["full_title"], r["url"], r["id"]) # Bohemian Rhapsody by Queen https://genius.com/Queen-bohemian-rhapsody-lyrics 1063 break # Simpler search (song section only): def genius_search_songs(query, per_page=5): sections = genius_search(query, per_page) for s in sections: if s["type"] == "song": return [h["result"] for h in s["hits"]] return [] ``` ### Artist songs (paginated) ```python def genius_artist_songs(artist_id, per_page=20, sort="popularity"): """Fetch paginated list of songs for an artist. sort: 'popularity' or 'title'.""" page = 1 while True: url = (f"https://genius.com/api/artists/{artist_id}/songs" f"?per_page={per_page}&page={page}&sort={sort}") data = json.loads(genius_get(url))["response"] songs = data["songs"] if not songs: break yield from songs if data["next_page"] is None: break page = data["next_page"] # Example: get top 5 Queen songs by popularity for song in list(genius_artist_songs(563, per_page=5))[:5]: print(f"{song['full_title']} — {song['stats']['pageviews']:,} views") # Bohemian Rhapsody by Queen — 11,067,663 views # Don't Stop Me Now by Queen — 2,453,240 views # Under Pressure by Queen & David Bowie — 1,972,606 views # Somebody to Love by Queen — 1,241,740 views # Killer Queen by Queen — 1,146,813 views ``` --- ## Approach 2: Lyrics from HTML — Regex on data-lyrics-container The lyrics live in `