tekore
latest

Package

  • Release notes
  • Reference

Guide

  • Getting started
  • Authorisation guide
  • Advanced usage
  • Examples
  • Additional resources
tekore
  • tekore 5.4.0 documentation
  • Edit on GitHub

logo

Welcome to the online documentation of Tekore! We provide a client for the Spotify Web API for Python, complete with all available endpoints and authentication methods, async support and loads of additional features. Tekore allows you to interact with the API effortlessly. Here’s five lines to get you full access and start playing your top songs.

import tekore as tk

conf = (client_id, client_secret, redirect_uri)
token = tk.prompt_for_user_token(*conf, scope=tk.scope.every)

spotify = tk.Spotify(token)
tracks = spotify.current_user_top_tracks(limit=10)
spotify.playback_start_tracks([t.id for t in tracks.items])

See our homepage on PyPI for more information about the package and its versions. If you’ve found a bug or would like to propose a feature, please submit an issue on GitHub. Join our Discord community to ask for help or discuss just about anything related to Tekore. You can also ask a question on Stack Overflow.

If you’re new here, have a look at Getting started. Example scripts can be found in Examples. Detailed information is available in our concise Reference.

Features

The Web API provides access to a plethora of data on music and users. Tekore implements these integral features completely.

  • Authorisation for applications and users.

  • Endpoints for access to every resource in the API.

Additional features are provided for your convenience.

  • Support for asynchronous programming using async/await.

  • A self-refreshing access token.

  • Responses are parsed into models with explicit attributes. They have a readable repr and can be serialised back to JSON.

  • Senders, a hook between Tekore and the Web API. Enables retries on failed requests, response caching and session persistence.

  • Access rights for user tokens.

  • Conversions between Spotify IDs, URIs and URLs.

  • Read and write configuration from files and environment variables.

Release notes

Unreleased

Fixed
  • Make available_markets of Show, LocalAlbum and LocalTrack optional (#323)

5.4.0 (2024-02-27)

Fixed
  • Add ep as a valid enum to AlbumType for top tracks API responses (#318)

  • Add undocumented smart_shuffle to CurrentlyPlayingContext (#320)

Added
  • Support HTTPX 0.27 (#317)

5.3.1 (2024-01-28)

Fixed
  • Reference: remove methods playlist_remove_indices and playlist_remove_occurrences, which were removed from the API (#315)

5.3.0 (2023-12-22)

Fixed
  • Make images optional in Playlist to fix getting empty playlists without an image (#309)

  • Add undocumented dict to types of publisher in Show (#310)

  • Make is_playable in Episode optional despite API specifications (#310)

  • Make deprecated language optional in Episode pending removal (#310)

  • Add undocumented dict to types of duration_ms in LocalTrack (#310)

  • Add undocumented available_markets to FullPlaylistEpisode (#310)

Added
  • Add restrictions to FullEpisode (#310)

  • Support HTTPX 0.26 (#311)

  • Improve UnknownModelAttributeWarning to include model name (#313)

5.2.1 (2023-11-22)

Fixed
  • Exclude Pydantic versions 2.5.0 and 2.5.1 (#306)

  • Add missing available_markets to FullChapter (#308)

5.2.0 (2023-11-05)

Fixed
  • Add undocumented is_externally_hosted attribute to Audiobook (#302)

  • Make audio_preview_url of Episode optional (#305)

Added
  • Add support for short URLs as is_short_link() and follow_short_link (#301)

5.1.1 (2023-10-14)

Fixed
  • Make Album optional (#300)

5.1.0 (2023-10-04)

Added
  • Support HTTPX 0.25 (#294)

Fixed
  • Remove misleading documentation on models (#295)

  • Add missing supports_volume to Device (#296)

  • Exclude Pydantic version 2.4.0 (#297)

5.0.1 (2023-07-06)

Fixed
  • Add missing Optional annotations to support Pydantic 2 (#293)

5.0.0 (2023-06-18)

Tekore 5 comes with an overhauled response model system based on Pydantic. Although the underlying change is major, the primary usage of models remains unchanged. The new models are more robust and easier to maintain. However, with more careful data validation new issues may arise. Please submit them on GitHub.

Changed
  • Remove support for Python 3.7 (EOL) (#292)

  • Use Pydantic in response models (#279)

    • Many type hints are fixed and improved.

    • Instead of retaining unknown response attributes, they are now discarded. However, the same warning message is raised.

    • .json and .asbuiltin methods are replaced by Pydantic models’ .json and .dict.

    • Models use the builtin datetime object directly.

    • .pprint and custom __repr__ are removed in favor of using Pydantic’s own machinery.

    • The builtin list class is used everywhere instead of the old ModelList.

4.6.1 (2023-05-25)

Fixed
  • FullAlbum - remove “album_group” attribute introduced in version 4.6.0 (#291)

4.6.0 (2023-04-12)

Added
  • Make enumerations case insensitive (#285)

  • Support HTTPX 0.24 (#289)

Fixed
  • FullChapter / SimpleChapter - change “restriction” to “restrictions” (#286)

  • FullAlbum - add undocumented “album_group” attribute (#287)

4.5.0 (2022-11-06)

Added
  • Errors - carry scope information with Unauthorised (#276)

  • Client - add new audiobook (Audiobook API) and chapter Chapter API endpoints and “audiobook” as a valid search type. Note that audiobooks are currently only available in the US market. The APIs also seem to be unreliable. (#277)

  • Client - add new get queue endpoint playback_queue. Queue results seem inconsistent at this time. (#278)

Fixed
  • Models - add missing “minute” resolution to ReleaseDatePrecision (#277)

  • Models - parse response models safely everywhere (#280)

  • Client - fix chunking of empty inputs (#281)

4.4.1 (2022-10-08)

Fixed
  • Handle changed response to fix paging at search limit (#275)

4.4.0 (2022-05-24)

Added
  • Dependency to HTTPX upgraded to include version 0.23.* (#272)

  • Declare support for Python 3.10 (#273)

4.3.0 (2022-03-11)

Added
  • Dependency to HTTPX upgraded to include version 0.22.* (#267)

  • Expose the underlying credentials manager in RefreshingCredentials and RefreshingToken to facilitate closing their HTTP client, which is no longer closed by default as of HTTPX version 0.22 (#267)

Fixed
  • Add missing context “collection” to ContextType for playing saved tracks (#270)

4.2.0 (2022-01-19)

Added
  • Conversions - add user as a valid ID type and URL-encode hashes of user IDs in to_url() (#266)

Fixed
  • URL-encode hashes in user IDs in user (#266)

4.1.0 (2021-11-20)

  • Dependency to HTTPX upgraded to include versions 0.20.0-0.21.* (#263, #265)

4.0.0 (2021-09-09)

Tekore 4.0 is a maintenance release to prepare for the nearly deprecated Python 3.6, update dependencies and improve backwards compatibility.

Changed
  • Removed Python 3.6 support and its conditional dependencies (#252)

  • Dependency to HTTPX upgraded to include versions from 0.15 to 0.19.* (#250, #261)

  • Add fields to Request and change their meaning to be in line with the new HTTPX interface (#250)

Added
  • Improved documentation for type hints and response models (#109)

  • Responses can now parse unknown attributes, greatly improving backwards compatibility. UnknownModelAttributeWarning was introduced (#247)

3.7.1 (2021-05-04)

Fixed
  • Models - add missing but undocumented html_description field to FullShow and SimpleShow (#251)

  • Models - require the newly documented html_description field in FullEpisode and SimpleEpisode (#251)

3.7.0 (2021-04-08)

Added
  • Client - add the new market API (#249)

  • Client - add episode endpoints to library API, and the corresponding SavedEpisode and SavedEpisodePaging models (#249)

3.6.2 (2021-03-23)

Fixed
  • Models - add missing but undocumented html_description field to FullEpisode and SimpleEpisode (#246)

3.6.1 (2021-03-07)

Fixed
  • Authorisation - allow missing scope in token responses and parse it to an empty Scope (#245)

3.6.0 (2021-03-02)

Added
  • Client - make context managers async safe on Python 3.7+, adds a dependency to the contextvars backport for Python 3.6 (#242)

  • Dependency to HTTPX upgraded to include version 0.17.* (#243)

3.5.1 (2021-02-12)

Fixed
  • Client - document decreased limit of search total (#241)

3.5.0 (2021-01-15)

Added
  • Authorisation - add streaming and app-remote-control as extra scopes (#237)

Fixed
  • Client - fix type hints for context managers (#239)

3.4.2 (2020-12-14)

Fixed
  • Models - fix model repr for optional lists (#233)

3.4.1 (2020-12-04)

Fixed
  • Client - document the need for at least one seed in recommendations (#229)

  • Client - match new behavior of track markets (#231)

3.4.0 (2020-11-24)

Added
  • Conversions - ignore URL parameters in from_url() (#226)

  • Conversions - from_uri(), from_url() raise proper errors with entirely invalid formats, error messages were improved (#227)

Fixed
  • Client - document new behavior of track markets (#228)

3.3.0 (2020-10-22)

Added
  • Configuration - warning messages for missing configuration now include the variable name which was missing (#222)

  • Models - improved type hints and documentation of potentially missing values (#221)

Fixed
  • Client - document new behavior of track markets (#217)

3.2.0 (2020-10-16)

Added
  • Support Python 3.9 (#219)

  • Dependency to HTTPX upgraded to include versions 0.15.* and 0.16.* (#216)

  • Error messages for parse_code_from_url(), parse_state_from_url() and Credentials.pkce_user_authorisation() were improved (#218)

  • Spotify and Senders, both synchronous and asynchronous, can now be closed directly with close (#220)

3.1.0 (2020-09-13)

Added
  • StrEnum - model enumerations now inherit from str, making e.g. using it as a key for sorting possible (#214)

Fixed
  • PrivateUser - a birthday attribute was added. It is not obtainable with new tokens but is returned for old tokens that have the now-invalid user-read-birthday scope (#52, #197)

3.0.1 (2020-09-05)

Fixed
  • featured_playlists - allow missing owner in Playlist models (#212)

3.0.0 (2020-09-03)

The next major iteration of Tekore brings fewer breaking changes than in 2.0, but packs a number of improvements to authorisation and senders. Most notably, PKCE is now provided as an option for user authorisation and Requests is no longer used to perform web requests. HTTPX, which was already in use with async, is used exclusively instead.

Added
  • Authorisation - PKCE can be used in user authorisation, providing added security for public clients by removing the need to use a client secret. (#189)

  • UserAuth - implement user authorisation with security checks, the caller simply provides the resulting URI after redirection (#207)

  • Senders - Tekore’s own Request and Response wrappers are now used in the sender interface (#139)

  • Classes now have a readable repr (#191)

  • Dependency to HTTPX upgraded to include version 0.14.* (#202)

  • gen_state() - generate state for user authorisation (#207)

  • parse_state_from_url() - parse state from URL parameters (#207)

Removed
  • Playlist API - methods for playlist tracks and episodes_as_tracks argument of Spotify.playlist() deprecated in 2.0 (#178, #202)

  • Dependency to Requests dropped in favor of HTTPX (#139)

  • Senders - TransientSender and SingletonSender along with their asynchronous variants were removed (#139)

  • Senders - default sender and keyword argument options were removed (#139)

Changed
  • Errors - web exceptions now inherit from Exception rather than the underlying HTTP library’s top-level exception. They always contain the relevant Request and Response (#139)

  • Senders - as the only concrete senders, PersistentSender and AsyncPersistentSender are now implemented in SyncSender and AsyncSender, respectively (#139)

  • CachingSender - argument order is now in line with RetryingSender (#139)

  • Senders - clients (Spotify and Credentials) now inherit from ExtendingSender (#139)

  • Authorisation - raise a more descriptive error if secret is required but not provided (#210)

Fixed
  • Client - fix chunking errors that occurred when passing certain parameters as positional arguments (#205)

2.1.3 (2020-08-04)

Fixed
  • Client - correctly return ModelList when chunking input (#196)

  • Authorisation - fix error handling when response does not contain an error description (#199)

  • playback and playback_currently_playing - correctly handle local tracks (#200)

2.1.2 (2020-07-21)

Fixed
  • FullShow - add undocumented total_episodes parameter, mark total_episodes of SimpleShow undocumented (#194)

2.1.1 (2020-07-02)

Fixed
  • SimpleShow - add optional total_episodes parameter (#190)

2.1.0 (2020-05-31)

Added
  • Client - required and optional scopes can now be determined in code for any endpoint of the client (#180)

  • Dependency to HTTPX upgraded to include version 0.13.* (#186)

Fixed
  • Errors - correctly fall back to ClientError and ServerError when encountering an unknown status code (#185)

2.0.0 (2020-05-27)

This release significantly improves the overall structure of the library and provides quality of life enhancements to various tasks. Most notably, submodules were removed in favor of a flat structure. Everything is now imported from the top level with the exception of Models.

Removed
  • Importing from submodules (#81)

  • OAuthError in Authorisation - see below for details (#154)

Deprecated
  • Playlist API - methods specifically for playlist tracks and episodes_as_tracks argument of Spotify.playlist() (#178)

Added
  • Authorisation - a list of scopes and strings is accepted in scope arguments (#81)

  • Scope operations were expanded to properly handle all combinations of str, scope and Scope (#177)

  • Playlist API - new methods to fully support episodes in playlists. The new endpoints are direct counterparts to playlist_tracks_* methods. (#178)

Changed
Import structure

Submodules were removed in favor of a flat structure. In addition to simply relocating objects, some changes were made as well. (#81)

  • Options for Senders and Configuration are now set at the top level

  • AuthorisationScopes was renamed to scope, and its alias scopes is no longer available

  • Ready-made scopes read, write and every are now accessed via the scope enumeration

Response models

These changes aim to make Models consistent and serialisation clear. (#149)

  • The JSON encoder used internally was made private

  • Hierarchies and names of model base classes and member types changed

  • Instead of using str, models are now converted to JSON using their json method

  • As a result of the change above, the repr of models can be viewed simply with print. The repr of model lists was significantly improved. Viewing attributes of models produces consistent results.

  • The asbuiltin method replaces asdict() for models and was also added for lists of models. Enumerations and timestamps are no longer preserved in the conversion.

  • pprint output is now compact by default

Playlist items

Boolean attributes of FullTrack and FullEpisode on a playlist were previously also available elsewhere, but had None values. They were removed. The booleans are still available in playlist-related calls with the new FullPlaylistTrack and FullPlaylistEpisode. LocalPlaylistTrack now also provides these booleans. (#170)

Miscellaneous
  • Exceptions thrown in Authorisation now match Client. Because of that, OAuthError was removed. Errors now inherit from a common base class. (#154)

  • Token.scope and RefreshingToken.scope now return a Scope instead of a string. (#177)

  • Default sender changed from TransientSender to PersistentSender, also affects Client behavior (#141)

Fixed
  • Properly close sessions in PersistentSender (#179)

  • Members of AlbumGroup are now strings as intended, rather than one-element tuples (#181)

  • Include readme to source distributions to fix setup (#182)

1.7.0 (2020-04-28)

Added
  • Most imports can be done directly at the top level (#174)

Deprecated
  • Importing from submodules, removed in Tekore 2.0 (#81)

Fixed
  • recommendations documentation changed to reflect that only IDs are accepted as seeds, not URIs or URLs (#173)

  • track_audio_analysis allow for missing attributes in analysis (#175)

1.6.0 (2020-04-07)

Added
  • Client - Support for podcasts. New APIs for episodes and shows. New scope user-read-playback-position for returning episode resume points. New endpoints for saving shows in a user’s library. playback_queue_add now accepts episodes. playback and playback_currently_playing can return currently playing episodes and shows. playlist and playlist_tracks can return episodes on playlists. search allows for searching episodes and shows. (#164)

  • Dependency to HTTPX upgraded to include version 0.12.* (#166)

Fixed
  • Errors are now correctly raised when parsing responses in playlist and playlist_tracks (#164)

  • Conversions to_url() now return URLs with prefix https instead of http, in line with API and application behavior. from_url() now correctly accepts https addresses for conversion. (#165)

  • Models - The repr of local items can now be produced without errors (#171)

1.5.0 (2020-03-11)

Added
  • RetryingSender - avoid unnecessary retries and reduce total wait time (#163)

Fixed
  • category_playlists require category parameter (#160)

  • AsyncPersistentSender - persist connections appropriately (#161)

  • playback_queue_add match endpoint address to changed API (#162)

1.4.0 (2020-03-02)

Added
  • playlist_tracks_clear - convenience endpoint for deleting tracks from a playlist (#155)

  • Conversions - accept shows and episodes as valid types (#159)

Fixed
  • playlist_tracks_add - insert tracks in correct order when chunking (#156)

1.3.0 (2020-02-26)

Added
  • playback_queue_add - add tracks to queue (#152)

  • Models - readable repr for models (32911c3a)

  • CachingSender - option to specify maximum cache size (#143)

  • Client - optionally send long lists of resources as chunks circumventing API limits (#153)

1.2.0 (2020-02-17)

Added
  • Client - optionally use maximum limits in all paging calls (#66)

Fixed
  • Paging navigation - respect API limits when retrieving all items or pages of a search (#145)

  • Paging navigation - always return an awaitable when asynchronous (#146)

1.1.0 (2020-02-02)

Added
  • Async support in authentication and API endpoints (#131)

  • CachingSender - a sender for response caching (#4)

  • Configuration - reading missing values produces a warning (0fa61801)

Fixed
  • playlist - parse correctly when fields is specified (#142)

1.0.1 (2020-01-17)

Fixed
  • PlaylistTrack - accept missing video thumbnail (#132)

1.0.0 (2020-01-14)

  • Packaging improvements

  • Declare versioning scheme

0.1.0 (2020-01-14)

Initial release of Tekore!

Reference

All public objects are documented in these sections.

Authorisation

Web API authorisation

Client

Web API endpoints

Configuration

Import and export credentials

Conversions

Convert IDs, URIs and URLs

Errors

Web errors for clients

Models

Response models

Senders

Request senders

Authorisation

Web API authorisation.

Credentials

Client for retrieving access tokens.

Token

Expiring access token.

RefreshingCredentials

Synchronous client for self-refreshing tokens.

RefreshingToken

Automatically refreshing access token.

AccessToken

Access token base class.

scope

User access token privileges.

Scope

Set of scopes for a token.

request_client_token

Request for client credentials.

prompt_for_user_token

Prompt for manual authorisation.

refresh_user_token

Request a refreshed user token.

prompt_for_pkce_token

Prompt for manual authorisation with PKCE.

refresh_pkce_token

Request a refreshed PKCE user token.

UserAuth

Implement user authorisation flow.

gen_state

Generate state to use in user authorisation.

parse_code_from_url

Parse an URL for parameter 'code'.

parse_state_from_url

Parse an URL for parameter 'state'.

See also: Authorisation guide.

Expiring credentials
class tekore.Credentials(client_id, client_secret=None, redirect_uri=None, sender=None, asynchronous=None)

Bases: Client

Client for retrieving access tokens.

Parameters:
  • client_id (str) – client id

  • client_secret (str) – client secret, not required for PKCE user authorisation

  • redirect_uri (str) – whitelisted redirect URI, required for user authorisation

  • sender (Sender) – request sender

  • asynchronous (bool) – synchronicity requirement

pkce_user_authorisation(scope=None, state=None, verifier_bytes=32)

Construct authorisation URL and verifier.

Step 1/2 in authorisation code flow with proof key for code exchange. The user should be redirected to the resulting URL for authorisation. The verifier is passed to request_pkce_token() in step 2.

Parameters:
  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

  • state (str) – additional state

  • verifier_bytes (int) – number of bytes to generate PKCE verifier with, 32 <= bytes <= 96. The specified range of bytes generates the appropriate number of characters (43 - 128) after base-64 encoding, as required in RFC 7636.

Returns:

authorisation URL and PKCE code verifier

Return type:

Tuple[str, str]

refresh(token)

Refresh an access token.

Both client and user tokens are accepted and refreshed. The correct refreshing method is applied regardless if PKCE was used or not. For client tokens, a new token is returned. For user tokens, a refreshed token is returned.

Parameters:

token (Token) – token to be refreshed

Returns:

refreshed access token

Return type:

Token

refresh_pkce_token(refresh_token)

Request a refreshed PKCE user token.

Parameters:

refresh_token (str) – refresh token

Returns:

refreshed user access token

Return type:

Token

refresh_user_token(refresh_token)

Request a refreshed user token.

Parameters:

refresh_token (str) – refresh token

Returns:

refreshed user access token

Return type:

Token

request_client_token()

Request a client token.

Returns:

client access token

Return type:

Token

request_pkce_token(code, verifier)

Request a new PKCE user token.

Step 2/2 in authorisation code flow with proof key for code exchange. Code is provided as a URL parameter in the redirect URI after login in step 1: pkce_user_authorisation().

Parameters:
  • code (str) – code from redirect parameters

  • verifier (str) – PKCE code verifier generated for authorisation URL

Returns:

user access token

Return type:

Token

request_user_token(code)

Request a new user token.

Step 2/2 in authorisation code flow. Code is provided as a URL parameter in the redirect URI after login in step 1: user_authorisation_url().

Parameters:

code (str) – code from redirect parameters

Returns:

user access token

Return type:

Token

user_authorisation_url(scope=None, state=None, show_dialog=False)

Construct an authorisation URL.

Step 1/2 in authorisation code flow. User should be redirected to the resulting URL for authorisation. Step 2/2: request_user_token().

Parameters:
  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

  • state (str) – additional state

  • show_dialog (bool) – force login dialog even if previously authorised

Returns:

login URL

Return type:

str

class tekore.Token(token_info, uses_pkce)

Bases: AccessToken

Expiring access token.

Represents both client and user tokens. The refresh token of a client token is None.

Parameters:
  • token_info (dict) –

  • uses_pkce (bool) –

property access_token: str

Bearer token value.

property expires_at: int

When the token expires.

property expires_in: int

Seconds until token expiration.

property is_expiring: bool

Determine whether token is about to expire.

property refresh_token: str | None

Refresh token for generating new access tokens.

None if the token is an application token.

property scope: Scope

Privileges granted to the token.

Empty Scope if the token is an application token or a user token without any scopes.

property token_type: str

How the token may be used, always ‘Bearer’.

property uses_pkce: bool

Proof key for code exchange used in authorisation.

class tekore.AccessToken

Bases: ABC

Access token base class.

abstract property access_token: str

Bearer token value.

Used as the string representation of the instance.

Refreshing credentials
class tekore.RefreshingCredentials(client_id, client_secret=None, redirect_uri=None, sender=None)

Synchronous client for self-refreshing tokens.

Delegates to an underlying Credentials manager and parses tokens it returns into RefreshingToken.

Parameters:
  • client_id (str) – client id

  • client_secret (str) – client secret, not required for PKCE user authorisation

  • redirect_uri (str) – whitelisted redirect URI, required for user authorisation

  • sender (Sender) – synchronous request sender

credentials

underlying credentials manager for token refreshing

pkce_user_authorisation(scope=None, state=None, verifier_bytes=32)

Construct authorisation URL and verifier.

Step 1/2 in authorisation code flow with proof key for code exchange. The user should be redirected to the resulting URL for authorisation. The verifier is passed to request_pkce_token() in step 2.

Parameters:
  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

  • state (str) – additional state

  • verifier_bytes (int) – number of bytes to generate PKCE verifier with, 32 <= bytes <= 96. The specified range of bytes generates the appropriate number of characters (43 - 128) after base-64 encoding, as required in RFC 7636.

Returns:

authorisation URL and PKCE code verifier

Return type:

Tuple[str, str]

refresh_pkce_token(refresh_token)

Request a refreshed PKCE user token.

Parameters:

refresh_token (str) – refresh token

Returns:

refreshed user access token

Return type:

RefreshingToken

refresh_user_token(refresh_token)

Request an automatically refreshing user token with a refresh token.

Parameters:

refresh_token (str) – refresh token

Returns:

automatically refreshing user token

Return type:

RefreshingToken

request_client_token()

Request a refreshing client token.

Returns:

automatically refreshing client token

Return type:

RefreshingToken

request_pkce_token(code, verifier)

Request a new PKCE user token.

Step 2/2 in authorisation code flow with proof key for code exchange. Code is provided as a URL parameter in the redirect URI after login in step 1: pkce_user_authorisation().

Parameters:
  • code (str) – code from redirect parameters

  • verifier (str) – PKCE code verifier generated for authorisation URL

Returns:

user access token

Return type:

RefreshingToken

request_user_token(code)

Request a new refreshing user token.

Step 2/2 in authorisation code flow. Code is provided as a URL parameter in the redirect URI after login in step 1: user_authorisation_url().

Parameters:

code (str) – code from redirect parameters

Returns:

automatically refreshing user token

Return type:

RefreshingToken

user_authorisation_url(scope=None, state=None, show_dialog=False)

Construct an authorisation URL.

Step 1/2 in authorisation code flow. User should be redirected to the resulting URL for authorisation. Step 2/2: request_user_token().

Parameters:
  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

  • state (str) – additional state

  • show_dialog (bool) – force login dialog even if previously authorised

Returns:

login URL

Return type:

str

class tekore.RefreshingToken(token, credentials)

Bases: AccessToken

Automatically refreshing access token.

Returned from utility functions and RefreshingCredentials. It shouldn’t have to be instantiated outside of the functions, unless you are sure that you want to.

Uses an instance of Credentials to automatically request a new access token when the old one is about to expire. This occurs when the access_token property is read.

Both expires_in and expires_at are always None, and is_expiring is always False.

Parameters:
  • token (Token) – access token object

  • credentials (Credentials) – credentials manager for token refreshing

credentials

credentials manager for token refreshing

property access_token: str

Bearer token value.

property expires_at: None

When the token expires, always None.

property expires_in: None

Seconds until token expiration, always None.

property is_expiring: bool

Determine whether token is about to expire, always False.

property refresh_token: str | None

Refresh token for generating new access tokens.

None if the token is an application token.

property scope: Scope

Privileges granted to the token.

Empty Scope if the token is an application token or a user token without any scopes.

property token_type: str

How the token may be used, always ‘Bearer’.

property uses_pkce: bool

Proof key for code exchange used in authorisation.

Scopes

Scopes are used in user authorisation to retrieve tokens with additional privileges. scope is an enumeration of every possible such privilege.

import tekore as tk

cred = (client_id, client_secret, redirect_uri)
scope = tk.scope.user_read_email + tk.scope.user_read_private
token = tk.prompt_for_user_token(*cred, scope)

See Spotify’s Authorization scopes guide for scope descriptions. Scopes that are required or optional are listed in each endpoint’s documentation, see Client. They can also be determined programmatically.

class tekore.scope(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

User access token privileges.

The string representation of a member is its enum value.

s = tk.scope.user_read_email
print(s)  # -> 'user-read-email'

Also provides three scopes that are a combination of others.

tk.scope.read: Scope = ...            # All read scopes
tk.scope.write: Scope = ...           # All write scopes
tk.scope.every: Scope = read + write  # All available scopes

Note

app_remote_control and streaming are only used outside of the Web API, and are not included in the premade scope combinations.

Addition and subtraction from both sides is supported but delegated to Scope and always returns a Scope.

__add__(other)

Combine to a set of scopes.

Return type:

Scope

__radd__(other)

Combine to a set of scopes.

Return type:

Scope

__rsub__(other)

Remove scope from another.

Return type:

Scope

__sub__(other)

Remove scope from another.

Return type:

Scope

app_remote_control = 'app-remote-control'
every = frozenset({'playlist-modify-private', 'playlist-modify-public', 'playlist-read-collaborative', 'playlist-read-private', 'ugc-image-upload', 'user-follow-modify', 'user-follow-read', 'user-library-modify', 'user-library-read', 'user-modify-playback-state', 'user-read-currently-playing', 'user-read-email', 'user-read-playback-position', 'user-read-playback-state', 'user-read-private', 'user-read-recently-played', 'user-top-read'})
playlist_modify_private = 'playlist-modify-private'
playlist_modify_public = 'playlist-modify-public'
playlist_read_collaborative = 'playlist-read-collaborative'
playlist_read_private = 'playlist-read-private'
read = frozenset({'playlist-read-collaborative', 'playlist-read-private', 'user-follow-read', 'user-library-read', 'user-read-currently-playing', 'user-read-email', 'user-read-playback-position', 'user-read-playback-state', 'user-read-private', 'user-read-recently-played', 'user-top-read'})
streaming = 'streaming'
ugc_image_upload = 'ugc-image-upload'
user_follow_modify = 'user-follow-modify'
user_follow_read = 'user-follow-read'
user_library_modify = 'user-library-modify'
user_library_read = 'user-library-read'
user_modify_playback_state = 'user-modify-playback-state'
user_read_currently_playing = 'user-read-currently-playing'
user_read_email = 'user-read-email'
user_read_playback_position = 'user-read-playback-position'
user_read_playback_state = 'user-read-playback-state'
user_read_private = 'user-read-private'
user_read_recently_played = 'user-read-recently-played'
user_top_read = 'user-top-read'
write = frozenset({'playlist-modify-private', 'playlist-modify-public', 'ugc-image-upload', 'user-follow-modify', 'user-library-modify', 'user-modify-playback-state'})
class tekore.Scope(*members)

Bases: frozenset

Set of scopes for a token.

Instantiated with an unpacked list of strings or scopes.

bruce = tk.Scope(*tk.scope)
sally = tk.Scope(tk.scope.user_read_email, 'ugc-image-upload')
elise = tk.Scope(*sally, *timmy, tk.scope.user_follow_modify)

Also supports flexible addition and subtraction from both sides with strings, scopes and other Scope objects. Addition is a set-like union, subtraction is a set-like relative complement. If any operation is unsuccessful, NotImplementedError is raised.

waldo = tk.scopes.user_follow_read + sally + elise - 'user-read-email'

The string representation of a Scope is a sorted, space-separated concatenation of its members.

s = tk.Scope('b', 'c', 'a')
print(s)  # -> 'a b c'

Construct a new set of scopes.

Parameters:

members – unpacked list of members of the new scope

__add__(other)

Combine two sets of scopes.

Return type:

Scope

static __new__(cls, *members)

Construct a new set of scopes.

Parameters:

members – unpacked list of members of the new scope

__radd__(other)

Combine two sets of scopes.

Return type:

Scope

__repr__()

Readable representation.

__rsub__(other)

Remove scopes from a set.

Return type:

Scope

__str__()

Join members with spaces.

__sub__(other)

Remove scopes from a set.

Return type:

Scope

Utilities

Authorisation utilities.

Note

These utilities are meant to get users up and running quickly. Consider implementing authorisation procedures that suit your needs specifically. See Authenticating server for more details.

request_client_token

Request for client credentials.

prompt_for_user_token

Prompt for manual authorisation.

refresh_user_token

Request a refreshed user token.

prompt_for_pkce_token

Prompt for manual authorisation with PKCE.

refresh_pkce_token

Request a refreshed PKCE user token.

UserAuth

Implement user authorisation flow.

gen_state

Generate state to use in user authorisation.

parse_code_from_url

Parse an URL for parameter 'code'.

parse_state_from_url

Parse an URL for parameter 'state'.

tekore.request_client_token(client_id, client_secret)

Request for client credentials.

Parameters:
  • client_id (str) – client ID

  • client_secret (str) – client secret

Returns:

automatically refreshing client token

Return type:

RefreshingToken

tekore.prompt_for_user_token(client_id, client_secret, redirect_uri, scope=None)

Prompt for manual authorisation.

Open a web browser for the user to log in with Spotify. Prompt to paste the URL after logging in to complete authorisation.

Parameters:
  • client_id (str) – client ID

  • client_secret (str) – client secret

  • redirect_uri (str) – whitelisted redirect URI

  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

Returns:

automatically refreshing user token

Return type:

RefreshingToken

Raises:

AssertionError – if state is inconsistent

tekore.refresh_user_token(client_id, client_secret, refresh_token)

Request a refreshed user token.

Parameters:
  • client_id (str) – client ID

  • client_secret (str) – client secret

  • refresh_token (str) – refresh token

Returns:

automatically refreshing user token

Return type:

RefreshingToken

tekore.prompt_for_pkce_token(client_id, redirect_uri, scope=None)

Prompt for manual authorisation with PKCE.

Open a web browser for the user to log in with Spotify. Prompt to paste the URL after logging in to complete authorisation.

Parameters:
  • client_id (str) – client ID

  • redirect_uri (str) – whitelisted redirect URI

  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

Returns:

automatically refreshing PKCE user token

Return type:

RefreshingToken

Raises:

AssertionError – if state is inconsistent

tekore.refresh_pkce_token(client_id, refresh_token)

Request a refreshed PKCE user token.

Parameters:
  • client_id (str) – client ID

  • refresh_token (str) – refresh token

Returns:

automatically refreshing user token

Return type:

RefreshingToken

class tekore.UserAuth(cred, scope=None, pkce=False)

Bases: object

Implement user authorisation flow.

Implements all steps and security checks for user authorisation. The responsibility of the caller is to redirect a user to the given URL and provide the resulting redirect URI or its parameters. Can be used with an asynchronous credentials client.

Parameters:
  • cred (Credentials | RefreshingCredentials) – credentials client

  • scope – token privileges, accepts a Scope, a single scope, a list of scopes and strings for Scope, or a space-separated list of scopes as a string

  • pkce (bool) – use proof key for code exchange

url

address to redirect a user to for authorisation

Type:

str

state

generated additional state

Type:

str

verifier

PKCE code verifier, None if PKCE is not used

Type:

str

Examples

auth = tk.UserAuth(cred, scope)

# Redirect user to auth.url and parse parameters
code, state = ...
token = auth.request_token(code, state)

# Or leave parsing to UserAuth
redirected = ...
token = auth.request_token(url=redirected)

# With an asynchronous client
token = await auth.request_token(url=redirected)
request_token(code=None, state=None, url=None)

Verify state consistency and request token.

Parameters:
  • code (str) – code from redirect parameters, required if url was not specified

  • state (str) – state from redirect parameters, required if url was not specified

  • url (str) – if specified, code and state are parsed from this URL instead

Returns:

access token

Return type:

Union[Token, RefreshingToken]

Raises:

AssertionError – if state is inconsistent

tekore.gen_state(n_bytes=32)

Generate state to use in user authorisation.

The generated state is random and URL-safe. It is generated using secrets.token_urlsafe().

Parameters:

n_bytes (int) –

Return type:

str

tekore.parse_code_from_url(url)

Parse an URL for parameter ‘code’.

Returns:

value of ‘code’

Return type:

str

Raises:

KeyError – if ‘code’ is not available or has multiple values

Parameters:

url (str) –

tekore.parse_state_from_url(url)

Parse an URL for parameter ‘state’.

Returns:

value of ‘state’

Return type:

str

Raises:

KeyError – if ‘state’ is not available or has multiple values

Parameters:

url (str) –

Client

Web API endpoints.

API summary

Album API

Album information

Artist API

Artist information

Audiobook API

Audiobook information

Browse API

Spotify featured catalogue

Chapter API

Chapter information

Episode API

Episode information

Follow API

Follow artists, playlists and users

Library API

Save (like) albums, shows and tracks

Markets API

Spotify market information

Personalisation API

User top listens

Player API

Playback operations

Playlist API

Playlist operations

Search API

Search functionality

Show API

Show information

Track API

Track information and analysis

User API

User information

Each method of the client corresponds to an API call, with some exceptions. Further documentation on endpoints can be viewed in the Web API reference.

import tekore as tk

# Initialise the client
spotify = tk.Spotify(token)

# Call the API
album = spotify.album('3RBULTZJ97bvVzZLpxcB0j')
for track in album.tracks.items:
    print(track.track_number, track.name)

Required and optional scopes to call any endpoint can be determined in code. Endpoints provide required_scope and optional_scope attributes which return a Scope. A combination of the two is provided in scope. They can be accessed via the class itself or its instances.

scope_cls = tk.Spotify.current_user_top_tracks.scope
scope_inst = tk.Spotify().current_user_top_tracks.scope
assert scope_cls == scope_inst
class tekore.Spotify(token=None, sender=None, asynchronous=None, max_limits_on=False, chunked_on=False)

Bases: tekore.Client.

Client to Web API endpoints.

Parameters:
  • token – bearer token for requests

  • sender (Sender) – request sender

  • asynchronous (bool) – synchronicity requirement

  • max_limits_on (bool) – use maximum limits in paging calls, overrided by endpoint arguments

  • chunked_on (bool) – use chunking when requesting lists of resources

token

bearer token for requests

sender

underlying sender

max_limits_on

use maximum limits in paging calls, overrided by endpoint arguments

chunked_on

use chunking when requesting lists of resources

Non-endpoint methods

Spotify.chunked

Toggle chunking lists of resources.

Spotify.max_limits

Toggle using maximum limits in paging calls.

Spotify.token_as

Use a different token with requests.

Spotify.follow_short_link

Follow redirect of a short link to get the underlying resource URL.

is_short_link

Determine if URL is a Spotify short link.

Spotify.send

Build request url and headers, and send with underlying sender.

Spotify.close

Close the underlying sender.

Spotify.chunked(on=True)

Toggle chunking lists of resources. Context manager, async safe.

Parameters:

on (bool) – enable or disable chunking

Returns:

self as context

Return type:

Generator[Spotify, None, None]

Examples

spotify = Spotify(token)
with spotify.chunked(True):
    tracks = spotify.tracks(many_ids)

spotify = Spotify(token, chunked_on=True)
with spotify.chunked(False):
    tracks = spotify.search(many_ids[:50])
Spotify.max_limits(on=True)

Toggle using maximum limits in paging calls. Context manager, async safe.

Parameters:

on (bool) – enable or disable using maximum limits

Returns:

self as context

Return type:

Generator[Spotify, None, None]

Examples

spotify = Spotify(token)
with spotify.max_limits(True):
    tracks, = spotify.search('piano')

spotify = Spotify(token, max_limits_on=True)
with spotify.max_limits(False):
    tracks, = spotify.search('piano')
Spotify.token_as(token)

Use a different token with requests. Context manager, async safe.

Parameters:

token – access token

Returns:

self as context

Return type:

Generator[Spotify, None, None]

Examples

spotify = Spotify()
with spotify.token_as(token):
    album = spotify.album(album_id)

spotify = Spotify(app_token)
with spotify.token_as(user_token):
    user = spotify.current_user()
Spotify.follow_short_link(link)

Follow redirect of a short link to get the underlying resource URL.

Safely also accept a direct link, request a redirect and return the original URL. Also use the underlying sender for an unauthenticated request.

Returns:

result of the short link redirect

Return type:

url

Parameters:

link (str) –

tekore.is_short_link(url)

Determine if URL is a Spotify short link.

Parameters:

url (str) –

Return type:

bool

Spotify.send(request)

Build request url and headers, and send with underlying sender.

Exposed to easily send arbitrary requests, for custom behavior in some endpoint e.g. for a subclass. It may also come in handy if a bugfix or a feature is not implemented in a timely manner, or in debugging related to the client or Web API.

Parameters:

request (Request) –

Return type:

Response | Coroutine[None, None, Response]

Spotify.close()

Close the underlying sender.

To close synchronous senders, call close(). To close asynchronous senders, await close().

Return type:

None | Coroutine[None, None, None]

Paging navigation

Spotify.next

Retrieve the next result set of a paging object.

Spotify.previous

Retrieve the previous result set of a paging object.

Spotify.all_items

Retrieve all items from all pages of a paging.

Spotify.all_pages

Retrieve all pages of a paging.

Spotify.next(page)

Retrieve the next result set of a paging object.

Parameters:

page (Paging) – paging object

Returns:

next result set

Return type:

Paging

Spotify.previous(page)

Retrieve the previous result set of a paging object.

Parameters:

page (OffsetPaging) – offset-based paging object

Returns:

previous result set

Return type:

OffsetPaging

Spotify.all_items(page)

Retrieve all items from all pages of a paging.

Request and yield new (next) items until the end of the paging. The items in the paging that was given as an argument are yielded first.

Parameters:

page (Paging) – paging object

Returns:

all items within a paging

Return type:

Generator

Spotify.all_pages(page)

Retrieve all pages of a paging.

Request and yield new (next) pages until the end of the paging. The paging that was given as an argument is yielded as the first result.

Parameters:

page (Paging) – paging object

Returns:

all pages within a paging

Return type:

Generator

Album API

Spotify.album

Get an album.

Spotify.album_tracks

Get tracks on album.

Spotify.albums

Get multiple albums.

Spotify.album(album_id, market=None)

Get an album.

Required scope: none
Optional scope: none
Parameters:
  • album_id (str) – album ID

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

Return type:

FullAlbum

Spotify.album_tracks(album_id, market=None, limit=20, offset=0)

Get tracks on album.

Required scope: none
Optional scope: none
Parameters:
  • album_id (str) – album ID

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimpleTrackPaging

Spotify.albums(album_ids, market=None)

Get multiple albums.

Required scope: none
Optional scope: none
Parameters:
  • album_ids (list) – list of album IDs, max 20 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

Return type:

List[FullAlbum]

Artist API

Spotify.artist

Get information for an artist.

Spotify.artist_albums

Get an artist's albums.

Spotify.artist_related_artists

Get artists similar to an identified artist.

Spotify.artist_top_tracks

Get an artist's top 10 tracks by country.

Spotify.artists

Get information for multiple artists.

Spotify.artist(artist_id)

Get information for an artist.

Required scope: none
Optional scope: none
Parameters:

artist_id (str) – artist ID

Return type:

FullArtist

Spotify.artist_albums(artist_id, include_groups=None, market=None, limit=20, offset=0)

Get an artist’s albums.

Required scope: none
Optional scope: none
Parameters:
  • artist_id (str) – the artist ID

  • include_groups (List[str | AlbumGroup]) – album groups to include in the response

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimpleAlbumPaging

Spotify.artist_related_artists(artist_id)

Get artists similar to an identified artist.

Required scope: none
Optional scope: none

Similarity is based on analysis of the Spotify community’s listening history.

Parameters:

artist_id (str) – artist ID

Return type:

List[FullArtist]

Spotify.artist_top_tracks(artist_id, market)

Get an artist’s top 10 tracks by country.

Required scope: none
Optional scope: none
Parameters:
  • artist_id (str) – the artist ID

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

Return type:

List[FullTrack]

Spotify.artists(artist_ids)

Get information for multiple artists.

Required scope: none
Optional scope: none
Parameters:

artist_ids (list) – list of artist IDs, max 50 without chunking

Return type:

List[FullArtist]

Audiobook API

Spotify.audiobook

Get information for an audiobook.

Spotify.audiobook_chapters

Get chapters of an audiobook.

Spotify.audiobooks

Get information for multiple audiobooks.

Spotify.audiobook(audiobook_id, market=None)

Get information for an audiobook.

Required scope: none
Optional scope: none
Parameters:
  • audiobook_id (str) – audiobook ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

Return type:

FullAudiobook

Spotify.audiobook_chapters(audiobook_id, market=None, limit=20, offset=0)

Get chapters of an audiobook.

Required scope: none
Optional scope: none
Parameters:
  • audiobook_id (str) – audiobook ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimpleChapterPaging

Spotify.audiobooks(audiobook_ids, market=None)

Get information for multiple audiobooks.

Required scope: none
Optional scope: none
Parameters:
  • audiobook_ids (list) – the audiobook IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

Return type:

List[FullAudiobook]

Browse API

Spotify.categories

Get a list of categories used to tag items in Spotify.

Spotify.category

Get a single category used to tag items in Spotify.

Spotify.category_playlists

Get a list of Spotify playlists tagged with a particular category.

Spotify.featured_playlists

Get a list of Spotify featured playlists.

Spotify.new_releases

Get a list of new album releases featured in Spotify.

Spotify.recommendation_genre_seeds

Get a list of available genre seeds.

Spotify.recommendations

Get a list of recommended tracks for seeds.

Spotify.categories(country=None, locale=None, limit=20, offset=0)

Get a list of categories used to tag items in Spotify.

Required scope: none
Optional scope: none
Parameters:
  • country (str) – an ISO 3166-1 alpha-2 country code

  • locale (str) – the desired language, consisting of a lowercase ISO 639 language code and an uppercase ISO 3166-1 alpha-2 country code joined by an underscore

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

CategoryPaging

Spotify.category(category_id, country=None, locale=None)

Get a single category used to tag items in Spotify.

Required scope: none
Optional scope: none
Parameters:
  • category_id (str) – category ID

  • country (str) – an ISO 3166-1 alpha-2 country code

  • locale (str) – the desired language, consisting of a lowercase ISO 639 language code and an uppercase ISO 3166-1 alpha-2 country code joined by an underscore

Return type:

Category

Spotify.category_playlists(category_id, country=None, limit=20, offset=0)

Get a list of Spotify playlists tagged with a particular category.

Required scope: none
Optional scope: none
Parameters:
  • category_id (str) – category ID

  • country (str) – an ISO 3166-1 alpha-2 country code

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimplePlaylistPaging

Spotify.featured_playlists(country=None, locale=None, timestamp=None, limit=20, offset=0)

Get a list of Spotify featured playlists.

Required scope: none
Optional scope: none
Parameters:
  • country (str) – an ISO 3166-1 alpha-2 country code

  • locale (str) – the desired language, consisting of a lowercase ISO 639 language code and an uppercase ISO 3166-1 alpha-2 country code joined by an underscore

  • timestamp (str) – Timestamp in ISO 8601 format: yyyy-MM-ddTHH:mm:ss. Used to specify the user’s local time to get results tailored for that specific date and time in the day.

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Returns:

message and playlists

Return type:

Tuple[str, SimplePlaylistPaging]

Spotify.new_releases(country=None, limit=20, offset=0)

Get a list of new album releases featured in Spotify.

Required scope: none
Optional scope: none
Parameters:
  • country (str) – an ISO 3166-1 alpha-2 country code

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimpleAlbumPaging

Spotify.recommendation_genre_seeds()

Get a list of available genre seeds.

Required scope: none
Optional scope: none
Return type:

List[str]

Spotify.recommendations(artist_ids=None, genres=None, track_ids=None, limit=20, market=None, **attributes)

Get a list of recommended tracks for seeds.

Required scope: none
Optional scope: none

Warning

The total number of seeds provided in artist_ids, genres and track_ids must be at least 1 and at most 5.

Parameters:
  • artist_ids (list) – list of seed artist IDs

  • genres (list) – list of seed genre names

  • track_ids (list) – list of seed track IDs

  • limit (int) – the number of items to return (1..100)

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • attributes – min/max/target_<attribute> - For all tuneable track attributes see model.RecommendationAttribute, these values provide filters and targeting on results.

Raises:

ValueError – if any attribute is not allowed

Return type:

Recommendations

Chapter API

Spotify.chapter

Get information for a chapter.

Spotify.chapters

Get information for multiple chapters.

Spotify.chapter(chapter_id, market=None)

Get information for a chapter.

Required scope: none
Optional scope: none
Parameters:
  • chapter_id (str) – chapter ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the episode is considered unavailable.

Return type:

FullChapter

Spotify.chapters(chapter_ids, market=None)

Get information for multiple chapters.

Required scope: none
Optional scope: none
Parameters:
  • chapter_ids (list) – the chapter IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the episode is considered unavailable.

Return type:

List[FullChapter]

Episode API

Spotify.episode

Get information for an episode.

Spotify.episodes

Get information for multiple episodes.

Spotify.episode(episode_id, market=None)

Get information for an episode.

Required scope: none
Optional scope: none
Parameters:
  • episode_id (str) – episode ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the episode is considered unavailable.

Return type:

FullEpisode

Spotify.episodes(episode_ids, market=None)

Get information for multiple episodes.

Required scope: none
Optional scope: none
Parameters:
  • episode_ids (list) – the episode IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the episode is considered unavailable.

Return type:

List[FullEpisode]

Follow API

Spotify.artists_follow

Follow artists as current user.

Spotify.artists_is_following

Check if current user follows artists.

Spotify.artists_unfollow

Unfollow artists as current user.

Spotify.followed_artists

Get artists followed by the current user.

Spotify.playlist_follow

Follow a playlist as current user.

Spotify.playlist_is_following

Check if users are following a playlist.

Spotify.playlist_unfollow

Unfollow a playlist as current user.

Spotify.followed_playlists

Get a list of the playlists owned or followed by the current user.

Spotify.users_follow

Follow users as current user.

Spotify.users_is_following

Check if current user follows users.

Spotify.users_unfollow

Unfollow users as current user.

Spotify.artists_follow(artist_ids)

Follow artists as current user.

Required scope: user-follow-modify
Optional scope: none
Parameters:

artist_ids (list) – list of artist IDs, max 50 without chunking

Return type:

None

Spotify.artists_is_following(artist_ids)

Check if current user follows artists.

Required scope: user-follow-read
Optional scope: none
Parameters:

artist_ids (list) – list of artist IDs, max 50 without chunking

Returns:

follow statuses in the same order that the artist IDs were given

Return type:

List[bool]

Spotify.artists_unfollow(artist_ids)

Unfollow artists as current user.

Required scope: user-follow-modify
Optional scope: none
Parameters:

artist_ids (list) – list of artist IDs, max 50 without chunking

Return type:

None

Spotify.followed_artists(limit=20, after=None)

Get artists followed by the current user.

Required scope: user-follow-read
Optional scope: none
Parameters:
  • limit (int) – the number of items to return (1..50)

  • after (str) – the last artist ID retrieved from the previous request

Return type:

FullArtistCursorPaging

Spotify.playlist_follow(playlist_id, public=True)

Follow a playlist as current user.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • public (bool) – follow publicly

Return type:

None

Spotify.playlist_is_following(playlist_id, user_ids)

Check if users are following a playlist.

Required scope: none
Optional scope: playlist-read-private
Parameters:
  • playlist_id (str) – playlist ID

  • user_ids (list) – list of user IDs, max 5 without chunking

Returns:

follow statuses in the same order that the user IDs were given

Return type:

List[bool]

Spotify.playlist_unfollow(playlist_id)

Unfollow a playlist as current user.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:

playlist_id (str) – playlist ID

Return type:

None

Spotify.followed_playlists(limit=20, offset=0)

Get a list of the playlists owned or followed by the current user.

Required scope: none
Optional scope: playlist-read-collaborative playlist-read-private
Parameters:
  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimplePlaylistPaging

Spotify.users_follow(user_ids)

Follow users as current user.

Required scope: user-follow-modify
Optional scope: none
Parameters:

user_ids (list) – list of user IDs, max 50 without chunking

Return type:

None

Spotify.users_is_following(user_ids)

Check if current user follows users.

Required scope: user-follow-read
Optional scope: none
Parameters:

user_ids (list) – list of user IDs, max 50 without chunking

Returns:

follow statuses in the same order that the user IDs were given

Return type:

List[bool]

Spotify.users_unfollow(user_ids)

Unfollow users as current user.

Required scope: user-follow-modify
Optional scope: none
Parameters:

user_ids (list) – list of user IDs, max 50 without chunking

Return type:

None

Library API

Spotify.saved_albums

Get the albums saved in the current user's library.

Spotify.saved_albums_add

Save albums for current user.

Spotify.saved_albums_contains

Check if user has saved albums.

Spotify.saved_albums_delete

Remove albums for current user.

Spotify.saved_episodes

Get the episodes saved in the current user's library.

Spotify.saved_episodes_add

Save episodes for current user.

Spotify.saved_episodes_contains

Check if user has saved episodes.

Spotify.saved_episodes_delete

Remove episodes for current user.

Spotify.saved_shows

Get the shows saved in the current user's library.

Spotify.saved_shows_add

Save shows for current user.

Spotify.saved_shows_contains

Check if user has saved shows.

Spotify.saved_shows_delete

Remove shows for current user.

Spotify.saved_tracks

Get the songs saved in the current user's library.

Spotify.saved_tracks_add

Save tracks for current user.

Spotify.saved_tracks_contains

Check if user has saved tracks.

Spotify.saved_tracks_delete

Remove tracks for current user.

Spotify.saved_albums(market=None, limit=20, offset=0)

Get the albums saved in the current user’s library.

Required scope: user-library-read
Optional scope: none
Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SavedAlbumPaging

Spotify.saved_albums_add(album_ids)

Save albums for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

album_ids (list) – list of album IDs, max 50 without chunking

Return type:

None

Spotify.saved_albums_contains(album_ids)

Check if user has saved albums.

Required scope: user-library-read
Optional scope: none
Parameters:

album_ids (list) – list of album IDs, max 50 without chunking

Returns:

save statuses in the same order the album IDs were given

Return type:

List[bool]

Spotify.saved_albums_delete(album_ids)

Remove albums for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

album_ids (list) – list of album IDs, max 50 without chunking

Return type:

None

Spotify.saved_episodes(market=None, limit=20, offset=0)

Get the episodes saved in the current user’s library.

Required scope: user-library-read
Optional scope: none
Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SavedEpisodePaging

Spotify.saved_episodes_add(episode_ids)

Save episodes for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

episode_ids (list) – list of episode IDs, max 50 without chunking

Return type:

None

Spotify.saved_episodes_contains(episode_ids)

Check if user has saved episodes.

Required scope: user-library-read
Optional scope: none
Parameters:

episode_ids (list) – list of episode IDs, max 50 without chunking

Returns:

save statuses in the same order the episode IDs were given

Return type:

List[bool]

Spotify.saved_episodes_delete(episode_ids)

Remove episodes for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

episode_ids (list) – list of episode IDs, max 50 without chunking

Return type:

None

Spotify.saved_shows(market=None, limit=20, offset=0)

Get the shows saved in the current user’s library.

Required scope: user-library-read
Optional scope: none
Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SavedShowPaging

Spotify.saved_shows_add(show_ids)

Save shows for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

show_ids (list) – list of show IDs, max 50 without chunking

Return type:

None

Spotify.saved_shows_contains(show_ids)

Check if user has saved shows.

Required scope: user-library-read
Optional scope: none
Parameters:

show_ids (list) – list of show IDs, max 50 without chunking

Returns:

save statuses in the same order the show IDs were given

Return type:

List[bool]

Spotify.saved_shows_delete(show_ids, market=None)

Remove shows for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:
  • show_ids (list) – list of show IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code, only remove shows that are available in the specified market, overrided by token’s country

Return type:

None

Spotify.saved_tracks(market=None, limit=20, offset=0)

Get the songs saved in the current user’s library.

Required scope: user-library-read
Optional scope: none
Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SavedTrackPaging

Spotify.saved_tracks_add(track_ids)

Save tracks for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

track_ids (list) – list of track IDs, max 50 without chunking

Return type:

None

Spotify.saved_tracks_contains(track_ids)

Check if user has saved tracks.

Required scope: user-library-read
Optional scope: none
Parameters:

track_ids (list) – list of track IDs, max 50 without chunking

Returns:

save statuses in the same order the track IDs were given

Return type:

List[bool]

Spotify.saved_tracks_delete(track_ids)

Remove tracks for current user.

Required scope: user-library-modify
Optional scope: none
Parameters:

track_ids (list) – list of track IDs, max 50 without chunking

Return type:

None

Markets API

Spotify.markets

Get available market country codes.

Spotify.markets()

Get available market country codes.

Required scope: none
Optional scope: none
Returns:

available markets

Return type:

List[str]

Personalisation API

Spotify.current_user_top_artists

Get the current user's top artists.

Spotify.current_user_top_tracks

Get the current user's top tracks.

Spotify.current_user_top_artists(time_range='medium_term', limit=20, offset=0)

Get the current user’s top artists.

Required scope: user-top-read
Optional scope: none
Parameters:
  • time_range (str) – Over what time frame are the affinities computed. Valid-values: short_term, medium_term, long_term

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

FullArtistOffsetPaging

Spotify.current_user_top_tracks(time_range='medium_term', limit=20, offset=0)

Get the current user’s top tracks.

Required scope: user-top-read
Optional scope: none
Parameters:
  • time_range (str) – Over what time frame are the affinities computed. Valid-values: short_term, medium_term, long_term

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

FullTrackPaging

Player API

Spotify.playback

Get information about user's current playback.

Spotify.playback_currently_playing

Get user's currently playing track.

Spotify.playback_devices

Get a user's available devices.

Spotify.playback_next

Skip user's playback to next track.

Spotify.playback_pause

Pause a user's playback.

Spotify.playback_previous

Skip user's playback to previous track.

Spotify.playback_queue

Get items in a user's queue.

Spotify.playback_queue_add

Add a track or an episode to a user's queue.

Spotify.playback_recently_played

Get tracks from the current user's recently played tracks.

Spotify.playback_repeat

Set repeat mode for playback.

Spotify.playback_resume

Resume user's playback.

Spotify.playback_seek

Seek to position in current playing track.

Spotify.playback_shuffle

Toggle shuffle for user's playback.

Spotify.playback_start_context

Start playback of a context: an album, artist or playlist.

Spotify.playback_start_tracks

Start playback of one or more tracks.

Spotify.playback_transfer

Transfer playback to another device.

Spotify.playback_volume

Set volume for user's playback.

Spotify.playback(market=None, tracks_only=False)

Get information about user’s current playback.

Required scope: user-read-playback-state
Optional scope: none
Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • tracks_only (bool) – return only tracks in the currently playing item, if True, episodes have None as the currently playing item

Returns:

information about current playback

Return type:

CurrentlyPlayingContext

Spotify.playback_currently_playing(market=None, tracks_only=False)

Get user’s currently playing track.

Required scope: user-read-currently-playing user-read-playback-state
Optional scope: user-read-currently-playing user-read-playback-state

Only one of the scopes above is required.

Parameters:
  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • tracks_only (bool) – return only tracks in the currently playing item, if True, episodes have None as the currently playing item

Returns:

information about the current track playing

Return type:

CurrentlyPlaying

Spotify.playback_devices()

Get a user’s available devices.

Required scope: user-read-playback-state
Optional scope: none
Return type:

List[Device]

Spotify.playback_next(device_id=None)

Skip user’s playback to next track.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:

device_id (str) – device to skip track on

Return type:

None

Spotify.playback_pause(device_id=None)

Pause a user’s playback.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:

device_id (str) – device to pause playback on

Return type:

None

Spotify.playback_previous(device_id=None)

Skip user’s playback to previous track.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:

device_id (str) – device to skip track on

Return type:

None

Spotify.playback_queue()

Get items in a user’s queue.

Required scope: user-read-playback-state
Optional scope: none

The result also include items in the current playback context even though they are not explicitly in the queue. The number of items in the queue is inconsistent, but from manual experimentation at least two items are returned.

Return type:

Queue

Spotify.playback_queue_add(uri, device_id=None)

Add a track or an episode to a user’s queue.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • uri (str) – resource to add, track or episode

  • device_id (str) – devide to extend the queue on

Return type:

None

Spotify.playback_recently_played(limit=20, after=None, before=None)

Get tracks from the current user’s recently played tracks.

Required scope: user-read-recently-played
Optional scope: none

Only after or before should be specified at one time.

Parameters:
  • limit (int) – the number of items to return (1..50)

  • after (int) – a unix timestamp in milliseconds, must not be specified with ‘before’

  • before (int) – a unix timestamp in milliseconds, must not be specified with ‘after’

Return type:

PlayHistoryPaging

Spotify.playback_repeat(state, device_id=None)

Set repeat mode for playback.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • state (str | RepeatState) – track, context, or off

  • device_id (str) – device to set repeat on

Return type:

None

Spotify.playback_resume(device_id=None)

Resume user’s playback.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:

device_id (str) – device to start playback on

Return type:

None

Spotify.playback_seek(position_ms, device_id=None)

Seek to position in current playing track.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • position_ms (int) – position on track

  • device_id (str) – device to seek on

Return type:

None

Spotify.playback_shuffle(state, device_id=None)

Toggle shuffle for user’s playback.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • state (bool) – shuffle state

  • device_id (str) – device to toggle shuffle on

Return type:

None

Spotify.playback_start_context(context_uri, offset=None, position_ms=None, device_id=None)

Start playback of a context: an album, artist or playlist.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • context_uri (str) – context to start playing

  • offset (int | str) – offset into context by index or track ID, only available when context is an album or playlist

  • position_ms (int) – initial position of first played track

  • device_id (str) – device to start playback on

Return type:

None

Spotify.playback_start_tracks(track_ids, offset=None, position_ms=None, device_id=None)

Start playback of one or more tracks.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • track_ids (list) – track IDs to start playing

  • offset (int | str) – offset into tracks by index or track ID

  • position_ms (int) – initial position of first played track

  • device_id (str) – device to start playback on

Return type:

None

Spotify.playback_transfer(device_id, force_play=False)

Transfer playback to another device.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • device_id (str) – device to transfer playback to

  • force_play (bool) – true: play after transfer, false: keep current state

Return type:

None

Spotify.playback_volume(volume_percent, device_id=None)

Set volume for user’s playback.

Required scope: user-modify-playback-state
Optional scope: none
Parameters:
  • volume_percent (int) – volume to set (0..100)

  • device_id (str) – device to set volume on

Return type:

None

Playlist API

Spotify.playlist

Get playlist of a user.

Spotify.playlists

Get a list of the playlists owned or followed by a user.

Spotify.playlist_create

Create a playlist.

Spotify.playlist_change_details

Change a playlist's details.

Spotify.playlist_cover_image

Get cover image of a playlist.

Spotify.playlist_cover_image_upload

Upload a custom playlist cover image.

Spotify.playlist_items

Full details of items on a playlist.

Spotify.playlist_add

Add items.

Spotify.playlist_clear

Remove all items.

Spotify.playlist_remove

Remove items by URI.

Spotify.playlist_reorder

Reorder items.

Spotify.playlist_replace

Replace all items.

See Spotify’s guide on working with playlists for additional information.

Spotify.playlist(playlist_id, fields=None, market=None, as_tracks=False)

Get playlist of a user.

Required scope: none
Optional scope: none

Note

Returns a dictionary if fields or as_tracks is specified.

Parameters:
  • playlist_id (str) – playlist ID

  • fields (str) – which fields to return, see the Web API documentation for details

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’ when using a user token to authenticate. For episodes in the playlist, if a user token is used, the country associated with it overrides this parameter. If an application token is used and no market is specified, episodes are considered unavailable and returned as None.

  • as_tracks (bool | Iterable[str]) – return types of items with track-like fields. If True, return all other types as tracks. If an iterable is passed, types contained are returned as tracks. Currently the only extra type is episode.

Returns:

playlist object, or raw dictionary if fields or as_tracks was specified

Return type:

Union[FullPlaylist, dict]

Spotify.playlists(user_id, limit=20, offset=0)

Get a list of the playlists owned or followed by a user.

Required scope: none
Optional scope: playlist-read-collaborative playlist-read-private

Collaborative playlists are only returned for the current user.

Parameters:
  • user_id (str) – user ID

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimplePlaylistPaging

Spotify.playlist_create(user_id, name, public=True, description='')

Create a playlist.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • user_id (str) – user ID

  • name (str) – the name of the playlist

  • public (bool) – is the created playlist public

  • description (str) – the description of the playlist

Return type:

FullPlaylist

Spotify.playlist_change_details(playlist_id, name=None, public=None, collaborative=None, description=None)

Change a playlist’s details.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • name (str) – name of the playlist

  • public (bool) – is the playlist public

  • collaborative (bool) – is the playlist collaborative

  • description (str) – description of the playlist

Return type:

None

Spotify.playlist_cover_image(playlist_id)

Get cover image of a playlist.

Required scope: none
Optional scope: none

Note

Returns a list of images.

Parameters:

playlist_id (str) – playlist ID

Return type:

List[Image]

Spotify.playlist_cover_image_upload(playlist_id, image)

Upload a custom playlist cover image.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • image (str) – image data as a base64-encoded string

Return type:

None

Spotify.playlist_items(playlist_id, fields=None, market=None, as_tracks=False, limit=100, offset=0)

Full details of items on a playlist.

Required scope: none
Optional scope: none

Note

Returns a dictionary if fields or as_tracks is specified.

Parameters:
  • playlist_id (str) – playlist ID

  • fields (str) – which fields to return, see the Web API documentation for details

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’ when using a user token to authenticate. For episodes in the playlist, if a user token is used, the country associated with it overrides this parameter. If an application token is used and no market is specified, episodes are considered unavailable and returned as None.

  • as_tracks (bool | Iterable[str]) – return types of items with track-like fields. If True, return all other types as tracks. If an iterable is passed, types contained are returned as tracks. Currently the only extra type is episode.

  • limit (int) – the number of items to return (1..100)

  • offset (int) – the index of the first item to return

Returns:

paging object containing playlist items, or raw dictionary if fields or as_tracks was specified

Return type:

Union[PlaylistTrackPaging, dict]

Spotify.playlist_add(playlist_id, uris, position=None)

Add items.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • uris (list) – list of URIs, max 100 without chunking

  • position (int) – index to insert the items in

Returns:

snapshot ID for the playlist

Return type:

str

Spotify.playlist_clear(playlist_id)

Remove all items.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:

playlist_id (str) – playlist ID

Return type:

None

Spotify.playlist_remove(playlist_id, uris, snapshot_id=None)

Remove items by URI.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private

Removes all occurrences of the specified items. Note that when chunked, snapshot_id is not updated between requests.

Parameters:
  • playlist_id (str) – playlist ID

  • uris (list) – list of URIs, max 100 without chunking

  • snapshot_id (str) – snapshot ID for the playlist

Returns:

snapshot ID for the playlist

Return type:

str

Spotify.playlist_reorder(playlist_id, range_start, insert_before, range_length=1, snapshot_id=None)

Reorder items.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • range_start (int) – position of the first item to be reordered

  • range_length (int) – number of items to be reordered

  • insert_before (int) – position where the items should be inserted

  • snapshot_id (str) – snapshot ID for the playlist

Returns:

snapshot ID for the playlist

Return type:

str

Spotify.playlist_replace(playlist_id, uris)

Replace all items.

Required scope: playlist-modify-public
Optional scope: playlist-modify-private
Parameters:
  • playlist_id (str) – playlist ID

  • uris (list) – list of URIs, max 100

Return type:

None

Search API
Spotify.search(query, types=('track',), market=None, include_external=None, limit=20, offset=0)

Search for an item.

Required scope: none
Optional scope: none

Returns NotFound if limit+offset would be above 1000.

Parameters:
  • query (str) – search query

  • types (tuple) – resources to search for, tuple of strings containing artist, album, audiobook, track, playlist, show and/or episode

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

  • include_external (str) – if ‘audio’, response will include any externally hosted audio

Returns:

Paging objects containing the types of items searched for in the order that they were specified in ‘types’.

  • artist: FullArtistOffsetPaging

  • album: SimpleAlbumPaging

  • audiobook: SimpleAudiobookPaging

  • episode: SimpleEpisodePaging

  • playlist: SimplePlaylistPaging

  • show: SimpleShowPaging

  • track: FullTrackPaging

Return type:

tuple

Examples

tracks, = spotify.search('monty python')
artists, = spotify.search('sheeran', types=('artist',))
albums, tracks = spotify.search('piano', types=('album', 'track'))
spotify.search('gold album:boba artist:abba', types=('track',))
spotify.search('bob year:1980-2020', types=('show',))

Note

You can narrow down search results by specifying field filters (e.g. year range, genre). See the Search for an Item page of the official documentation for more information.

Show API

Spotify.show

Get information for a show.

Spotify.show_episodes

Get episodes of a show.

Spotify.shows

Get information for multiple shows.

Spotify.show(show_id, market=None)

Get information for a show.

Required scope: none
Optional scope: user-read-playback-position

The user-read-playback-position scope allows episode resume points to be returned.

Parameters:
  • show_id (str) – show ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

Return type:

FullShow

Spotify.show_episodes(show_id, market=None, limit=20, offset=0)

Get episodes of a show.

Required scope: none
Optional scope: none
Parameters:
  • show_id (str) – show ID

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

  • limit (int) – the number of items to return (1..50)

  • offset (int) – the index of the first item to return

Return type:

SimpleEpisodePaging

Spotify.shows(show_ids, market=None)

Get information for multiple shows.

Required scope: none
Optional scope: user-read-playback-position

The user-read-playback-position scope allows episode resume points to be returned.

Parameters:
  • show_ids (list) – the show IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code. If a user token is used to authenticate, the country associated with it overrides this parameter. If an application token is used and no market is specified, the show is considered unavailable.

Return type:

List[FullShow]

Track API

Spotify.track

Get information for a track.

Spotify.track_audio_analysis

Get a detailed audio analysis for a track.

Spotify.track_audio_features

Get audio feature information for a track.

Spotify.tracks

Get information for multiple tracks.

Spotify.tracks_audio_features

Get audio feature information for multiple tracks.

Spotify.track(track_id, market=None)

Get information for a track.

Required scope: none
Optional scope: none
Parameters:
  • track_id (str) – track ID

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

Return type:

FullTrack

Spotify.track_audio_analysis(track_id)

Get a detailed audio analysis for a track.

Required scope: none
Optional scope: none

The analysis describes the track’s structure and musical content, including rythm, pitch and timbre.

Parameters:

track_id (str) –

Return type:

AudioAnalysis

Spotify.track_audio_features(track_id)

Get audio feature information for a track.

Required scope: none
Optional scope: none
Parameters:

track_id (str) –

Return type:

AudioFeatures

Spotify.tracks(track_ids, market=None)

Get information for multiple tracks.

Required scope: none
Optional scope: none
Parameters:
  • track_ids (list) – the track IDs, max 50 without chunking

  • market (str) – an ISO 3166-1 alpha-2 country code or ‘from_token’

Return type:

List[FullTrack]

Spotify.tracks_audio_features(track_ids)

Get audio feature information for multiple tracks.

Required scope: none
Optional scope: none

Feature information for a track may be None if not available.

Parameters:

track_ids (list) – track IDs, max 100 without chunking

Return type:

List[AudioFeatures]

User API

Spotify.current_user

Get current user's profile.

Spotify.user

Get a user's profile.

Spotify.current_user()

Get current user’s profile.

Required scope: none
Optional scope: user-read-email user-read-private

The user-read-private scope allows the user’s country and product subscription level to be returned.

Return type:

PrivateUser

Spotify.user(user_id)

Get a user’s profile.

Required scope: none
Optional scope: none
Parameters:

user_id (str) – user ID

Return type:

PublicUser

Configuration

Importing and exporting application credentials.

config_from_environment

Read configuration from environment variables.

config_from_file

Read configuration from a config file.

config_to_file

Write configuration to a config file.

MissingConfigurationWarning

Missing value read from configuration.

client_id_var

Configuration variable name for a client ID.

client_secret_var

Configuration variable name for a client secret.

redirect_uri_var

Configuration variable name for a redirect URI.

user_refresh_var

Configuration variable name for a user refresh token.

Environment variables and configuration files can be used to provide application and user credentials. See also Options.

tekore.config_from_environment(return_refresh=False)

Read configuration from environment variables.

Parameters:

return_refresh (bool) – return user refresh token

Returns:

(client ID, client secret, redirect URI), None if not found. If return_refresh is True, also return user refresh token.

Return type:

tuple

Raises:

MissingConfigurationWarning – when a missing value is encountered

Examples

client_id, client_secret, redirect_uri = tk.config_from_environment()

conf = tk.config_from_environment(return_refresh=True)
client_id, client_secret, redirect_uri, user_refresh = conf
tekore.config_from_file(file_path, section='DEFAULT', return_refresh=False)

Read configuration from a config file.

The configuration must be in INI format as accepted by configparser.ConfigParser.

Parameters:
  • file_path (str) – path of the file containing the credential variables

  • section (str) – name of the section to read variables from

  • return_refresh (bool) – return user refresh token

Returns:

(client ID, client secret, redirect URI), None if not found. If return_refresh is True, also return user refresh token.

Return type:

tuple

Raises:

MissingConfigurationWarning – when a missing value is encountered

Examples

client_id, client_secret, redirect_uri = tk.config_from_file(filename)

conf = tk.config_from_file(filename, return_refresh=True)
client_id, client_secret, redirect_uri, user_refresh = conf
tekore.config_to_file(file_path, values, section='DEFAULT')

Write configuration to a config file.

Existing configuration is preserved if it’s not in conflict.

Parameters:
  • file_path (str) – path of the configuration file

  • values (Iterable | dict) – configuration values to write, dict or iterable, see below for examples

  • section (str) – name of the section to write to

Return type:

None

Examples

Configuration can be written in different ways. Pass in an iterable to use the preset variable names. The values should be ordered as returned when reading configuration: client_id, client_secret, redirect_uri, user_refresh.

conf = (client_id, client_secret, redirect_uri, user_refresh)
config_to_file(filename, conf)

A shorter iterable or one containing None values may be passed. Items missing from the end are ignored and None values are discarded.

# Write partial information
config_to_file(filename, (client_id, client_secret))

# Fill the missing configuration
conf = (None, None, redirect_uri, user_refresh)
config_to_file(filename, conf)

A dictionary is also accepted. In this case the keys are used instead of preset variable names.

config_to_file(filename, {'REFRESH_TOKEN': refresh_token})
class tekore.MissingConfigurationWarning

Bases: RuntimeWarning

Missing value read from configuration.

Options

Configuration values are read from and written to preset names. Those names can be changed to your liking.

import tekore as tk

tk.client_id_var = 'your_client_id_var'
tk.client_secret_var = 'your_client_secret_var'
tk.redirect_uri_var = 'your_redirect_uri_var'
tk.user_refresh_var = 'your_user_refresh_var'

Note

Changing values requires importing Tekore as a module as above.

tekore.client_id_var: str = 'SPOTIFY_CLIENT_ID'

Configuration variable name for a client ID.

tekore.client_secret_var: str = 'SPOTIFY_CLIENT_SECRET'

Configuration variable name for a client secret.

tekore.redirect_uri_var: str = 'SPOTIFY_REDIRECT_URI'

Configuration variable name for a redirect URI.

tekore.user_refresh_var: str = 'SPOTIFY_USER_REFRESH'

Configuration variable name for a user refresh token.

Conversions

Conversions between Spotify IDs, URIs and URLs.

to_uri

Convert an ID to an URI of the appropriate type.

to_url

Convert an ID to an URL of the appropriate type.

from_uri

Parse type and ID from an URI.

from_url

Parse type and ID from an URL.

ConversionError

Error in conversion.

IdentifierType

Valid types of Spotify IDs.

check_id

Validate resource ID to be base 62.

check_type

Validate type of an ID.

import tekore as tk

# Create ULR for opening an album in the browser
mountain = '3RBULTZJ97bvVzZLpxcB0j'
m_url = tk.to_url('album', mountain)

# Parse input
type_, id_ = tk.from_url(m_url)
print(f'Got type `{type_}` with ID `{id_}`')
tekore.check_id(id_)

Validate resource ID to be base 62.

Note that user IDs can have special characters, so they cannot be validated.

Raises:

ConversionError – When ID is invalid.

Parameters:

id_ (str) –

Return type:

None

tekore.check_type(type_)

Validate type of an ID.

Raises:

ConversionError – When type is invalid.

Parameters:

type_ (str | IdentifierType) –

Return type:

None

class tekore.ConversionError

Bases: Exception

Error in conversion.

tekore.from_uri(uri)

Parse type and ID from an URI.

Parameters:

uri (str) – URI to parse

Returns:

type and ID parsed from the URI

Return type:

Tuple[str, str]

Raises:

ConversionError – On invalid format, prefix, type or ID.

tekore.from_url(url)

Parse type and ID from an URL.

Any parameters in the URL will be ignored.

Parameters:

url (str) – URL to parse

Returns:

type and ID parsed from the URL

Return type:

Tuple[str, str]

Raises:

ConversionError – On invalid format, prefix, type or ID.

tekore.to_uri(type_, id_)

Convert an ID to an URI of the appropriate type.

Parameters:
  • type – valid IdentifierType

  • id – resource identifier

  • type_ (str | IdentifierType) –

  • id_ (str) –

Returns:

converted URI

Return type:

str

Raises:

ConversionError – On invalid type or ID.

tekore.to_url(type_, id_)

Convert an ID to an URL of the appropriate type.

Parameters:
  • type – valid IdentifierType

  • id – resource identifier

  • type_ (str | IdentifierType) –

  • id_ (str) –

Returns:

converted URL

Return type:

str

Raises:

ConversionError – On invalid type or ID.

class tekore.IdentifierType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Valid types of Spotify IDs.

album = 'album'
artist = 'artist'
episode = 'episode'
playlist = 'playlist'
show = 'show'
track = 'track'
user = 'user'

Errors

Web errors for Authorisation and Client.

HTTPError

Base error for all web status errors.

ClientError

4xx - Base client error.

ServerError

5xx - Base server error.

BadRequest

400 - Bad request.

Unauthorised

401 - Unauthorised.

Forbidden

403 - Forbidden.

NotFound

404 - Not found.

TooManyRequests

429 - Too many requests.

InternalServerError

500 - Internal server error.

BadGateway

502 - Bad gateway.

ServiceUnavailable

503 - Service unavailable.

Clients facing the Web API raise errors when recieving bad status codes. Only errors documented in the Web API documentation are expected and provided. Other exceptions are raised as ClientError or ServerError.

import tekore as tk

conf = tk.config_from_environment()
token = tk.request_client_token(*conf[:2])
spotify = tk.Spotify(token)

try:
    spotify.album('not-a-real-album')
except tk.BadRequest:
    print('Whoops, bad request!')
except tk.HTTPError:
    print('Something is seriously wrong.')

Error objects also contain the relevant Request and Response objects for closer inspection.

try:
    spotify.album('not-a-real-album')
except tk.BadRequest as ex:
    print(str(ex))
    print(ex.request)
    print(ex.response)
class tekore.HTTPError(message, request, response)

Bases: Exception

Base error for all web status errors.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

request

request that led to the error

response

response from the web server

class tekore.ClientError(message, request, response)

Bases: HTTPError

4xx - Base client error.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.ServerError(message, request, response)

Bases: HTTPError

5xx - Base server error.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.BadRequest(message, request, response)

Bases: ClientError

400 - Bad request.

The request could not be understood by the server due to malformed syntax.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.Unauthorised(message, request, response)

Bases: ClientError

401 - Unauthorised.

The request requires user authentication or, if the request included authorization credentials, authorization has been refused for those credentials.

The scopes associated with the call are attached to this class.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

optional_scope: str
required_scope: str
scope: str
class tekore.Forbidden(message, request, response)

Bases: ClientError

403 - Forbidden.

The server understood the request, but is refusing to fulfill it.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.NotFound(message, request, response)

Bases: ClientError

404 - Not found.

The requested resource could not be found. This error can be due to a temporary or permanent condition.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.TooManyRequests(message, request, response)

Bases: ClientError

429 - Too many requests.

Rate limiting has been applied.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.InternalServerError(message, request, response)

Bases: ServerError

500 - Internal server error.

You should never receive this error because the clever coders at Spotify catch them all… But if you are unlucky enough to get one, please report it to Spotify through their GitHub (spotify/web-api).

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.BadGateway(message, request, response)

Bases: ClientError

502 - Bad gateway.

The server was acting as a gateway or proxy and received an invalid response from the upstream server.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

class tekore.ServiceUnavailable(message, request, response)

Bases: ClientError

503 - Service unavailable.

The server is currently unable to handle the request due to a temporary condition which will be alleviated after some delay. You can choose to resend the request again.

Parameters:
  • message (str) –

  • request (Request) –

  • response (Response) –

Models

Response model definitions for client.

Responses are parsed into Pydantic models. This allows accessing parts of the response directly as attributes. Further documentation on specific attribute values can be viewed in the Web API reference.

import tekore as tk

# Call the API
spotify = tk.Spotify(token)
album = spotify.album('3RBULTZJ97bvVzZLpxcB0j')

# Use the response
for track in album.tracks.items:
    print(track.track_number, track.name)

Using Pydantic models means that responses are easy to work with. They provide a readable repr (particularly with devtools) for quick inspection, and it is also possible to convert models to builtin and JSON representations:

from pprint import pprint

print(album)
pprint(album, depth=2)

album.dict()
album.json()

Responses will sometimes contain unknown attributes when the API changes. They are ignored when parsing the response model, but a UnknownModelAttributeWarning is issued when encountering one. Please consider upgrading Tekore if a newer version documents and handles it.

Models are made available in the tekore.models namespace.

Album

Album

Album base.

AlbumGroup

Relationship between artist and album.

AlbumType

Type of album.

SimpleAlbum

Simplified album object.

SimpleAlbumPaging

Paging containing simplified albums.

FullAlbum

Complete album object.

SavedAlbum

Album saved to library.

SavedAlbumPaging

Paging of albums in library.

class tekore.model.Album(*, id, href, type, uri, album_type, artists, external_urls, images, name, total_tracks, release_date, release_date_precision, available_markets=None, is_playable=None)

Bases: Item

Album base.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • album_type (AlbumType) –

  • artists (List[SimpleArtist]) –

  • external_urls (dict) –

  • images (List[Image]) –

  • name (str) –

  • total_tracks (int) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • available_markets (List[str] | None) –

  • is_playable (bool | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album_type': FieldInfo(annotation=AlbumType, required=True), 'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'total_tracks': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.AlbumGroup(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Relationship between artist and album.

album = 'album'
appears_on = 'appears_on'
compilation = 'compilation'
single = 'single'
class tekore.model.AlbumType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Type of album.

album = 'album'
compilation = 'compilation'
ep = 'ep'
single = 'single'
class tekore.model.SimpleAlbum(*, id, href, type, uri, album_type, artists, external_urls, images, name, total_tracks, release_date, release_date_precision, available_markets=None, is_playable=None, album_group=None)

Bases: Album

Simplified album object.

album_group is available when getting an artist’s albums. available_markets is available when market is not specified.

The presence of is_playable is undocumented and it appears to only be True when it is present.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • album_type (AlbumType) –

  • artists (List[SimpleArtist]) –

  • external_urls (dict) –

  • images (List[Image]) –

  • name (str) –

  • total_tracks (int) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • available_markets (List[str] | None) –

  • is_playable (bool | None) –

  • album_group (AlbumGroup | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album_group': FieldInfo(annotation=Union[AlbumGroup, NoneType], required=False, default=None), 'album_type': FieldInfo(annotation=AlbumType, required=True), 'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'total_tracks': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleAlbumPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging containing simplified albums.

Parameters:
  • href (str) –

  • items (List[SimpleAlbum]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleAlbum], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullAlbum(*, id, href, type, uri, album_type, artists, external_urls, images, name, total_tracks, release_date, release_date_precision, available_markets=None, is_playable=None, copyrights, external_ids, genres, label, popularity, tracks)

Bases: Album

Complete album object.

available_markets is available when market is not specified.

The presence of is_playable is undocumented and it appears to only be True when it is present.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • album_type (AlbumType) –

  • artists (List[SimpleArtist]) –

  • external_urls (dict) –

  • images (List[Image]) –

  • name (str) –

  • total_tracks (int) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • available_markets (List[str] | None) –

  • is_playable (bool | None) –

  • copyrights (List[Copyright]) –

  • external_ids (dict) –

  • genres (List[str]) –

  • label (str | None) –

  • popularity (int) –

  • tracks (SimpleTrackPaging) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album_type': FieldInfo(annotation=AlbumType, required=True), 'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'external_ids': FieldInfo(annotation=dict, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'genres': FieldInfo(annotation=List[str], required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'label': FieldInfo(annotation=Union[str, NoneType], required=True), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'total_tracks': FieldInfo(annotation=int, required=True), 'tracks': FieldInfo(annotation=SimpleTrackPaging, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedAlbum(*, added_at, album)

Bases: Model

Album saved to library.

Parameters:
  • added_at (datetime) –

  • album (FullAlbum) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'added_at': FieldInfo(annotation=datetime, required=True), 'album': FieldInfo(annotation=FullAlbum, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedAlbumPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of albums in library.

Parameters:
  • href (str) –

  • items (List[SavedAlbum]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SavedAlbum], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Artist

Artist

Artist base.

SimpleArtist

Simplified artist object.

FullArtist

Complete artist object.

FullArtistCursorPaging

Paging of full artists.

FullArtistOffsetPaging

Paging of full artists.

class tekore.model.Artist(*, id, href, type, uri, external_urls, name)

Bases: Item

Artist base.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • name (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleArtist(*, id, href, type, uri, external_urls, name)

Bases: Artist

Simplified artist object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • name (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullArtist(*, id, href, type, uri, external_urls, name, followers, genres, images, popularity)

Bases: Artist

Complete artist object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • name (str) –

  • followers (Followers) –

  • genres (List[str]) –

  • images (List[Image]) –

  • popularity (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'followers': FieldInfo(annotation=Followers, required=True), 'genres': FieldInfo(annotation=List[str], required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullArtistCursorPaging(*, href, items, limit, next, cursors, total)

Bases: CursorPaging

Paging of full artists.

Parameters:
  • href (str) –

  • items (List[FullArtist]) –

  • limit (int) –

  • next (str | None) –

  • cursors (Cursor) –

  • total (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'cursors': FieldInfo(annotation=Cursor, required=True), 'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[FullArtist], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullArtistOffsetPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of full artists.

Parameters:
  • href (str) –

  • items (List[FullArtist]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[FullArtist], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Audiobook

Audiobook

Audiobook base.

SimpleAudiobook

Simplified audiobook.

SimpleAudiobookPaging

Paging of simplified audiobooks.

FullAudiobook

Complete audiobook object.

Author

Audiobook author.

Narrator

Audiobook narrator.

class tekore.model.Audiobook(*, id, href, type, uri, authors, available_markets=None, copyrights, description, edition, explicit, external_urls, html_description, images, is_externally_hosted=None, languages, media_type, name, narrators, publisher, total_chapters)

Bases: Item

Audiobook base.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • authors (List[Author]) –

  • available_markets (List[str] | None) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • edition (str | None) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • narrators (List[Narrator]) –

  • publisher (str) –

  • total_chapters (int | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'authors': FieldInfo(annotation=List[Author], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'edition': FieldInfo(annotation=Union[str, NoneType], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'narrators': FieldInfo(annotation=List[Narrator], required=True), 'publisher': FieldInfo(annotation=str, required=True), 'total_chapters': FieldInfo(annotation=Union[int, NoneType], required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleAudiobook(*, id, href, type, uri, authors, available_markets=None, copyrights, description, edition, explicit, external_urls, html_description, images, is_externally_hosted=None, languages, media_type, name, narrators, publisher, total_chapters, chapters=None)

Bases: Audiobook

Simplified audiobook.

May contain chapters, but that is likely an error.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • authors (List[Author]) –

  • available_markets (List[str] | None) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • edition (str | None) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • narrators (List[Narrator]) –

  • publisher (str) –

  • total_chapters (int | None) –

  • chapters (dict | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'authors': FieldInfo(annotation=List[Author], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'chapters': FieldInfo(annotation=Union[dict, NoneType], required=False, default=None), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'edition': FieldInfo(annotation=Union[str, NoneType], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'narrators': FieldInfo(annotation=List[Narrator], required=True), 'publisher': FieldInfo(annotation=str, required=True), 'total_chapters': FieldInfo(annotation=Union[int, NoneType], required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleAudiobookPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified audiobooks.

Parameters:
  • href (str) –

  • items (List[SimpleAudiobook]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleAudiobook], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullAudiobook(*, id, href, type, uri, authors, available_markets=None, copyrights, description, edition, explicit, external_urls, html_description, images, is_externally_hosted=None, languages, media_type, name, narrators, publisher, total_chapters, chapters)

Bases: Audiobook

Complete audiobook object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • authors (List[Author]) –

  • available_markets (List[str] | None) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • edition (str | None) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • narrators (List[Narrator]) –

  • publisher (str) –

  • total_chapters (int | None) –

  • chapters (SimpleChapterPaging) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'authors': FieldInfo(annotation=List[Author], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'chapters': FieldInfo(annotation=SimpleChapterPaging, required=True), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'edition': FieldInfo(annotation=Union[str, NoneType], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'narrators': FieldInfo(annotation=List[Narrator], required=True), 'publisher': FieldInfo(annotation=str, required=True), 'total_chapters': FieldInfo(annotation=Union[int, NoneType], required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Author(*, name)

Bases: Model

Audiobook author.

Parameters:

name (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'name': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Narrator(*, name)

Bases: Model

Audiobook narrator.

Parameters:

name (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'name': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Category

Category

Spotify tag category.

CategoryPaging

Paging of categories.

class tekore.model.Category(*, id, href, icons, name)

Bases: Identifiable

Spotify tag category.

Parameters:
  • id (str) –

  • href (str) –

  • icons (List[Image]) –

  • name (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'icons': FieldInfo(annotation=List[Image], required=True), 'id': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.CategoryPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of categories.

Parameters:
  • href (str) –

  • items (List[Category]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[Category], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Chapter

Chapter

Audiobook chapter base.

SimpleChapter

Simplified chapter.

SimpleChapterPaging

Paging of simplified chapters.

FullChapter

Complete chapter object.

class tekore.model.Chapter(*, id, href, type, uri, audio_preview_url, available_markets=None, chapter_number, description, duration_ms, explicit, external_urls, html_description, images, is_playable=None, languages, name, release_date_precision, release_date, restrictions=None, resume_point)

Bases: Item

Audiobook chapter base.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • available_markets (List[str] | None) –

  • chapter_number (int) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_playable (bool | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • release_date (str) –

  • restrictions (Restrictions | None) –

  • resume_point (ResumePoint) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'chapter_number': FieldInfo(annotation=int, required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'resume_point': FieldInfo(annotation=ResumePoint, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleChapter(*, id, href, type, uri, audio_preview_url, available_markets=None, chapter_number, description, duration_ms, explicit, external_urls, html_description, images, is_playable=None, languages, name, release_date_precision, release_date, restrictions=None, resume_point)

Bases: Chapter

Simplified chapter.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • available_markets (List[str] | None) –

  • chapter_number (int) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_playable (bool | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • release_date (str) –

  • restrictions (Restrictions | None) –

  • resume_point (ResumePoint) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'chapter_number': FieldInfo(annotation=int, required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'resume_point': FieldInfo(annotation=ResumePoint, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleChapterPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified chapters.

Parameters:
  • href (str) –

  • items (List[SimpleChapter]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleChapter], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullChapter(*, id, href, type, uri, audio_preview_url, available_markets=None, chapter_number, description, duration_ms, explicit, external_urls, html_description, images, is_playable=None, languages, name, release_date_precision, release_date, restrictions=None, resume_point, audiobook)

Bases: Chapter

Complete chapter object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • available_markets (List[str] | None) –

  • chapter_number (int) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_playable (bool | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • release_date (str) –

  • restrictions (Restrictions | None) –

  • resume_point (ResumePoint) –

  • audiobook (SimpleAudiobook) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'audiobook': FieldInfo(annotation=SimpleAudiobook, required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'chapter_number': FieldInfo(annotation=int, required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'resume_point': FieldInfo(annotation=ResumePoint, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Episode

Episode

Episode base.

SimpleEpisode

Simplified episode object.

SimpleEpisodePaging

Paging of simplified episodes.

FullEpisode

Complete episode object.

SavedEpisode

Episode saved to library.

SavedEpisodePaging

Paging of episodes in library.

ResumePoint

Resume point.

class tekore.model.Episode(*, id, href, type, uri, audio_preview_url, description, duration_ms, explicit, external_urls, html_description, images, is_externally_hosted, is_playable=None, language=None, languages, name, release_date, release_date_precision, resume_point=None)

Bases: Item

Episode base.

language is deprecated.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool) –

  • is_playable (bool | None) –

  • language (str | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • resume_point (ResumePoint | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'resume_point': FieldInfo(annotation=Union[ResumePoint, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleEpisode(*, id, href, type, uri, audio_preview_url, description, duration_ms, explicit, external_urls, html_description, images, is_externally_hosted, is_playable=None, language=None, languages, name, release_date, release_date_precision, resume_point=None)

Bases: Episode

Simplified episode object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool) –

  • is_playable (bool | None) –

  • language (str | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • resume_point (ResumePoint | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'resume_point': FieldInfo(annotation=Union[ResumePoint, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleEpisodePaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified episodes.

Parameters:
  • href (str) –

  • items (List[SimpleEpisode]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleEpisode], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullEpisode(*, id, href, type, uri, audio_preview_url, description, duration_ms, explicit, external_urls, html_description, images, is_externally_hosted, is_playable=None, language=None, languages, name, release_date, release_date_precision, resume_point=None, restrictions=None, show)

Bases: Episode

Complete episode object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool) –

  • is_playable (bool | None) –

  • language (str | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • resume_point (ResumePoint | None) –

  • restrictions (Restrictions | None) –

  • show (SimpleShow) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'resume_point': FieldInfo(annotation=Union[ResumePoint, NoneType], required=False, default=None), 'show': FieldInfo(annotation=SimpleShow, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedEpisode(*, added_at, episode)

Bases: Model

Episode saved to library.

Parameters:
  • added_at (datetime) –

  • episode (FullEpisode) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'added_at': FieldInfo(annotation=datetime, required=True), 'episode': FieldInfo(annotation=FullEpisode, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedEpisodePaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of episodes in library.

Parameters:
  • href (str) –

  • items (List[SavedEpisode]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SavedEpisode], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.ResumePoint(*, fully_played, resume_position_ms)

Bases: Model

Resume point.

Parameters:
  • fully_played (bool) –

  • resume_position_ms (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'fully_played': FieldInfo(annotation=bool, required=True), 'resume_position_ms': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Playback

CurrentlyPlaying

Current playback.

CurrentlyPlayingContext

Extended current playback context.

CurrentlyPlayingType

Type of currently playing item.

Queue

Playback queue.

Device

Playback device.

DeviceType

Type of playback device.

Actions

Player actions.

Disallows

Disallowed player actions.

PlayerErrorReason

Reasons for errors in player actions.

RepeatState

Playback repeat state.

PlayHistory

Previously played track.

PlayHistoryCursor

Cursor to play history.

PlayHistoryPaging

Paging to play history.

Context

Context of a played track or episode.

ContextType

Type of player context.

Currently playing
class tekore.model.CurrentlyPlaying(*, actions, currently_playing_type, is_playing, timestamp, context, progress_ms, item)

Bases: Model

Current playback.

context, progress_ms and item may be None e.g. during a private session.

Parameters:
  • actions (Actions) –

  • currently_playing_type (CurrentlyPlayingType) –

  • is_playing (bool) –

  • timestamp (int) –

  • context (Context | None) –

  • progress_ms (int | None) –

  • item (FullTrack | LocalTrack | FullEpisode | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'actions': FieldInfo(annotation=Actions, required=True), 'context': FieldInfo(annotation=Union[Context, NoneType], required=True), 'currently_playing_type': FieldInfo(annotation=CurrentlyPlayingType, required=True), 'is_playing': FieldInfo(annotation=bool, required=True), 'item': FieldInfo(annotation=Union[FullTrack, LocalTrack, FullEpisode, NoneType], required=True), 'progress_ms': FieldInfo(annotation=Union[int, NoneType], required=True), 'timestamp': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.CurrentlyPlayingContext(*, actions, currently_playing_type, is_playing, timestamp, context, progress_ms, item, device, repeat_state, shuffle_state, smart_shuffle)

Bases: CurrentlyPlaying

Extended current playback context.

smart_shuffle is not documented in the Spotify API.

Parameters:
  • actions (Actions) –

  • currently_playing_type (CurrentlyPlayingType) –

  • is_playing (bool) –

  • timestamp (int) –

  • context (Context | None) –

  • progress_ms (int | None) –

  • item (FullTrack | LocalTrack | FullEpisode | None) –

  • device (Device) –

  • repeat_state (RepeatState) –

  • shuffle_state (bool) –

  • smart_shuffle (bool | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'actions': FieldInfo(annotation=Actions, required=True), 'context': FieldInfo(annotation=Union[Context, NoneType], required=True), 'currently_playing_type': FieldInfo(annotation=CurrentlyPlayingType, required=True), 'device': FieldInfo(annotation=Device, required=True), 'is_playing': FieldInfo(annotation=bool, required=True), 'item': FieldInfo(annotation=Union[FullTrack, LocalTrack, FullEpisode, NoneType], required=True), 'progress_ms': FieldInfo(annotation=Union[int, NoneType], required=True), 'repeat_state': FieldInfo(annotation=RepeatState, required=True), 'shuffle_state': FieldInfo(annotation=bool, required=True), 'smart_shuffle': FieldInfo(annotation=Union[bool, NoneType], required=True), 'timestamp': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.CurrentlyPlayingType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Type of currently playing item.

ad = 'ad'
episode = 'episode'
track = 'track'
unknown = 'unknown'
class tekore.model.Queue(*, currently_playing, queue)

Bases: Model

Playback queue.

Parameters:
  • currently_playing (FullTrack | LocalTrack | FullEpisode | None) –

  • queue (List[FullTrack | LocalTrack | FullEpisode | None]) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'currently_playing': FieldInfo(annotation=Union[FullTrack, LocalTrack, FullEpisode, NoneType], required=True), 'queue': FieldInfo(annotation=List[Union[FullTrack, LocalTrack, FullEpisode, NoneType]], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Device(*, id, is_active, is_private_session, is_restricted, name, type, volume_percent, supports_volume)

Bases: Identifiable

Playback device.

Parameters:
  • id (str) –

  • is_active (bool) –

  • is_private_session (bool) –

  • is_restricted (bool) –

  • name (str) –

  • type (DeviceType) –

  • volume_percent (int | None) –

  • supports_volume (bool) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'id': FieldInfo(annotation=str, required=True), 'is_active': FieldInfo(annotation=bool, required=True), 'is_private_session': FieldInfo(annotation=bool, required=True), 'is_restricted': FieldInfo(annotation=bool, required=True), 'name': FieldInfo(annotation=str, required=True), 'supports_volume': FieldInfo(annotation=bool, required=True), 'type': FieldInfo(annotation=DeviceType, required=True), 'volume_percent': FieldInfo(annotation=Union[int, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.DeviceType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Type of playback device.

AVR = 'AVR'
AudioDongle = 'AudioDongle'
Automobile = 'Automobile'
CastAudio = 'CastAudio'
CastVideo = 'CastVideo'
Computer = 'Computer'
GameConsole = 'GameConsole'
STB = 'STB'
Smartphone = 'Smartphone'
Speaker = 'Speaker'
TV = 'TV'
Tablet = 'Tablet'
Unknown = 'Unknown'
audiodongle = 'AudioDongle'
automobile = 'Automobile'
avr = 'AVR'
castaudio = 'CastAudio'
castvideo = 'CastVideo'
computer = 'Computer'
gameconsole = 'GameConsole'
smartphone = 'Smartphone'
speaker = 'Speaker'
stb = 'STB'
tablet = 'Tablet'
tv = 'TV'
unknown = 'Unknown'
class tekore.model.Actions(*, disallows)

Bases: Model

Player actions.

Parameters:

disallows (Disallows) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'disallows': FieldInfo(annotation=Disallows, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Disallows(*, interrupting_playback=False, pausing=False, resuming=False, seeking=False, skipping_next=False, skipping_prev=False, toggling_repeat_context=False, toggling_shuffle=False, toggling_repeat_track=False, transferring_playback=False)

Bases: Model

Disallowed player actions.

Parameters:
  • interrupting_playback (bool) –

  • pausing (bool) –

  • resuming (bool) –

  • seeking (bool) –

  • skipping_next (bool) –

  • skipping_prev (bool) –

  • toggling_repeat_context (bool) –

  • toggling_shuffle (bool) –

  • toggling_repeat_track (bool) –

  • transferring_playback (bool) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'interrupting_playback': FieldInfo(annotation=bool, required=False, default=False), 'pausing': FieldInfo(annotation=bool, required=False, default=False), 'resuming': FieldInfo(annotation=bool, required=False, default=False), 'seeking': FieldInfo(annotation=bool, required=False, default=False), 'skipping_next': FieldInfo(annotation=bool, required=False, default=False), 'skipping_prev': FieldInfo(annotation=bool, required=False, default=False), 'toggling_repeat_context': FieldInfo(annotation=bool, required=False, default=False), 'toggling_repeat_track': FieldInfo(annotation=bool, required=False, default=False), 'toggling_shuffle': FieldInfo(annotation=bool, required=False, default=False), 'transferring_playback': FieldInfo(annotation=bool, required=False, default=False)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PlayerErrorReason(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Reasons for errors in player actions.

ALREADY_PAUSED = 'The command requires playback to not be paused.'
ALREADY_PLAYING = 'The track should not be restarted if the same track and context is already playing, and there is a resume point.'
CONTEXT_DISALLOW = 'The command could not be performed on the context.'
DEVICE_NOT_CONTROLLABLE = 'Not possible to remote control the device.'
ENDLESS_CONTEXT = 'The shuffle command cannot be applied on an endless context.'
NOT_PAUSED = 'The command requires playback to be paused.'
NOT_PLAYING_CONTEXT = 'The command requires that a context is currently playing.'
NOT_PLAYING_LOCALLY = 'The command requires playback on the local device.'
NOT_PLAYING_TRACK = 'The command requires that a track is currently playing.'
NO_ACTIVE_DEVICE = 'Requires an active device and the user has none.'
NO_NEXT_TRACK = 'The command requires a next track, but there is none in the context.'
NO_PREV_TRACK = 'The command requires a previous track, but there is none in the context.'
NO_SPECIFIC_TRACK = 'The requested track does not exist.'
PREMIUM_REQUIRED = 'The request is prohibited for non-premium users.'
RATE_LIMITED = 'The user is rate limited due to too frequent track play,also known as cat-on-the-keyboard spamming.'
REMOTE_CONTROL_DISALLOW = 'The context cannot be remote-controlled.'
UNKNOWN = 'Certain actions are restricted because of unknown reasons.'
VOLUME_CONTROL_DISALLOW = 'Not possible to remote control the device’s volume.'
class tekore.model.RepeatState(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Playback repeat state.

context = 'context'
off = 'off'
track = 'track'
Play history
class tekore.model.PlayHistory(*, track, played_at, context)

Bases: Model

Previously played track.

Context is supposedly sometimes available.

Parameters:
  • track (FullTrack) –

  • played_at (datetime) –

  • context (Context | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'context': FieldInfo(annotation=Union[Context, NoneType], required=True), 'played_at': FieldInfo(annotation=datetime, required=True), 'track': FieldInfo(annotation=FullTrack, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PlayHistoryCursor(*, after, before)

Bases: Cursor

Cursor to play history.

Parameters:
  • after (str | None) –

  • before (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'after': FieldInfo(annotation=Union[str, NoneType], required=True), 'before': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PlayHistoryPaging(*, href, items, limit, next, cursors)

Bases: CursorPaging

Paging to play history.

Cursors are not available when paging is exhausted.

Parameters:
  • href (str) –

  • items (List[PlayHistory]) –

  • limit (int) –

  • next (str | None) –

  • cursors (PlayHistoryCursor | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'cursors': FieldInfo(annotation=Union[PlayHistoryCursor, NoneType], required=True), 'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[PlayHistory], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Context(*, type, href, external_urls, uri)

Bases: Model

Context of a played track or episode.

Parameters:
  • type (ContextType) –

  • href (str) –

  • external_urls (dict) –

  • uri (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=ContextType, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.ContextType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Type of player context.

album = 'album'
artist = 'artist'
collection = 'collection'
playlist = 'playlist'
show = 'show'
Playlist

Playlist

Playlist base.

PlaylistTrack

Track or episode on a playlist.

PlaylistTrackPaging

Paging of playlist tracks.

SimplePlaylist

Simplified playlist object.

SimplePlaylistPaging

Paging of simplified playlists.

FullPlaylist

Complete playlist object.

FullPlaylistTrack

Track on a playlist.

FullPlaylistEpisode

Episode on a playlist.

LocalPlaylistTrack

Local track on a playlist.

LocalItem

Base for local items.

LocalAlbum

Album of a locally saved track.

LocalArtist

Artist of a locally saved track.

LocalTrack

Locally saved track.

class tekore.model.Playlist(*, id, href, type, uri, collaborative, description, external_urls, images, name, owner, public, snapshot_id, primary_color)

Bases: Item

Playlist base.

owner can be None on featured playlists.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • collaborative (bool) –

  • description (str | None) –

  • external_urls (dict) –

  • images (List[Image] | None) –

  • name (str) –

  • owner (PublicUser) –

  • public (bool | None) –

  • snapshot_id (str) –

  • primary_color (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'collaborative': FieldInfo(annotation=bool, required=True), 'description': FieldInfo(annotation=Union[str, NoneType], required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=True), 'name': FieldInfo(annotation=str, required=True), 'owner': FieldInfo(annotation=PublicUser, required=True), 'primary_color': FieldInfo(annotation=Union[str, NoneType], required=True), 'public': FieldInfo(annotation=Union[bool, NoneType], required=True), 'snapshot_id': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PlaylistTrack(*, added_at, added_by, is_local, track, primary_color, video_thumbnail)

Bases: Model

Track or episode on a playlist.

Parameters:
  • added_at (datetime) –

  • added_by (PublicUser) –

  • is_local (bool) –

  • track (FullPlaylistTrack | FullPlaylistEpisode | LocalPlaylistTrack | None) –

  • primary_color (str | None) –

  • video_thumbnail (dict | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'added_at': FieldInfo(annotation=datetime, required=True), 'added_by': FieldInfo(annotation=PublicUser, required=True), 'is_local': FieldInfo(annotation=bool, required=True), 'primary_color': FieldInfo(annotation=Union[str, NoneType], required=True), 'track': FieldInfo(annotation=Union[FullPlaylistTrack, FullPlaylistEpisode, LocalPlaylistTrack, NoneType], required=True), 'video_thumbnail': FieldInfo(annotation=Union[dict, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PlaylistTrackPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of playlist tracks.

Parameters:
  • href (str) –

  • items (List[PlaylistTrack]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[PlaylistTrack], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimplePlaylist(*, id, href, type, uri, collaborative, description, external_urls, images, name, owner, public, snapshot_id, primary_color, tracks)

Bases: Playlist

Simplified playlist object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • collaborative (bool) –

  • description (str | None) –

  • external_urls (dict) –

  • images (List[Image] | None) –

  • name (str) –

  • owner (PublicUser) –

  • public (bool | None) –

  • snapshot_id (str) –

  • primary_color (str | None) –

  • tracks (Tracks) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'collaborative': FieldInfo(annotation=bool, required=True), 'description': FieldInfo(annotation=Union[str, NoneType], required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=True), 'name': FieldInfo(annotation=str, required=True), 'owner': FieldInfo(annotation=PublicUser, required=True), 'primary_color': FieldInfo(annotation=Union[str, NoneType], required=True), 'public': FieldInfo(annotation=Union[bool, NoneType], required=True), 'snapshot_id': FieldInfo(annotation=str, required=True), 'tracks': FieldInfo(annotation=Tracks, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimplePlaylistPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified playlists.

Parameters:
  • href (str) –

  • items (List[SimplePlaylist | None]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[Union[SimplePlaylist, NoneType]], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullPlaylist(*, id, href, type, uri, collaborative, description, external_urls, images, name, owner, public, snapshot_id, primary_color, followers, tracks)

Bases: Playlist

Complete playlist object.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • collaborative (bool) –

  • description (str | None) –

  • external_urls (dict) –

  • images (List[Image] | None) –

  • name (str) –

  • owner (PublicUser) –

  • public (bool | None) –

  • snapshot_id (str) –

  • primary_color (str | None) –

  • followers (Followers) –

  • tracks (PlaylistTrackPaging) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'collaborative': FieldInfo(annotation=bool, required=True), 'description': FieldInfo(annotation=Union[str, NoneType], required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'followers': FieldInfo(annotation=Followers, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=True), 'name': FieldInfo(annotation=str, required=True), 'owner': FieldInfo(annotation=PublicUser, required=True), 'primary_color': FieldInfo(annotation=Union[str, NoneType], required=True), 'public': FieldInfo(annotation=Union[bool, NoneType], required=True), 'snapshot_id': FieldInfo(annotation=str, required=True), 'tracks': FieldInfo(annotation=PlaylistTrackPaging, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullPlaylistTrack(*, id, href, type, uri, artists, available_markets=None, disc_number, duration_ms, explicit, external_urls, is_local, is_playable=None, linked_from=None, name, preview_url, restrictions=None, track_number, album, external_ids, popularity, episode, track)

Bases: FullTrack

Track on a playlist.

Provides episode and track booleans to easily determine the type of playlist item.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • artists (List[SimpleArtist]) –

  • available_markets (List[str] | None) –

  • disc_number (int) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • is_local (Literal[False]) –

  • is_playable (bool | None) –

  • linked_from (TrackLink | None) –

  • name (str) –

  • preview_url (str | None) –

  • restrictions (Restrictions | None) –

  • track_number (int) –

  • album (SimpleAlbum) –

  • external_ids (dict) –

  • popularity (int) –

  • episode (Literal[False]) –

  • track (Literal[True]) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album': FieldInfo(annotation=SimpleAlbum, required=True), 'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'episode': FieldInfo(annotation=Literal[False], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_ids': FieldInfo(annotation=dict, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'is_local': FieldInfo(annotation=Literal[False], required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'linked_from': FieldInfo(annotation=Union[TrackLink, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'track': FieldInfo(annotation=Literal[True], required=True), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullPlaylistEpisode(*, id, href, type, uri, audio_preview_url, description, duration_ms, explicit, external_urls, html_description, images, is_externally_hosted, is_playable=None, language=None, languages, name, release_date, release_date_precision, resume_point=None, restrictions=None, show, available_markets=None, episode, track)

Bases: FullEpisode

Episode on a playlist.

Provides episode and track booleans to easily determine the type of playlist item. available_markets is undocumented.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • audio_preview_url (str | None) –

  • description (str) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str) –

  • images (List[Image]) –

  • is_externally_hosted (bool) –

  • is_playable (bool | None) –

  • language (str | None) –

  • languages (List[str]) –

  • name (str) –

  • release_date (str) –

  • release_date_precision (ReleaseDatePrecision) –

  • resume_point (ResumePoint | None) –

  • restrictions (Restrictions | None) –

  • show (SimpleShow) –

  • available_markets (List[str] | None) –

  • episode (Literal[True]) –

  • track (Literal[False]) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'audio_preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'description': FieldInfo(annotation=str, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'episode': FieldInfo(annotation=Literal[True], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'languages': FieldInfo(annotation=List[str], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=str, required=True), 'release_date_precision': FieldInfo(annotation=ReleaseDatePrecision, required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'resume_point': FieldInfo(annotation=Union[ResumePoint, NoneType], required=False, default=None), 'show': FieldInfo(annotation=SimpleShow, required=True), 'track': FieldInfo(annotation=Literal[False], required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Local items

Some playlists contain locally stored tracks. They contain mostly None values along with empty lists and dictionaries.

class tekore.model.LocalPlaylistTrack(*, id, href, name, type, uri, album, artists, available_markets=None, disc_number, duration_ms, explicit, external_ids, external_urls, is_local, popularity, preview_url, track_number, episode=False, track=True)

Bases: LocalTrack

Local track on a playlist.

Provides episode and track booleans to easily determine the type of playlist item.

Parameters:
  • id (None) –

  • href (None) –

  • name (str) –

  • type (str) –

  • uri (str) –

  • album (LocalAlbum) –

  • artists (List[LocalArtist]) –

  • available_markets (List[None]) –

  • disc_number (int) –

  • duration_ms (int | dict) –

  • explicit (bool) –

  • external_ids (dict) –

  • external_urls (dict) –

  • is_local (Literal[True]) –

  • popularity (int) –

  • preview_url (None) –

  • track_number (int) –

  • episode (Literal[False]) –

  • track (Literal[True]) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album': FieldInfo(annotation=LocalAlbum, required=True), 'artists': FieldInfo(annotation=List[LocalArtist], required=True), 'available_markets': FieldInfo(annotation=List[NoneType], required=False, default_factory=list), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=Union[int, dict], required=True), 'episode': FieldInfo(annotation=Literal[False], required=False, default=False), 'explicit': FieldInfo(annotation=bool, required=True), 'external_ids': FieldInfo(annotation=dict, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=NoneType, required=True), 'id': FieldInfo(annotation=NoneType, required=True), 'is_local': FieldInfo(annotation=Literal[True], required=True), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'preview_url': FieldInfo(annotation=NoneType, required=True), 'track': FieldInfo(annotation=Literal[True], required=False, default=True), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.LocalItem(*, id, href, name, type, uri)

Bases: Model

Base for local items.

Parameters:
  • id (None) –

  • href (None) –

  • name (str) –

  • type (str) –

  • uri (None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=NoneType, required=True), 'id': FieldInfo(annotation=NoneType, required=True), 'name': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=NoneType, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.LocalAlbum(*, id, href, name, type, uri, album_type, artists, available_markets=None, external_urls, images, release_date, release_date_precision)

Bases: LocalItem

Album of a locally saved track.

Parameters:
  • id (None) –

  • href (None) –

  • name (str) –

  • type (str) –

  • uri (None) –

  • album_type (None) –

  • artists (List[None]) –

  • available_markets (List[None]) –

  • external_urls (dict) –

  • images (List[None]) –

  • release_date (None) –

  • release_date_precision (None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album_type': FieldInfo(annotation=NoneType, required=True), 'artists': FieldInfo(annotation=List[NoneType], required=True), 'available_markets': FieldInfo(annotation=List[NoneType], required=False, default_factory=list), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=NoneType, required=True), 'id': FieldInfo(annotation=NoneType, required=True), 'images': FieldInfo(annotation=List[NoneType], required=True), 'name': FieldInfo(annotation=str, required=True), 'release_date': FieldInfo(annotation=NoneType, required=True), 'release_date_precision': FieldInfo(annotation=NoneType, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=NoneType, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.LocalArtist(*, id, href, name, type, uri, external_urls)

Bases: LocalItem

Artist of a locally saved track.

Parameters:
  • id (None) –

  • href (None) –

  • name (str) –

  • type (str) –

  • uri (None) –

  • external_urls (dict) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=NoneType, required=True), 'id': FieldInfo(annotation=NoneType, required=True), 'name': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=NoneType, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.LocalTrack(*, id, href, name, type, uri, album, artists, available_markets=None, disc_number, duration_ms, explicit, external_ids, external_urls, is_local, popularity, preview_url, track_number)

Bases: LocalItem

Locally saved track.

Locally saved track where most attributes are always None, empty, zero or False. duration_ms being an object is undocumented.

Parameters:
  • id (None) –

  • href (None) –

  • name (str) –

  • type (str) –

  • uri (str) –

  • album (LocalAlbum) –

  • artists (List[LocalArtist]) –

  • available_markets (List[None]) –

  • disc_number (int) –

  • duration_ms (int | dict) –

  • explicit (bool) –

  • external_ids (dict) –

  • external_urls (dict) –

  • is_local (Literal[True]) –

  • popularity (int) –

  • preview_url (None) –

  • track_number (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album': FieldInfo(annotation=LocalAlbum, required=True), 'artists': FieldInfo(annotation=List[LocalArtist], required=True), 'available_markets': FieldInfo(annotation=List[NoneType], required=False, default_factory=list), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=Union[int, dict], required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_ids': FieldInfo(annotation=dict, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=NoneType, required=True), 'id': FieldInfo(annotation=NoneType, required=True), 'is_local': FieldInfo(annotation=Literal[True], required=True), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'preview_url': FieldInfo(annotation=NoneType, required=True), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Recommendation

Recommendations

Track recommendations.

RecommendationSeed

Recommendation seeds.

RecommendationAttribute

Attributes available in recommendations.

class tekore.model.Recommendations(*, seeds, tracks)

Bases: Model

Track recommendations.

Parameters:
  • seeds (List[RecommendationSeed]) –

  • tracks (List[FullTrack]) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'seeds': FieldInfo(annotation=List[RecommendationSeed], required=True), 'tracks': FieldInfo(annotation=List[FullTrack], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.RecommendationSeed(*, id, afterFilteringSize, afterRelinkingSize, href, initialPoolSize, type)

Bases: Identifiable

Recommendation seeds.

Parameters:
  • id (str) –

  • afterFilteringSize (int) –

  • afterRelinkingSize (int) –

  • href (str | None) –

  • initialPoolSize (int) –

  • type (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'afterFilteringSize': FieldInfo(annotation=int, required=True), 'afterRelinkingSize': FieldInfo(annotation=int, required=True), 'href': FieldInfo(annotation=Union[str, NoneType], required=True), 'id': FieldInfo(annotation=str, required=True), 'initialPoolSize': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.RecommendationAttribute(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Attributes available in recommendations.

acousticness = 'acousticness'
danceability = 'danceability'
duration_ms = 'duration_ms'
energy = 'energy'
instrumentalness = 'instrumentalness'
key = 'key'
liveness = 'liveness'
loudness = 'loudness'
mode = 'mode'
popularity = 'popularity'
speechiness = 'speechiness'
tempo = 'tempo'
time_signature = 'time_signature'
valence = 'valence'
Show

Show

Show base.

SimpleShow

Simplified show object.

SimpleShowPaging

Paging of simplified shows.

FullShow

Complete show object.

SavedShow

Show saved in library.

SavedShowPaging

Paging of shows in library.

class tekore.model.Show(*, id, href, type, uri, available_markets=None, copyrights, description, explicit, external_urls, html_description=None, images, is_externally_hosted, languages, media_type, name, publisher, total_episodes=None)

Bases: Item

Show base.

publisher being an object is undocumented.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • available_markets (List[str]) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str | None) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • publisher (str | dict) –

  • total_episodes (int | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'available_markets': FieldInfo(annotation=List[str], required=False, default_factory=list), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=True), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'publisher': FieldInfo(annotation=Union[str, dict], required=True), 'total_episodes': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleShow(*, id, href, type, uri, available_markets=None, copyrights, description, explicit, external_urls, html_description=None, images, is_externally_hosted, languages, media_type, name, publisher, total_episodes=None)

Bases: Show

Simplified show object.

total_episodes is undocumented by Spotify, so it might be missing or removed in a future version.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • available_markets (List[str]) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str | None) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • publisher (str | dict) –

  • total_episodes (int | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'available_markets': FieldInfo(annotation=List[str], required=False, default_factory=list), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=True), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'publisher': FieldInfo(annotation=Union[str, dict], required=True), 'total_episodes': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleShowPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified shows.

Parameters:
  • href (str) –

  • items (List[SimpleShow]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleShow], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullShow(*, id, href, type, uri, available_markets=None, copyrights, description, explicit, external_urls, html_description=None, images, is_externally_hosted, languages, media_type, name, publisher, total_episodes=None, episodes=None)

Bases: Show

Complete show object.

total_episodes is undocumented by Spotify, so it might be missing or removed in a future version.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • available_markets (List[str]) –

  • copyrights (List[Copyright]) –

  • description (str) –

  • explicit (bool) –

  • external_urls (dict) –

  • html_description (str | None) –

  • images (List[Image]) –

  • is_externally_hosted (bool | None) –

  • languages (List[str]) –

  • media_type (str) –

  • name (str) –

  • publisher (str | dict) –

  • total_episodes (int | None) –

  • episodes (SimpleEpisodePaging | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'available_markets': FieldInfo(annotation=List[str], required=False, default_factory=list), 'copyrights': FieldInfo(annotation=List[Copyright], required=True), 'description': FieldInfo(annotation=str, required=True), 'episodes': FieldInfo(annotation=Union[SimpleEpisodePaging, NoneType], required=False, default=None), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'html_description': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=List[Image], required=True), 'is_externally_hosted': FieldInfo(annotation=Union[bool, NoneType], required=True), 'languages': FieldInfo(annotation=List[str], required=True), 'media_type': FieldInfo(annotation=str, required=True), 'name': FieldInfo(annotation=str, required=True), 'publisher': FieldInfo(annotation=Union[str, dict], required=True), 'total_episodes': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedShow(*, added_at, show)

Bases: Model

Show saved in library.

Parameters:
  • added_at (datetime) –

  • show (SimpleShow) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'added_at': FieldInfo(annotation=datetime, required=True), 'show': FieldInfo(annotation=SimpleShow, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedShowPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of shows in library.

Parameters:
  • href (str) –

  • items (List[SavedShow]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SavedShow], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Track

Track

Track base.

SimpleTrack

Simplified track object.

SimpleTrackPaging

Paging of simplified tracks.

FullTrack

Complete track object.

FullTrackPaging

Paging of full tracks.

SavedTrack

Track saved to library.

SavedTrackPaging

Paging of tracks in library.

Tracks

Minimal representation of playlist tracks.

TrackLink

Relinked track.

Restrictions

Restrictions on relinked resource.

AudioAnalysis

Track audio analysis.

TimeInterval

Generic representation of an interval.

Section

Analysis of a track's section.

Segment

Analysis of a track's segment.

AudioFeatures

Features of a track.

class tekore.model.Track(*, id, href, type, uri, artists, available_markets=None, disc_number, duration_ms, explicit, external_urls, is_local, is_playable=None, linked_from=None, name, preview_url, restrictions=None, track_number)

Bases: Item

Track base.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • artists (List[SimpleArtist]) –

  • available_markets (List[str] | None) –

  • disc_number (int) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • is_local (bool) –

  • is_playable (bool | None) –

  • linked_from (TrackLink | None) –

  • name (str) –

  • preview_url (str | None) –

  • restrictions (Restrictions | None) –

  • track_number (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'is_local': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'linked_from': FieldInfo(annotation=Union[TrackLink, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleTrack(*, id, href, type, uri, artists, available_markets=None, disc_number, duration_ms, explicit, external_urls, is_local, is_playable=None, linked_from=None, name, preview_url, restrictions=None, track_number)

Bases: Track

Simplified track object.

When market is specified, available_markets is not available. is_playable is not available when market is not specified. restrictions is available if restrictions have been placed on the track, making it unplayable.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • artists (List[SimpleArtist]) –

  • available_markets (List[str] | None) –

  • disc_number (int) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • is_local (bool) –

  • is_playable (bool | None) –

  • linked_from (TrackLink | None) –

  • name (str) –

  • preview_url (str | None) –

  • restrictions (Restrictions | None) –

  • track_number (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'is_local': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'linked_from': FieldInfo(annotation=Union[TrackLink, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SimpleTrackPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of simplified tracks.

Parameters:
  • href (str) –

  • items (List[SimpleTrack]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SimpleTrack], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullTrack(*, id, href, type, uri, artists, available_markets=None, disc_number, duration_ms, explicit, external_urls, is_local, is_playable=None, linked_from=None, name, preview_url, restrictions=None, track_number, album, external_ids, popularity)

Bases: Track

Complete track object.

When market is specified, available_markets is not available. is_playable is not available when market is not specified. restrictions is available if restrictions have been placed on the track, making it unplayable.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • artists (List[SimpleArtist]) –

  • available_markets (List[str] | None) –

  • disc_number (int) –

  • duration_ms (int) –

  • explicit (bool) –

  • external_urls (dict) –

  • is_local (bool) –

  • is_playable (bool | None) –

  • linked_from (TrackLink | None) –

  • name (str) –

  • preview_url (str | None) –

  • restrictions (Restrictions | None) –

  • track_number (int) –

  • album (SimpleAlbum) –

  • external_ids (dict) –

  • popularity (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'album': FieldInfo(annotation=SimpleAlbum, required=True), 'artists': FieldInfo(annotation=List[SimpleArtist], required=True), 'available_markets': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'disc_number': FieldInfo(annotation=int, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'explicit': FieldInfo(annotation=bool, required=True), 'external_ids': FieldInfo(annotation=dict, required=True), 'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'is_local': FieldInfo(annotation=bool, required=True), 'is_playable': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'linked_from': FieldInfo(annotation=Union[TrackLink, NoneType], required=False, default=None), 'name': FieldInfo(annotation=str, required=True), 'popularity': FieldInfo(annotation=int, required=True), 'preview_url': FieldInfo(annotation=Union[str, NoneType], required=True), 'restrictions': FieldInfo(annotation=Union[Restrictions, NoneType], required=False, default=None), 'track_number': FieldInfo(annotation=int, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.FullTrackPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of full tracks.

Parameters:
  • href (str) –

  • items (List[FullTrack]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[FullTrack], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedTrack(*, added_at, track)

Bases: Model

Track saved to library.

Parameters:
  • added_at (datetime) –

  • track (FullTrack) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'added_at': FieldInfo(annotation=datetime, required=True), 'track': FieldInfo(annotation=FullTrack, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.SavedTrackPaging(*, href, items, limit, next, total, offset, previous)

Bases: OffsetPaging

Paging of tracks in library.

Parameters:
  • href (str) –

  • items (List[SavedTrack]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=List[SavedTrack], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Tracks(*, href, total)

Bases: Model

Minimal representation of playlist tracks.

Parameters:
  • href (str) –

  • total (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.TrackLink(*, id, href, type, uri, external_urls)

Bases: Item

Relinked track.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'external_urls': FieldInfo(annotation=dict, required=True), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Restrictions(*, reason)

Bases: Model

Restrictions on relinked resource.

Parameters:

reason (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'reason': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Audio analysis
class tekore.model.AudioAnalysis(*, bars, beats, sections, segments, tatums, meta, track)

Bases: Model

Track audio analysis.

See the Web API documentation for more details.

Parameters:
  • bars (List[TimeInterval]) –

  • beats (List[TimeInterval]) –

  • sections (List[Section]) –

  • segments (List[Segment]) –

  • tatums (List[TimeInterval]) –

  • meta (dict) –

  • track (dict) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'bars': FieldInfo(annotation=List[TimeInterval], required=True), 'beats': FieldInfo(annotation=List[TimeInterval], required=True), 'meta': FieldInfo(annotation=dict, required=True), 'sections': FieldInfo(annotation=List[Section], required=True), 'segments': FieldInfo(annotation=List[Segment], required=True), 'tatums': FieldInfo(annotation=List[TimeInterval], required=True), 'track': FieldInfo(annotation=dict, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.TimeInterval(*, duration, start=None, confidence=None)

Bases: Model

Generic representation of an interval.

Attributes are sometimes not available.

Parameters:
  • duration (float) –

  • start (float | None) –

  • confidence (float | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'confidence': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'duration': FieldInfo(annotation=float, required=True), 'start': FieldInfo(annotation=Union[float, NoneType], required=False, default=None)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Section(*, duration, loudness, tempo, tempo_confidence, key_confidence, mode_confidence, time_signature, time_signature_confidence, confidence=None, mode=None, key=None, start=None)

Bases: Model

Analysis of a track’s section.

Attributes are sometimes not available.

Parameters:
  • duration (float) –

  • loudness (float) –

  • tempo (float) –

  • tempo_confidence (float) –

  • key_confidence (float) –

  • mode_confidence (float) –

  • time_signature (int) –

  • time_signature_confidence (float) –

  • confidence (float | None) –

  • mode (int | None) –

  • key (int | None) –

  • start (float | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'confidence': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'duration': FieldInfo(annotation=float, required=True), 'key': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'key_confidence': FieldInfo(annotation=float, required=True), 'loudness': FieldInfo(annotation=float, required=True), 'mode': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'mode_confidence': FieldInfo(annotation=float, required=True), 'start': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'tempo': FieldInfo(annotation=float, required=True), 'tempo_confidence': FieldInfo(annotation=float, required=True), 'time_signature': FieldInfo(annotation=int, required=True), 'time_signature_confidence': FieldInfo(annotation=float, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Segment(*, duration, loudness_start, loudness_max, pitches, timbre, confidence=None, loudness_end=None, loudness_max_time=None, start=None)

Bases: Model

Analysis of a track’s segment.

Attributes are sometimes not available.

Parameters:
  • duration (float) –

  • loudness_start (float) –

  • loudness_max (float) –

  • pitches (List[float]) –

  • timbre (List[float]) –

  • confidence (float | None) –

  • loudness_end (float | None) –

  • loudness_max_time (float | None) –

  • start (float | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'confidence': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'duration': FieldInfo(annotation=float, required=True), 'loudness_end': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'loudness_max': FieldInfo(annotation=float, required=True), 'loudness_max_time': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'loudness_start': FieldInfo(annotation=float, required=True), 'pitches': FieldInfo(annotation=List[float], required=True), 'start': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'timbre': FieldInfo(annotation=List[float], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Audio features
class tekore.model.AudioFeatures(*, id, acousticness, analysis_url, danceability, duration_ms, energy, instrumentalness, key, liveness, loudness, mode, speechiness, tempo, time_signature, track_href, type, uri, valence)

Bases: Identifiable

Features of a track.

Parameters:
  • id (str) –

  • acousticness (float) –

  • analysis_url (str) –

  • danceability (float) –

  • duration_ms (int) –

  • energy (float) –

  • instrumentalness (float) –

  • key (int) –

  • liveness (float) –

  • loudness (float) –

  • mode (int) –

  • speechiness (float) –

  • tempo (float) –

  • time_signature (int) –

  • track_href (str) –

  • type (str) –

  • uri (str) –

  • valence (float) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'acousticness': FieldInfo(annotation=float, required=True), 'analysis_url': FieldInfo(annotation=str, required=True), 'danceability': FieldInfo(annotation=float, required=True), 'duration_ms': FieldInfo(annotation=int, required=True), 'energy': FieldInfo(annotation=float, required=True), 'id': FieldInfo(annotation=str, required=True), 'instrumentalness': FieldInfo(annotation=float, required=True), 'key': FieldInfo(annotation=int, required=True), 'liveness': FieldInfo(annotation=float, required=True), 'loudness': FieldInfo(annotation=float, required=True), 'mode': FieldInfo(annotation=int, required=True), 'speechiness': FieldInfo(annotation=float, required=True), 'tempo': FieldInfo(annotation=float, required=True), 'time_signature': FieldInfo(annotation=int, required=True), 'track_href': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True), 'valence': FieldInfo(annotation=float, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

User

User

User base.

PublicUser

User as viewable by anyone.

PrivateUser

User with private information.

ExplicitContent

Explicit content filter of a user.

class tekore.model.User(*, id, href, type, uri, external_urls, display_name=None, followers=None, images=None)

Bases: Item

User base.

display_name, followers and images may not be available.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • display_name (str | None) –

  • followers (Followers | None) –

  • images (List[Image] | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'display_name': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'external_urls': FieldInfo(annotation=dict, required=True), 'followers': FieldInfo(annotation=Union[Followers, NoneType], required=False, default=None), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PublicUser(*, id, href, type, uri, external_urls, display_name=None, followers=None, images=None)

Bases: User

User as viewable by anyone.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • display_name (str | None) –

  • followers (Followers | None) –

  • images (List[Image] | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'display_name': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'external_urls': FieldInfo(annotation=dict, required=True), 'followers': FieldInfo(annotation=Union[Followers, NoneType], required=False, default=None), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.PrivateUser(*, id, href, type, uri, external_urls, display_name=None, followers=None, images=None, country=None, email=None, explicit_content=None, product=None, birthday=None)

Bases: User

User with private information.

country, explicit_content and product require the user-read-private scope. email requires the user-read-email scope. birthday is unavailable unless the now-invalid user-read-birthdate scope was granted to the token.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

  • external_urls (dict) –

  • display_name (str | None) –

  • followers (Followers | None) –

  • images (List[Image] | None) –

  • country (str | None) –

  • email (str | None) –

  • explicit_content (ExplicitContent | None) –

  • product (str | None) –

  • birthday (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'birthday': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'country': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'display_name': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'email': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'explicit_content': FieldInfo(annotation=Union[ExplicitContent, NoneType], required=False, default=None), 'external_urls': FieldInfo(annotation=dict, required=True), 'followers': FieldInfo(annotation=Union[Followers, NoneType], required=False, default=None), 'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'images': FieldInfo(annotation=Union[List[Image], NoneType], required=False, default=None), 'product': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.ExplicitContent(*, filter_enabled, filter_locked)

Bases: Model

Explicit content filter of a user.

Parameters:
  • filter_enabled (bool) –

  • filter_locked (bool) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'filter_enabled': FieldInfo(annotation=bool, required=True), 'filter_locked': FieldInfo(annotation=bool, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Miscellaneous

Copyright

Copyright.

Followers

Followers.

Image

Image link and information.

ReleaseDatePrecision

Precision of a release date.

class tekore.model.Copyright(*, text, type)

Bases: Model

Copyright.

Parameters:
  • text (str) –

  • type (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'text': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Followers(*, href, total)

Bases: Model

Followers.

href is always None.

Parameters:
  • href (None) –

  • total (int) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=NoneType, required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Image(*, url, height, width)

Bases: Model

Image link and information.

The Web API documentation reports that height and width can be None or not available in the response.

Parameters:
  • url (str) –

  • height (int | None) –

  • width (int | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'height': FieldInfo(annotation=Union[int, NoneType], required=True), 'url': FieldInfo(annotation=str, required=True), 'width': FieldInfo(annotation=Union[int, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.ReleaseDatePrecision(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: StrEnum

Precision of a release date.

day = 'day'
minute = 'minute'
month = 'month'
year = 'year'
Model bases

Model

Response model base.

UnknownModelAttributeWarning

The response model contains an unknown attribute.

Identifiable

Object identified with a Spotify ID.

Item

Identifiable with additional fields.

Paging

Paging base.

OffsetPaging

Offset paging base.

Cursor

Data cursor.

CursorPaging

Cursor paging base.

Functionality
class tekore.model.Model

Bases: BaseModel

Response model base.

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.UnknownModelAttributeWarning

Bases: RuntimeWarning

The response model contains an unknown attribute.

Models
class tekore.model.Identifiable(*, id)

Bases: Model

Object identified with a Spotify ID.

Parameters:

id (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'id': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Item(*, id, href, type, uri)

Bases: Identifiable

Identifiable with additional fields.

Parameters:
  • id (str) –

  • href (str) –

  • type (str) –

  • uri (str) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'uri': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Paging(*, href, items, limit, next)

Bases: Model

Paging base.

Parameters:
  • href (str) –

  • items (Sequence[Model]) –

  • limit (int) –

  • next (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=Sequence[Model], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.OffsetPaging(*, href, items, limit, next, total, offset, previous)

Bases: Paging

Offset paging base.

Paging that can be navigated both forward and back.

Parameters:
  • href (str) –

  • items (Sequence[Model]) –

  • limit (int) –

  • next (str | None) –

  • total (int) –

  • offset (int) –

  • previous (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=Sequence[Model], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True), 'offset': FieldInfo(annotation=int, required=True), 'previous': FieldInfo(annotation=Union[str, NoneType], required=True), 'total': FieldInfo(annotation=int, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.Cursor(*, after)

Bases: Model

Data cursor.

Parameters:

after (str | None) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'after': FieldInfo(annotation=Union[str, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

class tekore.model.CursorPaging(*, href, items, limit, next, cursors)

Bases: Paging

Cursor paging base.

Paging that can be navigated only forward following the cursor.

Parameters:
  • href (str) –

  • items (Sequence[Model]) –

  • limit (int) –

  • next (str | None) –

  • cursors (Cursor) –

model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'cursors': FieldInfo(annotation=Cursor, required=True), 'href': FieldInfo(annotation=str, required=True), 'items': FieldInfo(annotation=Sequence[Model], required=True), 'limit': FieldInfo(annotation=int, required=True), 'next': FieldInfo(annotation=Union[str, NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

Member types
class tekore.model.StrEnum(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: str, Enum

Convert enumeration members to strings using their name.

Ignores case when getting items. This does not change values.

Senders

Manipulate the way clients send requests.

SyncSender

Send requests synchronously.

AsyncSender

Send requests asynchronously.

RetryingSender

Retry requests if unsuccessful.

CachingSender

Cache successful GET requests.

See also Other classes.

Senders provide a hook between defining a request and sending it to the Web API. The sender of a Client also determines whether synchronous or asynchronous calls are used to send requests and process responses.

Sender instances are passed to a client at initialisation.

import tekore as tk

tk.Credentials(*conf, sender=tk.SyncSender())
tk.Spotify(sender=tk.RetryingSender())

Senders wrap around the httpx library and accept additional keyword arguments to httpx.Client().

proxies = {
    'http': 'http://10.10.10.10:8000',
    'https': 'http://10.10.10.10:8000',
}
tk.SyncSender(proxies=proxies)

Instances of httpx.Client or httpx.AsyncClient can also be passed in for a finer control over sender behaviour.

from httpx import Client

client = Client(proxies=proxies)
tk.SyncSender(client)
Concrete senders

Final senders in a possible chain that concretely make the request to Spotify.

class tekore.SyncSender(client=None)

Bases: Sender

Send requests synchronously.

Warning

The underlying client is not closed automatically. Use sender.client.close() to close it, particularly if multiple senders are instantiated.

Parameters:

client (Client) – httpx.Client to use when sending requests

close()

Close the underlying synchronous client.

Return type:

None

property is_async: bool

Sender asynchronicity, always False.

send(request)

Send request with httpx.Client.

Parameters:

request (Request) –

Return type:

Response

class tekore.AsyncSender(client=None)

Bases: Sender

Send requests asynchronously.

Warning

The underlying client is not closed automatically. Use await sender.client.aclose() to close it, particularly if multiple senders are instantiated.

Parameters:

client (AsyncClient) – httpx.AsyncClient to use when sending requests

async close()

Close the underlying asynchronous client.

Return type:

None

property is_async: bool

Sender asynchronicity, always True.

async send(request)

Send request with httpx.AsyncClient.

Parameters:

request (Request) –

Return type:

Response

Extending senders

Senders that extend the functionality of other senders.

class tekore.RetryingSender(retries=0, sender=None)

Bases: ExtendingSender

Retry requests if unsuccessful.

On server errors the set amount of retries are used to resend requests. On TooManyRequests the Retry-After header is checked and used to wait before requesting again.

Note

Even when the number of retries is set to zero, retries based on rate limiting are still performed.

Parameters:
  • retries (int) – maximum number of retries on server errors before giving up

  • sender (Sender) – request sender, SyncSender if not specified

Examples

Use for only rate limiting by leaving the retry count to zero.

tk.RetryingSender()

Pass the maximum number of retries to retry failed requests.

tk.RetryingSender(retries=3)
send(request)

Delegate request to underlying sender and retry if failed.

Parameters:

request (Request) –

Return type:

Response | Coroutine[None, None, Response]

class tekore.CachingSender(max_size=None, sender=None)

Bases: ExtendingSender

Cache successful GET requests.

The Web API provides response headers for caching. Resources are cached based on Cache-Control, ETag and Vary headers. Thus CachingSender can be used with user tokens too. Resources marked as private, errors and Vary: * are not cached.

When using asynchronous senders, the cache is protected with asyncio.Lock to prevent concurrent access. The lock is instantiated on the first asynchronous call, so using only one asyncio.run() (per sender) is advised.

Note that if the cache has no maximum size it can grow without limit. Use CachingSender.clear() to empty the cache.

Parameters:
  • max_size (int) – maximum cache size (amount of responses), if specified the least recently used response is discarded when the cache would overflow

  • sender (Sender) – request sender, SyncSender if not specified

clear()

Clear sender cache.

Return type:

None

property max_size: int | None

Maximum amount of requests stored in the cache.

Returns:

maximum cache size

Return type:

Optional[int]

send(request)

Maybe load request from cache, or delegate to underlying sender.

Parameters:

request (Request) –

Return type:

Response | Coroutine[None, None, Response]

Other classes

Bases for subclassing or other endeavours.

Sender

Sender interface for requests.

ExtendingSender

Base class for senders that extend other senders.

SenderConflictWarning

Sender arguments to a client are in conflict.

Client

Base class for clients.

Request

Wrapper for parameters of a HTTP request.

Response

Wrapper for result of a HTTP request.

class tekore.Sender

Bases: ABC

Sender interface for requests.

abstract close()

Close underlying client.

Return type:

None | Coroutine[None, None, None]

abstract property is_async: bool

Sender asynchronicity mode.

abstract send(request)

Send a request.

Parameters:

request (Request) – request to send

Returns:

resulting response

Return type:

Response

class tekore.ExtendingSender(sender)

Bases: Sender

Base class for senders that extend other senders.

Parameters:

sender (Sender | None) – request sender, SyncSender if not specified

close()

Close the underlying sender.

To close synchronous senders, call close(). To close asynchronous senders, await close().

Return type:

None | Coroutine[None, None, None]

property is_async: bool

Sender asynchronicity, delegated to the underlying sender.

class tekore.SenderConflictWarning

Bases: RuntimeWarning

Sender arguments to a client are in conflict.

class tekore.Client(sender, asynchronous=None)

Bases: ExtendingSender

Base class for clients.

Parameters:
  • sender (Sender | None) – request sender - If not specified, a SyncSender is used

  • asynchronous (bool) – synchronicity requirement - If specified, overrides passed sender if they are in conflict and instantiates a sender of the requested type

send(request)

Send request with underlying sender.

Parameters:

request (Request) –

Return type:

Response | Coroutine[None, None, Response]

class tekore.Request(method, url, params=None, headers=None, data=None, json=None, content=None)

Wrapper for parameters of a HTTP request.

Parameters:
  • method (str) –

  • url (str) –

  • params (dict | None) –

  • headers (dict | None) –

  • data (dict | None) –

  • json (dict | None) –

  • content (str | None) –

class tekore.Response(url, headers, status_code, content)

Wrapper for result of a HTTP request.

Parameters:
  • url (str) –

  • headers (dict) –

  • status_code (int) –

  • content (dict | None) –

Getting started

To use the Web API, you’ll need to register an application. From its page retrieve the client ID and secret. They are your application’s credentials to the API. A walkthrough of creating an application and setting it up can be found here.

Retrieving a client token

First we’ll retrieve a client token for the Spotify application. It is a token associated with your application and can be used to make basic calls to the API.

import tekore as tk

client_id = 'your_id_here'
client_secret = 'your_secret_here'

app_token = tk.request_client_token(client_id, client_secret)

Calling the API

Next the Spotify object should be created. The following script will list the track numbers and names of songs on an album given the album ID.

spotify = tk.Spotify(app_token)

album = spotify.album('3RBULTZJ97bvVzZLpxcB0j')
for track in album.tracks.items:
    print(track.track_number, track.name)

Response attributes can be directly accessed with dot notation as above. To quickly inspect a response or any part of it, print its contents.

print(album)
print(album.artists[0])

Retrieving a user token

Many endpoints require user authorisation, for which another type of access token is needed. User tokens are associated with a Spotify user account.

Retrieving them requires some more setting up. A redirect URI should be whitelisted in application settings. It is the address to which users are redirected after authorising the application. Alternatively, the default redirect URI https://example.com/callback can be used with a client with no other redirect URIs whitelisted.

Different privileges or scopes can be requested when authorising. Below we’ll retrieve a token that has every possible scope. The script will open a web page prompting for a Spotify login. The user is then redirected back to the whitelisted redirect URI. Paste the redirected URI in full to the shell to finalise token retrieval.

redirect_uri = 'your_uri_here'

user_token = tk.prompt_for_user_token(
    client_id,
    client_secret,
    redirect_uri,
    scope=tk.scope.every
)

Note

prompt_for_user_token() eliminates the need for a web server, which would normally be used to complete authorisation, by requesting the user to manually enter information to the shell. However, that also makes it unusable on a server. Other authorisation methods are introduced in Authorisation guide.

Calling the API as a user

The following script replaces the application token with a user token and lists some of the user’s most listened tracks.

spotify.token = user_token

tracks = spotify.current_user_top_tracks(limit=10)
for track in tracks.items:
    print(track.name)

The snippet below will play Sibelius’ Finlandia if the user has a recently used Spotify application open. If no active device is found, an error is thrown.

finlandia = '3hHWhvw2hjwfngWcFjIzqr'
spotify.playback_start_tracks([finlandia])

Saving the configuration

Currently, we need to go through the authorisation process every time the script is run. Let’s save the configuration to avoid this in the future.

conf = (client_id, client_secret, redirect_uri, user_token.refresh_token)
tk.config_to_file('tekore.cfg', conf)

Now we can replace the authorisation lines with reconstructing the token.

conf = tk.config_from_file('tekore.cfg', return_refresh=True)
user_token = tk.refresh_user_token(*conf[:2], conf[3])

Note

This approach is not scalable to multi-user scenarios. See Authorisation guide for more information.

How to read the documentation

The reference documentation is built for easy navigation. Each endpoint (like playback) contains a description, required and optional scopes, arguments and return information. Notably, the return type often contains a link to the relevant response model. Follow them to discover the attributes that a model has. Further links can be followed down the model hierarchy.

What’s next?

Our Authorisation guide details different authorisation options. Advanced usage provides an overview of things to keep in mind when building an actual application and what Tekore has to offer for that. You could also have a look at some example scripts to start familiarising yourself with the Web API.

Authorisation guide

Introduction

Spotify uses access tokens to authorise requests to the Web API. They are strings of characters and ordinarily expire in an hour. There are two levels of authorisation: client and user authorisation, which yield different types of tokens and grant access to different parts of the API. Client tokens, also referred to as application tokens can be used to access generic endpoints, like retrieving albums or tracks. User tokens are required for endpoints that involve a specific user, like accessing saved tracks or manipulating playback, but they also grant access to generic endpoints.

The appropriate authorisation method for an application depends on the intended number of users. Simple scripts can be created relatively easily. Running a web server requires more effort, but it allows an application to serve any number of users. For examples of authorisation with application and user tokens in both server and scripting contexts, see the following:

  • Async server - server using application tokens

  • Authenticating server - server using user tokens

  • Creating local scripts

See Authorisation for reference documentation and Spotify’s authorisation guide for more information about the underlying authentication procedures.

User authorisation

User authorisation involves a two-step process.

  • A user opens or is redirected to a specific URL in their browser and logs in to Spotify to authorise the application. Then the user is redirected to a set redirect URI with the address containing a code.

  • An access token is requested with the code from the redirection result.

This redirection and extraction of data is carried out with a web server. See this recipe on an Authenticating server for an example implementation. Spinning up a server can be replaced with some manual work, e.g. the user pasting information to a terminal. For example prompt_for_user_token() uses this manual way, but this also makes it unusable on a server.

Refreshing and saving tokens

Access tokens expire after an hour. Their expiration status can be determined via Token.is_expiring. How another valid token is obtained depends on the type of the token. Application tokens can simply be requested again. To avoid another authorisation when using user tokens, a refresh_token can be used to request new access tokens.

A refresh token is enough to request new user access tokens, making them a perfect candidate to save to a file or a database. It is valid until the user manually revokes it from Spotify. A new refresh token may also be returned when requesting a new access token.

In practice receiving new refresh tokens seems to be rare, but it is a possibility as laid out in the OAuth 2 specification. The spec states that if it does happen the old refresh token may be revoked, so the new recent token should be used instead. The most recent refresh token is always returned when refreshing a token, but any saved refresh tokens need to be updated too.

A means of saving and loading refresh tokens is provided in Configuration.

Token scopes

The scope of a user token represents its access rights to various endpoints. By default, tokens cannot be used, for example, to modify a user’s library. Scopes are set during user authorisation, and requesting additional scopes requires another authorisation.

However, the scope of a token can be gradually expanded across a number of authorisations. When authorising, the scope of the resulting token is determined by the scopes that were set at the start of authorisation. But when refreshing a token, the scope is expanded to cover all scopes that the user has granted a particular application in previous authorisations. This holds for refresh tokens of both new and previously generated tokens.

PKCE user authorisation

User authorisation can be performed using Proof Key for Code Exchange, an extension to ordinary user authorisation. It is intended for public clients, i.e. applications whose client secret might be exposed. As such, retrieving user tokens with it does not require a client secret.

However, PKCE introduces some restrictions to refreshing user tokens. The refresh token of a PKCE-authorised token can only be used to spawn the next token, after which it is invalidated, which means that if saved, the refresh token needs to be constantly updated to match the newest available refresh token. Still, all access tokens are valid for their full duration.

Security concerns

There are two main security concerns with authorisation, besides the obvious leaking of access tokens.

Using a state with user authorisation prevents cross-site request forgery (RFC 6749). A string can be sent as state on authorisation. When a user is redirected back, the same state should be returned as a query parameter. gen_state() is provided to generate random strings to send as state. parse_state_from_url() can then be used to extract the returned state. State is generated and checked automatically when using UserAuth.

A client secret might not always be safe and the redirect URI handler might be vulnerable (RFC 7636). The PKCE extension to user authorisation allows retrieving user tokens without a client secret, and provides an added layer of security with code challenges and verifiers. Challenges and verifiers are handled automatically when using UserAuth with PKCE enabled. Here’s a summary of the configuration requirements for each authorisation method.

Method

Client ID

Client secret

Redirect URI

Client authorisation

x

x

User authorisation

x

x

x

User token refresh

x

x

PKCE user authorisation

x

x

PKCE token refresh

x

Summary of authorisation methods

Authorisation methods are provided on three levels for both application and user access tokens. At the lowest level Credentials provides ordinary access tokens. RefreshingCredentials has similar functionality, but returns self-refreshing tokens. Finally, utility functions that wrap around RefreshingCredentials are provided for easy one-time authorisation.

A summary of all authorisation methods can be found below. See each method’s documentation in Authorisation for more details. References to classes Credentials and RefreshingCredentials are abbreviated as C and RC for brevity.

  • Creation: is the method for generating new tokens or refreshing them

  • Type: is the resulting token expiring or automatically refreshing

Application tokens

Creation

Type

Notes

Method

New

Expiring

C.request_client_token

Refresh

Expiring

1

C.refresh

New

Refreshing

RC.request_client_token

New

Refreshing

2

request_client_token()

User tokens

There are two variants of each user authorisation method. One uses ordinary OAuth 2 authorisation, the other uses the PKCE extension.

Creation

Type

Notes

Ordinary

PKCE variant

New

Expiring

3

C.request_user_token

C.request_pkce_token

Refresh

Expiring

C.refresh_user_token

C.request_pkce_token

Refresh

Expiring

1

C.refresh

C.refresh

New

Refreshing

3

RC.request_user_token

RC.request_pkce_token

Refresh

Refreshing

RC.refresh_user_token

RC.refresh_pkce_token

New

Refreshing

2, 4

prompt_for_user_token()

prompt_for_pkce_token()

Refresh

Refreshing

2

refresh_user_token()

refresh_pkce_token()

UserAuth can be used to simplify the implementation of user authorisation with either one of the credentials clients, with or without PKCE.

Notes

  1. This is a subject-agnostic refresh, fit for both app and user tokens with or without PKCE. For application tokens, a new token is returned.

  2. These functions wrap around RefreshingCredentials internally to provide a shorthand for one-off authorisation.

  3. These methods are paired with the first step of user authorisation: redirecting the user to a URL to login with Spotify.

  4. Requires manually pasting text to a terminal, is not usable on a server.

Authorising requests

The client provides two ways of authorising requests. Firstly an access token is accepted in the client’s constructor.

import tekore as tk

s = tk.Spotify(token)
a = s.artist(artist_id)

Secondly, the client can temporarily use another token for requests. This is particularly handy if only one client instance is created but there are many users, or if tokens are manually refreshed.

s = tk.Spotify(app_token)
a = s.artist(artist_id)

with s.token_as(user_token):
    user = s.current_user()

Advanced usage

Client options

The client provides options and toggles to customise behavior in a variety of ways.

Maximise limits

The Web API limits the number of resources returned in many endpoints. By default, these limits are below their maximum values, matching API defaults. However, they can be maximised when instantiating a client or as a context.

import tekore as tk

spotify = tk.Spotify(max_limits_on=True)
spotify.max_limits_on = False

with spotify.max_limits():
    tracks = spotify.all_items(spotify.search('piano')[0])
Chunked requests

Endpoints that accept lists of resources often limit the amount of items that can be passed in. To help with this restriction, those lists can be chunked.

spotify = tk.Spotify(chunked_on=True)
spotify.chunked_on = False

with spotify.chunked():
    pass  # Go nuts with e.g. spotify.artists_follow

Application configuration

It is generally advisable to separate configuration from code, and more importantly keep secrets outside public version control. To facilitate that, environment variables and configuration files can be used to provide application credentials. Set values in your environment or write a configuration file, then read the configuration.

export SPOTIFY_CLIENT_ID=your_id
export SPOTIFY_CLIENT_SECRET=your_secret
export SPOTIFY_REDIRECT_URI=your_uri
[DEFAULT]
SPOTIFY_CLIENT_ID=your_id
SPOTIFY_CLIENT_SECRET=your_secret
SPOTIFY_REDIRECT_URI=your_uri
client_id, client_secret, redirect_uri = tk.config_from_environment()
client_id, client_secret, redirect_uri = tk.config_from_file(filename)

These values can then be used to retrieve access tokens. Note that if all configuration values are defined, it is possible to use unpacking to provide the configuration.

conf = tk.config_from_environment()
cred = tk.Credentials(*conf)

Configuring a user refresh token is also possible. Define SPOTIFY_USER_REFRESH and pass in a boolean flag to read it as a fourth configuration value.

tk.config_from_environment(return_refresh=True)

Configuration files can be written too. This is handy if a user’s refresh token needs to be stored.

tk.config_to_file(filename, (id_, secret, uri, refresh))

For more information see Configuration.

Customising request behavior

By default Tekore doesn’t do anything clever when sending requests. Its functionality, however, can be extended in a number of ways using different kinds of senders. Builtin senders can be used for retrying and caching.

Keepalive connections, retries and caching make up a performance-boosting and convenient setup, easily constructed from simple building blocks. Less errors, less requests and faster responses, particularly for busy applications that request the same static resources repeatedly.

sender = tk.CachingSender(
    max_size=256,
    sender=tk.RetryingSender(retries=2)
)

tk.Spotify(sender=sender)

At the lowest level, SyncSender and AsyncSender accept httpx.Client instances which can further customise behavior. For example, setting longer request timeouts and retrying on connection errors is possible with the following setup.

import httpx

trans = httpx.HTTPTransport(retries=3)
client = httpx.Client(timeout=30, transport=trans)
sender = tk.SyncSender(client=client)

With an async sender use httpx.AsyncClient and httpx.AsyncHTTPTransport instead.

Traversing paging objects

Many Web API endpoints that would return a large number of the same type of object return paging objects for performance reasons. The client defines a few ways to navigate these pagings. Next and previous pages can be requested one at a time.

import tekore as tk

spotify = tk.Spotify(token)
items = spotify.playlist_items('37i9dQZEVXbMDoHDwVN2tF', limit=10)
t_next = spotify.next(items)
t_prev = spotify.previous(t_next)

To retrieve the whole content additional methods are available.

pages = spotify.all_pages(items)
items = spotify.all_items(items)

Async support

Tekore provides support for asynchronous programming with async-await. Async mode may be enabled when instantiating a Client.

tk.Credentials(*conf, asynchronous=True)
tk.Spotify(token, asynchronous=True)

Alternatively, an asynchronous sender may be passed directly into a client.

spotify = tk.Spotify(token, sender=tk.AsyncSender())

Note

The boolean parameter above overrides any conflicting sender that is set as default or simultaneously passed in to the client.

Now every call to an endpoint returns an awaitable instead of a response. asyncio can then be used to execute asynchronous requests. See Senders and Examples for more information.

import asyncio

async def now_playing():
    return await spotify.playback_currently_playing()

np = asyncio.run(now_playing())

Asynchronous execution can also be used for quick bursts of calls when combined with asyncio.gather(). See Scrape playlist artists for an example.

While asynchronous Credentials is supported, it is worth considering that concurrently refreshing tokens may lead to multiple refreshes for one token. Synchronous credentials clients are recommended.

Note

Client context managers are async safe, meaning that they can be used in many tasks without affecting the state of other tasks. However, setting values outside of all contexts modifies the persistent value directly, and as such may affect other tasks.

Dynamic scoping

Gradually expanding token scopes and methods that “know” their associated scopes can be used to dynamically expand user scopes in a web application. Unauthorised carries the scope information from failing calls and can then be used to redirect users to authorise again.

@app.get("/endpoint")
def endpoint():
    try:
        spotify.playback()
    except tk.Unauthorised as e:
        return Redirect("/login?scope=" + str(e.scope))

Combined with refreshing the token on arrival to have the full scope and additionally redirecting the user back after authorisation, even static HTML applications using a Python backend become simple to implement.

Short links

The Spotify emits shortened URLs for e.g. playlists when sharing them through the mobile application. Unfortunately, they are not usable as they are, but they can be expanded to their full form. Tekore provides two utilities for processing them: is_short_link() and Spotify.follow_short_link().

link = '...'
if is_short_link(link):
    link = spotify.follow_short_link(link)
type, id_ = from_url(link)

Localisation

Many API calls that retrieve track information accept a market or country parameter with which only tracks or albums available in that market are returned. This sometimes changes track IDs as well. When calling with a user token, this country code can also be from_token, in which case the results are for the user’s locale.

spotify.search('sheeran', market='SE')
spotify.search('horse', market='from_token')

In addition to returning results relevant to a specific market, results can be requested in specific languages. This is helpful for example in viewing names with non-latin alphabet.

from httpx import Client

client = Client(headers={'Accept-Language': 'ru'})
spotify = tk.Spotify(token, sender=tk.SyncSender(client))

artist = spotify.artist('2LbinT29RFLaXOGAN0jfQN')
print(artist.name)

Examples

Here are some things you could do with Tekore, enjoy!

Artist follower

This script will find all the artists you aren’t already following from your playlists, and prompt you to do so.

import tekore as tk

conf = tk.config_from_environment()
scope = [
    tk.scope.user_follow_read,
    tk.scope.user_follow_modify,
    tk.scope.playlist_read_private
]
user_token = tk.prompt_for_user_token(*conf, scope=scope)
s = tk.Spotify(user_token, max_limits_on=True, chunked_on=True)


def prompt_user(what: str) -> bool:
    while True:
        resp = input(f"{what} [Y/n]: ").strip()
        if resp.lower() == "y" or resp == "":
            return True
        elif resp.lower() == "n":
            return False


artists = set()
for playlist in s.all_items(s.followed_playlists()):
    if not prompt_user(f"Analyze playlist '{playlist.name}'?"):
        continue

    for item in s.all_items(s.playlist_items(playlist.id)):
        if not item.track.track or item.track.is_local:
            continue

        for artist in item.track.artists:
            artists.add((artist.id, artist.name))


ids = [a[0] for a in artists]
names = [a[1] for a in artists]
following = s.artists_is_following(ids)
for id_, name, status in zip(ids, names, following):
    if status:
        print(f"Skipping '{name}' as it's already being followed.")
        continue

    if not prompt_user(f"Follow '{name}'?"):
        continue

    s.artists_follow([id_])
    print(f"Followed '{name}'.")

Async server

The following scripts starts up a simple AIOHTTP web server for song search.

Run the script and navigate to localhost:5000 to see the main page. Enter search terms to the address bar to search for songs.

Asynchronous programming is particularly handy in web server contexts. This is a small example, but the bottleneck of this server is Spotify’s API. Therefore at larger scale more requests can be served using this configuration than the synchronous counterpart. To demonstrate this, one can insert an artificial delay to the search call. First import asyncio, then insert await asyncio.sleep(10) somewhere in the function. When searching in parallel (e.g. with two browser tabs) one should observe that requests take about ten seconds, no matter how many are sent at once.

import tekore as tk
from aiohttp import web

conf = tk.config_from_environment()
token = tk.request_client_token(*conf[:2])
spotify = tk.Spotify(token, asynchronous=True)

routes = web.RouteTableDef()

@routes.get('/')
async def main(_) -> web.Response:
    html = '<br>'.join([
        'Enter text in the address bar to search for songs!',
        'Search terms should be separated by plus signs.',
        'For example:',
        'host:5000/monty+python',
        'host:5000/bright+side',
    ])
    return web.Response(text=html, content_type='text/html')

@routes.get('/{search}')
async def search(request: web.Request) -> web.Response:
    # await asyncio.sleep(10)
    try:
        query = request.match_info['search'].replace('+', ' ')
        tracks, = await spotify.search(query, limit=5)
        items = [t.artists[0].name + ': ' + t.name for t in tracks.items]
        html = '<br>'.join(items)
        return web.Response(text=html, content_type='text/html')
    except Exception:
        return web.Response(text='An error occured while searching!')

app = web.Application()
app.add_routes(routes)
web.run_app(app, port=5000)

Authenticating server

The scripts below detail how to start up a simple authentication sever.

In this example the configured redirect URI must match http://localhost:5000/callback and be whitelisted in your application settings. Note that the server need not be accessible from the web. With a server that is hosted elsewhere or one that needs to be accessible from outside, whitelist another redirect URI that matches the server’s address.

Run the script and navigate to localhost:5000 to see your user ID. It should be None before logging in. During login you will be redirected to authenticate at Spotify. If access is granted and the state security check passes, another redirection via /callback to an info page will be performed. It should read “successful” and refer you back to the main page. You should now see a generated user ID and your currently playing track. The ID is saved to your session cookies and preserved during navigation. Logging out deletes the cookie and server-stored access token.

Note

The auths dictionary could be used to store arbitrary information. In this example it is used to map the state parameters of ongoing authorisations to UserAuth objects.

import tekore as tk

from flask import Flask, request, redirect, session

conf = tk.config_from_environment()
cred = tk.Credentials(*conf)
spotify = tk.Spotify()

auths = {}  # Ongoing authorisations: state -> UserAuth
users = {}  # User tokens: state -> token (use state as a user ID)

in_link = '<a href="/login">login</a>'
out_link = '<a href="/logout">logout</a>'
login_msg = f'You can {in_link} or {out_link}'


def app_factory() -> Flask:
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'aliens'

    @app.route('/', methods=['GET'])
    def main():
        user = session.get('user', None)
        token = users.get(user, None)

        # Return early if no login or old session
        if user is None or token is None:
            session.pop('user', None)
            return f'User ID: None<br>{login_msg}'

        page = f'User ID: {user}<br>{login_msg}'
        if token.is_expiring:
            token = cred.refresh(token)
            users[user] = token

        try:
            with spotify.token_as(token):
                playback = spotify.playback_currently_playing()

            item = playback.item.name if playback else None
            page += f'<br>Now playing: {item}'
        except tk.HTTPError:
            page += '<br>Error in retrieving now playing!'

        return page

    @app.route('/login', methods=['GET'])
    def login():
        if 'user' in session:
            return redirect('/', 307)

        scope = tk.scope.user_read_currently_playing
        auth = tk.UserAuth(cred, scope)
        auths[auth.state] = auth
        return redirect(auth.url, 307)

    @app.route('/callback', methods=['GET'])
    def login_callback():
        code = request.args.get('code', None)
        state = request.args.get('state', None)
        auth = auths.pop(state, None)

        if auth is None:
            return 'Invalid state!', 400

        token = auth.request_token(code, state)
        session['user'] = state
        users[state] = token
        return redirect('/', 307)

    @app.route('/logout', methods=['GET'])
    def logout():
        uid = session.pop('user', None)
        if uid is not None:
            users.pop(uid, None)
        return redirect('/', 307)

    return app


if __name__ == '__main__':
    application = app_factory()
    application.run('127.0.0.1', 5000)
import uvicorn
import tekore as tk
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse, HTMLResponse
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="secret_key_placeholder")

conf = tk.config_from_environment()
cred = tk.Credentials(*conf)
spotify = tk.Spotify()

auths = {}  # Ongoing authorisations: state -> UserAuth
users = {}  # User tokens: state -> token (use state as a user ID)

in_link = '<a href="/login">login</a>'
out_link = '<a href="/logout">logout</a>'
login_msg = f"You can {in_link} or {out_link}"


@app.get("/", response_class=HTMLResponse)
def read_root(request: Request):
    user = request.session.get("user", None)
    token = users.get(user, None)

    # Return early if no login or old session
    if user is None or token is None:
        request.session.pop("user", None)
        return f"User ID: None<br>{login_msg}"

    page = f"User ID: {user}<br>{login_msg}"
    if token.is_expiring:
        token = cred.refresh(token)
        users[user] = token

    try:
        with spotify.token_as(token):
            playback = spotify.playback_currently_playing()

        item = playback.item.name if playback else None
        page += f"<br>Now playing: {item}"
    except tk.HTTPError:
        page += "<br>Error in retrieving now playing!"

    return HTMLResponse(content=page, status_code=200)


@app.get("/login")
def login(request: Request):
    if "user" in request.session:
        return RedirectResponse(url="/")

    scope = tk.scope.user_read_currently_playing
    auth = tk.UserAuth(cred, scope)
    auths[auth.state] = auth
    return RedirectResponse(auth.url)


@app.get("/callback")
def login_callback(request: Request, code: str, state: str):
    auth = auths.pop(state, None)

    if auth is None:
        return "Invalid state!", 400

    token = auth.request_token(code, state)
    request.session["user"] = state
    users[state] = token
    return RedirectResponse("/")


@app.get("/logout")
def logout(request: Request):
    uid = request.session.pop("user", None)
    if uid is not None:
        users.pop(uid, None)
    return RedirectResponse("/")


if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        port=5000,
        host="0.0.0.0",
        reload=True,
    )

Creating local scripts

The examples below give a framework for creating scripts to be run locally.

First we specify our client configuration and authorise a user. Once complete, the configuration is written to a file, along with the user’s refresh token which can be used to spawn new tokens without authorising again.

Note

The code below uses the default redirect URI https://example.com/callback, which doesn’t need to be configured. However, if another redirect URI has been configured, it will not work.

import tekore as tk

client_id = 'your_client_id'
client_secret = 'your_client_secret'
redirect_uri = 'https://example.com/callback'   # Or your redirect uri
conf = (client_id, client_secret, redirect_uri)
file = 'tekore.cfg'

token = tk.prompt_for_user_token(*conf, scope=tk.scope.every)
tk.config_to_file(file, conf + (token.refresh_token,))

That file can then be read whenever a script is started, and a refreshed token requested without user interaction. Thanks to the self-refreshing tokens returned by refresh_user_token(), even long-running scripts can simply use the same token indefinitely.

Note

When only application tokens are needed, client_id and client_secret can be written to a file without initial authorisation.

import tekore as tk

file = 'tekore.cfg'
conf = tk.config_from_file(file, return_refresh=True)
token = tk.refresh_user_token(*conf[:2], conf[3])

spotify = tk.Spotify(token)
print(spotify.current_user_top_artists())

The same principle applies to configuration residing in the system environment, which can be accessed with config_from_environment(), though configuration cannot be written to environment variables. These snippets could also be combined to a single script checking for the existence of configuration or even just the refresh token to decide whether a new authorisation is needed or not.

Discord bot

The following example script creates a simple Discord bot that can be used to search tracks.

A bot account is required for sending messages in Discord. A quickstart guide for setting up a bot can be found here.

Once the bot is added to the server users can call it with the prefix >tk track. The bot responds to the query by sending a brief summary of the search results. Queries can be for example:

>tk track Sheeran
>tk track "Monty Python"
import tekore as tk

from discord import Game, Embed
from discord.ext import commands

token_discord = "your_discord_token"
conf = tk.config_from_environment()
token_spotify = tk.request_client_token(*conf[:2])

description = "Spotify track search bot using Tekore"
bot = commands.Bot(command_prefix='>tk ', description=description, activity=Game(name=">tk help"))
spotify = tk.Spotify(token_spotify, asynchronous=True)


@bot.command()
async def track(ctx, *, query: str = None):
    if query is None:
        await ctx.send("No search query specified")
        return

    tracks, = await spotify.search(query, limit=5)
    embed = Embed(title="Track search results", color=0x1DB954)
    embed.set_thumbnail(url="https://i.imgur.com/890YSn2.png")
    embed.set_footer(text="Requested by " + ctx.author.display_name)

    for t in tracks.items:
        artist = t.artists[0].name
        url = t.external_urls["spotify"]

        message = "\n".join([
            "[Spotify](" + url + ")",
            ":busts_in_silhouette: " + artist,
            ":cd: " + t.album.name
        ])
        embed.add_field(name=t.name, value=message, inline=False)

    await ctx.send(embed=embed)


@bot.event
async def on_ready():
    print("Ready to demo!")


if __name__ == "__main__":
    bot.run(token_discord)

Snippets

Here are some example snippets for various use cases. Fair warning, some of them perform (harmless and small) actions on your behalf.

Albums of user’s top artist

The following script shows the albums of one of your top artists.

It assumes that your credentials are saved in the environment and you have used Spotify enough to have top artists.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.user_top_read
token = tk.prompt_for_user_token(*conf, scope=scope)

spotify = tk.Spotify(token)
artist = spotify.current_user_top_artists(limit=1).items[0]
albums = spotify.artist_albums(artist.id)

print(f'Albums of {artist.name}:')
for a in albums.items:
    print(a.release_date, a.name)
Analyse track from playlist

The following script retrieves a track from one of your playlists and analyses its features.

It assumes that your credentials are saved in the environment and you have followed or created at least one playlist and it has a track on it. Podcast episodes or a locally saved tracks are ignored, because they cannot be analysed.

import tekore as tk

conf = tk.config_from_environment()
token = tk.prompt_for_user_token(*conf)

spotify = tk.Spotify(token)
playlist = spotify.followed_playlists(limit=1).items[0]
track = spotify.playlist_items(playlist.id, limit=1).items[0].track
name = f'"{track.name}" from {playlist.name}'

if track.episode:
    print(f'Cannot analyse episodes!\nGot {name}.')
elif track.track and track.is_local:
    print(f'Cannot analyse local tracks!\nGot {name}.')
else:
    print(f'Analysing {name}...\n')
    analysis = spotify.track_audio_features(track.id)
    print(repr(analysis))
Follow with a search

The following script searches for an artist and prompts the user to follow the first match.

It assumes that your credentials are saved in the environment.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.user_follow_modify
token = tk.prompt_for_user_token(*conf, scope=scope)
spotify = tk.Spotify(token)

search = input('Search for an artist: ')
artists, = spotify.search(search, types=('artist',), limit=1)
artist = artists.items[0]
follow = input(f'Follow {artist.name}? (y/n) ')

if follow.lower() == 'y':
    spotify.artists_follow([artist.id])
    print(f'{artist.name} followed.')
else:
    print('Follow cancelled.')
Follow category playlist

The following script retrieves a Spotify playlist in one of the preset categories and follows it as the current user.

It assumes that your credentials are saved in the environment.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.playlist_modify_private
token = tk.prompt_for_user_token(*conf, scope=scope)

spotify = tk.Spotify(token)
category = spotify.categories(limit=1).items[0]
playlist = spotify.category_playlists(category.id, limit=1).items[0]

print(f'Following "{playlist.name}"" from category "{category.name}"...')
spotify.playlist_follow(playlist.id, public=False)
Play saved album

The following script plays one of your saved albums.

It assumes that your credentials are saved in the environment, there is at least one album saved in your library, and you have an active Spotify application open.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.user_library_read + tk.scope.user_modify_playback_state
token = tk.prompt_for_user_token(*conf, scope=scope)

spotify = tk.Spotify(token)
album = spotify.saved_albums(limit=1).items[0].album
album_uri = tk.to_uri('album', album.id)
spotify.playback_start_context(album_uri)
Recommended tracks playlist

The following script recommends songs based on your top tracks and creates a playlist from them.

It assumes that your credentials are saved in the environment and you have used Spotify enough to have top tracks.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.user_top_read + tk.scope.playlist_modify_private
token = tk.prompt_for_user_token(*conf, scope=scope)

spotify = tk.Spotify(token)
top_tracks = spotify.current_user_top_tracks(limit=5).items
top_track_ids = [t.id for t in top_tracks]
recommendations = spotify.recommendations(track_ids=top_track_ids).tracks

user = spotify.current_user()
playlist = spotify.playlist_create(
    user.id,
    'Tekore Recommendations',
    public=False,
    description='Recommendations based on your top tracks <3'
)
uris = [t.uri for t in recommendations]
spotify.playlist_add(playlist.id, uris=uris)
Related artists of user’s top artist

The following script shows artists related to one of your top artists and whether you have followed them or not.

It assumes that your credentials are saved in the environment and you have used Spotify enough to have top artists.

import tekore as tk

conf = tk.config_from_environment()
scope = tk.scope.user_top_read + tk.scope.user_follow_read
token = tk.prompt_for_user_token(*conf, scope=scope)

spotify = tk.Spotify(token)
artist = spotify.current_user_top_artists(limit=1).items[0]
related = spotify.artist_related_artists(artist.id)
followed = spotify.followed_artists(limit=50)
followed = spotify.all_items(followed)
followed_ids = [f.id for f in followed]

print(f'Artists related to {artist.name}:')
for a in related:
    f = ' F -' if a.id in followed_ids else 'NF -'
    print(f, a.name)
Scrape playlist artists

The following script retrieves all tracks of your playlists and aggregates the most common artists by total track count.

It assumes that your credentials are saved in the environment and you have followed or created at least one playlist. For this example, the artist of podcast episodes is the name of the show. To avoid errors when hitting rate limits, a RetryingSender is used.

import asyncio
import tekore as tk

from collections import Counter

conf = tk.config_from_environment()
scope = tk.scope.playlist_read_private
token = tk.prompt_for_user_token(*conf, scope=scope)


def get_artist(track) -> str:
    if track.episode:
        return track.show.name
    else:
        return track.artists[0].name

Asynchronous functions can be used to achieve a faster runtime when making lots of parallelisable calls to the API. A series of asynchronous calls alone does not help much, but the calls can be parallelised with asyncio.gather(). The code is more complex, but with bigger workloads more time is saved.

async def count_artists(spotify: tk.Spotify, playlist_id: str):
    tracks = await spotify.playlist_items(playlist_id)
    tracks = spotify.all_items(tracks)
    return Counter([get_artist(t.track) async for t in tracks])


async def main() -> Counter:
    sender = tk.RetryingSender(sender=tk.AsyncSender())
    spotify = tk.Spotify(token, sender=sender, max_limits_on=True)

    playlists = await spotify.followed_playlists()
    playlists = spotify.all_items(playlists)
    counts = [await count_artists(spotify, p.id) async for p in playlists]

    await spotify.close()
    return sum(counts, Counter())


artists = asyncio.run(main())

for name, count in artists.most_common(3):
    print(count, name)
async def count_artists(spotify: tk.Spotify, playlist_id: str):
    tracks_paging = await spotify.playlist_items(playlist_id)
    paging_len = len(tracks_paging.items)
    track_calls = [
        spotify.playlist_items(playlist_id, offset=ofs)
        for ofs in range(paging_len, tracks_paging.total, paging_len)
    ]
    pages = [tracks_paging] + await asyncio.gather(*track_calls)
    tracks = sum([page.items for page in pages], [])
    return Counter([get_artist(t.track) for t in tracks])


async def main() -> Counter:
    sender = tk.RetryingSender(sender=tk.AsyncSender())
    spotify = tk.Spotify(token, sender=sender, max_limits_on=True)

    playlists_paging = await spotify.followed_playlists()
    paging_len = len(playlists_paging.items)
    playlist_calls = [
        spotify.followed_playlists(offset=ofs)
        for ofs in range(paging_len, playlists_paging.total, paging_len)
    ]
    pages = [playlists_paging] + await asyncio.gather(*playlist_calls)
    playlists = sum([page.items for page in pages], [])

    count_calls = [count_artists(spotify, p.id) for p in playlists]
    counts = await asyncio.gather(*count_calls)

    await spotify.close()
    return sum(counts, Counter())


artists = asyncio.run(main())

for name, count in artists.most_common(3):
    print(count, name)
Tracks of a new release

The following script shows the tracks of a newly released album.

It assumes that your credentials are saved in the environment and there is at least new release in the Spotify catalogue.

import tekore as tk

conf = tk.config_from_environment()
token = tk.request_client_token(*conf[:2])

spotify = tk.Spotify(token)
simple_album = spotify.new_releases(limit=1).items[0]
album = spotify.album(simple_album.id)

print(f'Songs from {album.album_type} {album.name}:')
for t in album.tracks.items:
    print(t.track_number, '-', t.name)

Additional resources

This documentation covers the most important aspects of the Web API, but for in-depth tutorials, guides and reference, Spotify’s own resources are strongly recommended.

  • App Settings - walkthrough of creating and configuring an application

  • Authorisation Guide - detailed view of authorisation methods

  • Web API Reference - reference documentation of all endpoints

  • Track Relinking Guide - about track availability within markets and how tracks are relinked to user markets

  • Working With Playlists - playlist semantics and properties

  • Local Files in Spotify Playlists


© Copyright 2019-2023, Felix Hildén. Revision 98d40481.

Built with Sphinx using a theme provided by Read the Docs.
Read the Docs v: latest
Versions
latest
stable
Downloads
html
On Read the Docs
Project Home
Builds