Search for any song and print its full lyrics to your terminal — no API key, no account, no config.
Every lyrics API either requires an account, charges for access, or hides results behind a client-side JavaScript wall.
lyrics-scraper skips all of that — one command, full lyrics, nothing else.
git clone https://github.com/johnlester-0369/lyrics-scraper.git
cd lyrics-scraper
npm install
node index.js Ava Max TornExpected output:
Searching for: Ava Max Torn
Fetching lyrics from: https://genius.com/Ava-max-torn-lyrics
[Verse 1]
You, you take all of the li-li-light away
...
node index.js <song name>All arguments after node index.js are joined into a single query — shell quoting is never required.
# Single-word query
node index.js Torn
# Multi-word title — no quotes needed
node index.js Ava Max Torn
# Artist + title
node index.js Kendrick Lamar Not Like Us- Node.js ≥ 14 — required for native ES module support (
"type": "module"is set inpackage.json) - npm — bundled with Node.js
Three steps run on every invocation:
1. Search
Calls the Genius internal search API (/api/search/song?q=<query>) with the joined argument string. The top-ranked hit is selected — Genius orders results by relevance, so index 0 is the closest match. The result's fully-qualified URL is passed to the scraper.
2. Fetch
Retrieves the lyrics page using a real browser User-Agent header. Genius returns a JavaScript-only shell to headless clients; spoofing the header triggers the fully server-rendered HTML with lyrics embedded in [data-lyrics-container] divs.
3. Extract
Genius splits a song's lyrics across multiple [data-lyrics-container] divs with ad units injected between them in the DOM. Each container is cleaned independently before extraction:
<br>tags are replaced with\nbefore.text()— Cheerio's text extractor silently drops all tags, producing run-together lines if<br>is not converted first[data-exclude-from-selection]blocks are removed — these wrap the contributor count, translation links, song title, and bio preview that appear visually inside the first lyrics container but are not lyric content- Zero-width
<span tabindex="0">accessibility elements are removed — they produce empty text fragments in the extracted output - Empty containers are skipped — Genius sometimes renders placeholder divs that match the selector but contain no lyric content
All cleaned containers are joined with double newlines to reconstruct the full song.
- DOM dependency — scraping is tied to Genius's current HTML structure. If Genius changes the
[data-lyrics-container]attribute or injects non-lyric content into containers differently, extraction will break or produce incorrect output. - Top result only — the search always selects the first hit. Ambiguous queries (e.g.,
node index.js Tornwithout an artist name) may return a cover, remix, or unrelated track. - Rate limiting — Genius may throttle or block rapid repeated requests from the same IP address.
- Lyrics-only pages — requesting a URL that does not contain
[data-lyrics-container]divs throws an explicit error. Genius artist pages, album pages, and annotations will not work.
| Package | Version | Purpose |
|---|---|---|
| axios | ^1.13.6 | HTTP requests to Genius search API and lyrics pages |
| cheerio | ^1.2.0 | Server-side HTML parsing and DOM traversal for lyric extraction |
ISC