@checkleaked/spotify-api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 eduair94
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # @checkleaked/spotify-api
2
+
3
+ **Website:** [spotify.checkleaked.cc](https://spotify.checkleaked.cc) Β· **npm:** [@checkleaked/spotify-api](https://www.npmjs.com/package/@checkleaked/spotify-api)
4
+
5
+ Zero-dependency, fully-typed TypeScript SDK for the **Spotify Data API** β€” 110 typed methods across 25 namespaces covering search, artists, tracks, albums, lyrics, playlists, charts, browse, podcasts, concerts, tracking, webhooks and AI analysis.
6
+
7
+ - 🟒 **Zero runtime dependencies** β€” built on native `fetch`.
8
+ - 🌐 Runs on **Node 18+, Deno, Bun, edge runtimes and the browser**.
9
+ - πŸ“¦ Ships **ESM + CJS** with complete `.d.ts` types.
10
+ - πŸ†” Pass a Spotify **ID, URI (`spotify:…`) or URL** anywhere an id is expected β€” and **arrays** for multi-id endpoints.
11
+ - πŸ” Built-in **timeouts, retries with backoff** (honors `Retry-After`), **lifecycle hooks** and **uniform error handling**.
12
+ - πŸ“„ **Pagination helpers** for every `offset`/`limit` endpoint.
13
+ - πŸ”Œ **RapidAPI-compatible** out of the box (`spotify81.p.rapidapi.com`), with a `proxy` preset.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @checkleaked/spotify-api
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { createClient } from '@checkleaked/spotify-api';
25
+
26
+ const spotify = createClient({ apiKey: process.env.RAPIDAPI_KEY! });
27
+ // …or just `createClient()` and set SPOTIFY_API_KEY / RAPIDAPI_KEY in the env.
28
+
29
+ // Search
30
+ const results = await spotify.search.search({ q: 'daft punk', type: 'artists', limit: 5 });
31
+
32
+ // Artists β€” id, URI, or URL all work
33
+ const artist = await spotify.artists.overview({ id: 'spotify:artist:4tZwfgrHOc3mvqYlEYSvVi' });
34
+ const top = await spotify.artists.topTracks({ id: '4tZwfgrHOc3mvqYlEYSvVi', market: 'US' });
35
+
36
+ // Tracks & lyrics β€” pass an array of ids
37
+ const tracks = await spotify.tracks.get({ ids: ['0DiWol3AO6WpXZgp0goxAV', '7ouMYWpwJ422jRcDASZB7P'] });
38
+ const lyrics = await spotify.tracks.lyrics({ id: '0DiWol3AO6WpXZgp0goxAV', format: 'lrc' });
39
+
40
+ // Charts
41
+ const top200 = await spotify.charts.top200Tracks({ country: 'US' });
42
+ ```
43
+
44
+ Methods return the **unwrapped payload** (the API's `data` field) directly, so there is no `.data` to peel off at every call site.
45
+
46
+ ## IDs, URIs & URLs
47
+
48
+ Anywhere an `id` or `ids` is accepted you can pass a bare Spotify ID, a URI, or an open.spotify.com URL β€” they're reduced to the bare ID automatically. Multi-id endpoints also accept an **array**. Non-Spotify values (category slugs, webhook IDs) pass through untouched.
49
+
50
+ ```ts
51
+ await spotify.artists.overview({ id: 'https://open.spotify.com/intl-es/artist/4tZwfgrHOc3mvqYlEYSvVi' });
52
+ await spotify.artists.get({ ids: ['spotify:artist:4tZwfgrHOc3mvqYlEYSvVi', '06HL4z0CvFAxyc27GXpf02'] });
53
+
54
+ // Or normalize yourself:
55
+ import { normalizeId, normalizeIds } from '@checkleaked/spotify-api';
56
+ normalizeId('spotify:track:0DiWol3AO6WpXZgp0goxAV'); // '0DiWol3AO6WpXZgp0goxAV'
57
+ ```
58
+
59
+ ## Pagination
60
+
61
+ Every `offset`/`limit` endpoint can be iterated. You supply `getItems` to point at the page's array (shapes vary per endpoint):
62
+
63
+ ```ts
64
+ import { paginate, collect } from '@checkleaked/spotify-api';
65
+
66
+ const getItems = (p: any) => p?.data?.artist?.discography?.albums?.items ?? [];
67
+
68
+ // Iterate page by page
69
+ for await (const page of paginate((p) => spotify.artists.albums({ id, ...p }), { limit: 50, getItems })) {
70
+ // …handle page
71
+ }
72
+
73
+ // Or collect everything (bounded by maxItems / maxPages)
74
+ const albums = await collect((p) => spotify.artists.albums({ id, ...p }), { limit: 50, getItems, maxItems: 500 });
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ ```ts
80
+ const spotify = createClient({
81
+ apiKey: 'xxxxx', // sent as `x-rapidapi-key` (or env SPOTIFY_API_KEY / RAPIDAPI_KEY)
82
+ provider: 'rapidapi', // 'rapidapi' (default) | 'proxy'
83
+ // baseUrl: 'https://…', // override the base URL entirely
84
+ // host: 'spotify81.p.rapidapi.com', // override the x-rapidapi-host header
85
+ timeoutMs: 30_000, // per-request timeout (default 30s)
86
+ retries: 2, // retries on 429 / 5xx / network (default 2)
87
+ retryDelayMs: 500, // base backoff, exponential + jitter (default 500ms)
88
+ headers: { 'x-my-header': '1' }, // merged into every request
89
+ // fetch: customFetch, // inject a fetch implementation
90
+ // userAgent: 'my-app/1.0',
91
+ // debug: true, // log every request/response/retry
92
+ // onRequest, onResponse, onRetry, // lifecycle hooks (see below)
93
+ });
94
+ ```
95
+
96
+ ## Debugging & hooks
97
+
98
+ ```ts
99
+ const spotify = createClient({
100
+ apiKey,
101
+ debug: true, // or a custom (message, detail) => void
102
+ onRequest: ({ method, url }) => metrics.count('spotify.request', { url }),
103
+ onResponse: ({ status, durationMs }) => metrics.timing('spotify.latency', durationMs, { status }),
104
+ onRetry: ({ attempt, status, delayMs }) => log.warn(`retry #${attempt} in ${delayMs}ms (${status})`),
105
+ });
106
+ ```
107
+
108
+ ### Providers
109
+
110
+ | Provider | Base URL | Notes |
111
+ | --- | --- | --- |
112
+ | `rapidapi` (default) | `https://spotify81.p.rapidapi.com` | Adds `x-rapidapi-host`. Use your RapidAPI key. |
113
+ | `proxy` | `https://spotify-proxy.checkleaked.cc/spotify-data` | Direct proxy. |
114
+
115
+ ## Return shape & raw envelope
116
+
117
+ Every response is wrapped by the API in `{ success, data, metadata }`. Methods return `data`. To read `metadata` (pagination cursors, timings, etc.), call the low-level request:
118
+
119
+ ```ts
120
+ const envelope = await spotify.request({ method: 'GET', path: '/search', query: { q: 'muse' } });
121
+ // { success: true, data: {...}, metadata: {...} }
122
+ ```
123
+
124
+ ## Error handling
125
+
126
+ Any non-2xx response, or a body with `success: false`, throws a `SpotifyApiError`:
127
+
128
+ ```ts
129
+ import { SpotifyApiError } from '@checkleaked/spotify-api';
130
+
131
+ try {
132
+ await spotify.artists.overview({ id: 'bad-id' });
133
+ } catch (err) {
134
+ if (err instanceof SpotifyApiError) {
135
+ console.log(err.status); // HTTP status (e.g. 404)
136
+ console.log(err.code); // upstream error code, or TIMEOUT/NETWORK/ABORTED/CONFIG
137
+ console.log(err.url); // request URL
138
+ console.log(err.body); // parsed response body
139
+ console.log(err.isRateLimit, err.isServerError, err.isTimeout);
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## Timeouts, retries & cancellation
145
+
146
+ - Requests time out after `timeoutMs` (default 30s) and raise a `SpotifyApiError` with `code: 'TIMEOUT'`.
147
+ - `429` and `5xx` responses are retried up to `retries` times with exponential backoff + jitter, honoring the `Retry-After` header.
148
+ - Cancel a request with an `AbortSignal`:
149
+
150
+ ```ts
151
+ const controller = new AbortController();
152
+ setTimeout(() => controller.abort(), 1000);
153
+ await spotify.search.search({ q: 'muse' }, { signal: controller.signal });
154
+ ```
155
+
156
+ ## Custom `fetch` (edge / browser / testing)
157
+
158
+ ```ts
159
+ const spotify = createClient({ apiKey, fetch: (url, init) => myFetch(url, init) });
160
+ ```
161
+
162
+ ## API reference
163
+
164
+ All 25 namespaces and their methods. Every method takes a typed `params` object (where applicable) and an optional trailing `options?: { signal?, headers? }`.
165
+
166
+ | Namespace | # | Methods |
167
+ | --- | --- | --- |
168
+ | `search` | 5 | `search`, `topResults`, `lyrics`, `suggestions`, `multiMarket` |
169
+ | `albums` | 3 | `get`, `tracks`, `metadata` |
170
+ | `artists` | 10 | `get`, `overview`, `discographyOverview`, `albums`, `singles`, `appearsOn`, `discoveredOn`, `featuring`, `related`, `topTracks` |
171
+ | `tracks` | 4 | `get`, `credits`, `lyrics`, `recommendations` |
172
+ | `batch` | 5 | `albumMetadata`, `artistOverview`, `trackCredits`, `trackLyrics`, `trackPopularity` |
173
+ | `analysis` | 3 | `artistCompare`, `audioFeatures`, `playlistAnalysis` |
174
+ | `lookups` | 2 | `isrc`, `upc` |
175
+ | `playlists` | 3 | `get`, `tracks`, `fromSeed` |
176
+ | `snapshots` | 2 | `list`, `create` |
177
+ | `users` | 2 | `profile`, `followers` |
178
+ | `charts` | 6 | `topByMonthlyListeners`, `top200Tracks`, `topArtists`, `topAlbums`, `viralTracks`, `topByFollowers` |
179
+ | `downloads` | 2 | `track`, `soundcloud` |
180
+ | `browse` | 5 | `categories`, `category`, `featuredPlaylists`, `newReleases`, `recommendations` |
181
+ | `audiobooks` | 3 | `get`, `getById`, `chapters` |
182
+ | `shows` | 3 | `get`, `getById`, `episodes` |
183
+ | `episodes` | 2 | `get`, `byId` |
184
+ | `markets` | 1 | `get` |
185
+ | `partner` | 6 | `playlist`, `track`, `trackCount`, `album`, `artistOverview`, `artistDiscography` |
186
+ | `concerts` | 5 | `locations`, `feed`, `get`, `byArtist`, `searchArtists` |
187
+ | `webhooks` | 6 | `get`, `register`, `stats`, `delete`, `test`, `deliveries` |
188
+ | `cache` | 2 | `stats`, `invalidate` |
189
+ | `tracking` | 7 | `track`, `untrack`, `items`, `item`, `logs`, `stats`, `health` |
190
+ | `ai` | 13 | `health`, `types`, `analyze`, `chartTrends`, `artistSummary`, `albumReview`, `trackDeepDive`, `playlistCurator`, `marketComparison`, `genreLandscape`, `moodProfile`, `viralPotential`, `custom` |
191
+ | `health` | 8 | `ping`, `get`, `instance`, `cluster`, `clusterStrict`, `proxies`, `clearProxies`, `clearErrors` |
192
+ | `credentials` | 2 | `status`, `redisCookies` |
193
+
194
+ > **Response typing.** Request parameters are precisely typed from the API's OpenAPI spec. Response payload types for **74 endpoints** were inferred from real API responses (via [quicktype](https://quicktype.io/)); the remaining endpoints (AI analysis, downloads, webhooks mutations) return `unknown` β€” cast to your own type as needed. Some endpoints exposed on the direct proxy are not available on all RapidAPI plans.
195
+
196
+ ## Development
197
+
198
+ ```bash
199
+ npm run build # tsup -> dist (esm + cjs + d.ts)
200
+ npm test # vitest (mocked fetch, no network)
201
+ npm run typecheck # tsc --noEmit
202
+
203
+ # Regenerate types from the live API (needs a key):
204
+ npm run codegen:endpoints # openapi.json -> codegen.json
205
+ SPOTIFY_API_KEY=xxx npm run codegen:sample # sample endpoints -> fixtures
206
+ npm run codegen:types # quicktype fixtures -> src/generated/responses.ts
207
+ npm run codegen:enhance # upgrade unknown->typed returns, widen id arrays
208
+ npm run codegen:client # assemble client.ts + index.ts
209
+ ```
210
+
211
+ ## License
212
+
213
+ MIT Β© eduair94