https://github.com/frostbreker/spotify-private-api
This package provides a simple way to interact with Spotify's private API. It includes methods to fetch track information and play count.
https://github.com/frostbreker/spotify-private-api
api golang scraper spotify
Last synced: 6 months ago
JSON representation
This package provides a simple way to interact with Spotify's private API. It includes methods to fetch track information and play count.
- Host: GitHub
- URL: https://github.com/frostbreker/spotify-private-api
- Owner: FrostBreker
- License: mit
- Created: 2023-12-03T15:55:39.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-04-08T16:46:26.000Z (over 2 years ago)
- Last Synced: 2025-06-15T07:48:23.283Z (about 1 year ago)
- Topics: api, golang, scraper, spotify
- Language: Go
- Homepage:
- Size: 28.3 KB
- Stars: 8
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.MD
Awesome Lists containing this project
README
# Spotify Private API Go Package
[](https://golang.org)
[](LICENSE.MD)
[](https://github.com/FrostBreker/spotify-private-api/actions/workflows/tests.yml)
[](https://goreportcard.com/report/github.com/FrostBreker/spotify-private-api)
A Go package for accessing Spotify data by scraping public pages. No authentication or API tokens required.
## Features
- ๐ต **Track Information** - Track name, duration, album, artists, and cover art
- ๐ค **Artist Profiles** - Artist stats, biography, followers, and monthly listeners
- ๐ฟ **Album Details** - Album info with track listings and artwork
- ๐ **No Authentication** - Works without any API tokens or OAuth
- ๐ **Browser-Like Requests** - Uses TLS fingerprinting to mimic real browsers
- โก **Context Support** - All operations support cancellation and timeouts
- ๐งช **Fully Testable** - Mock support for comprehensive unit testing
- ๐ก๏ธ **Type-Safe Errors** - Custom error types for precise error handling
## Installation
```bash
go get github.com/FrostBreker/spotify-private-api
```
**Requirements:** Go 1.24 or higher
## Quick Start
```go
package main
import (
"context"
"fmt"
"log"
spotify "github.com/FrostBreker/spotify-private-api"
)
func main() {
client := spotify.NewClient()
ctx := context.Background()
// Fetch a track
track, err := client.FetchTrack(ctx, "4cOdK2wGLETKBW3PvgPWqT")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Track: %s\n", track.Data.TrackUnion.Name)
fmt.Printf("Artist: %s\n", track.Data.TrackUnion.FirstArtist.Items[0].Profile.Name)
fmt.Printf("Album: %s\n", track.Data.TrackUnion.AlbumOfTrack.Name)
}
```
## Usage Examples
### Track Information
```go
client := spotify.NewClient()
ctx := context.Background()
track, err := client.FetchTrack(ctx, "4cOdK2wGLETKBW3PvgPWqT")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s\n", track.Data.TrackUnion.Name)
fmt.Printf("URI: %s\n", track.Data.TrackUnion.URI)
fmt.Printf("Duration: %dms\n", track.Data.TrackUnion.Duration.TotalMilliseconds)
fmt.Printf("Album: %s\n", track.Data.TrackUnion.AlbumOfTrack.Name)
if len(track.Data.TrackUnion.FirstArtist.Items) > 0 {
fmt.Printf("Artist: %s\n", track.Data.TrackUnion.FirstArtist.Items[0].Profile.Name)
}
```
### Artist Information
```go
client := spotify.NewClient()
ctx := context.Background()
artist, err := client.FetchArtist(ctx, "4tZwfgrHOc3mvqYlEYSvVi")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Artist: %s\n", artist.Data.ArtistUnion.Profile.Name)
fmt.Printf("Followers: %d\n", artist.Data.ArtistUnion.Stats.Followers)
fmt.Printf("Monthly Listeners: %d\n", artist.Data.ArtistUnion.Stats.MonthlyListeners)
// Access artist's avatar
if len(artist.Data.ArtistUnion.Visuals.AvatarImage.Sources) > 0 {
fmt.Printf("Avatar URL: %s\n", artist.Data.ArtistUnion.Visuals.AvatarImage.Sources[0].URL)
}
```
### Album Information
```go
client := spotify.NewClient()
ctx := context.Background()
album, err := client.FetchAlbum(ctx, "2noRn2Aes5aoNVsU6iWThc")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Album: %s\n", album.Data.AlbumUnion.Name)
fmt.Printf("URI: %s\n", album.Data.AlbumUnion.URI)
fmt.Printf("Total Tracks: %d\n", album.Data.AlbumUnion.Tracks.TotalCount)
if len(album.Data.AlbumUnion.Artists.Items) > 0 {
fmt.Printf("Artist: %s\n", album.Data.AlbumUnion.Artists.Items[0].Profile.Name)
}
```
## Configuration Options
```go
// Enable debug logging
client := spotify.NewClient(
spotify.WithDebug(true),
)
// Custom logger (uses log/slog)
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
client := spotify.NewClient(
spotify.WithLogger(logger),
)
// Custom HTTP client for testing
client := spotify.NewClient(
spotify.WithHTTPDoer(mockDoer),
)
```
## API Reference
### Client Methods
| Method | Parameters | Description | Returns |
| --------------- | ----------------- | ------------------------ | ------------------ |
| `FetchTrack()` | `ctx`, `trackID` | Get track information | `*TrackResponse` |
| `FetchArtist()` | `ctx`, `artistID` | Get artist profile | `*ArtistResponse` |
| `FetchAlbum()` | `ctx`, `albumID` | Get album details | `*AlbumResponse` |
### Configuration Options
| Option | Description |
| ---------------------- | ---------------------------------- |
| `WithDebug(bool)` | Enable/disable debug logging |
| `WithHTTPDoer(Doer)` | Use custom HTTP doer (for testing) |
| `WithLogger(*slog.Logger)` | Use custom structured logger |
## Error Handling
```go
import "github.com/FrostBreker/spotify-private-api/errors"
track, err := client.FetchTrack(ctx, trackID)
if err != nil {
if errors.IsAPIError(err) {
fmt.Println("API returned an error")
}
if errors.IsParseError(err) {
fmt.Println("Failed to parse response")
}
if err == errors.ErrInvalidID {
fmt.Println("Invalid Spotify ID provided")
}
}
```
### Error Types
| Error Type | Description |
| -------------- | -------------------------------------- |
| `APIError` | Spotify returned an error response |
| `ParseError` | JSON parsing or processing error |
| `RequestError` | HTTP request failed |
### Sentinel Errors
| Error | Description |
| ------------------ | --------------------------- |
| `ErrInvalidID` | Invalid Spotify ID provided |
| `ErrEmptyResponse` | Empty response received |
## Project Structure
```
spotify-private-api/
โโโ client.go # Main client implementation
โโโ track.go # Track operations
โโโ artist.go # Artist operations
โโโ album.go # Album operations
โโโ doc.go # Package documentation
โโโ errors/
โ โโโ errors.go # Custom error types
โโโ internal/
โ โโโ http/
โ โโโ http.go # Browser-like HTTP client
โโโ models/
โ โโโ track.go # Track response types
โ โโโ artist.go # Artist response types
โ โโโ album.go # Album response types
โโโ example/
โโโ main.go # Usage examples
```
## Testing
```go
// Create a mock HTTP client
type mockHTTPClient struct {
doFunc func(req *http.Request) (*http.Response, error)
}
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
return m.doFunc(req)
}
// Use in tests
client := spotify.NewClient(
spotify.WithHTTPDoer(&mockHTTPClient{
doFunc: func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(strings.NewReader(mockHTML)),
}, nil
},
}),
)
```
Run tests:
```bash
go test ./...
go test -v ./...
go test -cover ./...
```
## How It Works
This package scrapes Spotify's public web pages to extract embedded data. Spotify embeds JSON data in a `` tag as base64-encoded content. The package:
1. Makes HTTP requests with browser-like TLS fingerprints (using CycleTLS)
2. Parses the HTML response to find the embedded data
3. Decodes and unmarshals the JSON into typed Go structs
## Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
## License
This project is licensed under the MIT License - see the [LICENSE.MD](LICENSE.MD) file for details.
## Disclaimer
โ ๏ธ **Important**: This package scrapes Spotify's public web pages. Use responsibly.
- This package is for educational and research purposes only
- The page structure may change without notice, potentially breaking this package
- Excessive use may result in rate limiting or IP blocks
- Not affiliated with or endorsed by Spotify
---
**Happy Listening! ๐ง**