@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 +21 -0
- package/README.md +213 -0
- package/dist/index.cjs +1630 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4068 -0
- package/dist/index.d.ts +4068 -0
- package/dist/index.js +1550 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
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
|