discogs-sdk
discogs-sdk is a modern Python client for the Discogs API covering every documented v2 endpoint, with a fluent chainable syntax and built-in response caching.
from discogs_sdk import Discogs
with Discogs() as client:
release = client.releases.get(352665)
print(f"{release.title} ({release.year})")
for result in client.search(query="Nine Inch Nails", type="artist"):
print(result.title)
Installation
pip install discogs-sdk
# or
uv add discogs-sdk
Requires Python 3.10+.
Features
- Complete Endpoint Coverage — Every route in the Discogs API v2 documentation
- Fluent API — Chain sub-resources naturally:
client.releases.get(id).rating.get() - Lazy Loading — No HTTP calls until you actually need the data
- Effortless Pagination — Browse results without managing pages or offsets
- Rate Limit Aware — Bounded retries with
Retry-Aftersupport, the live budget onclient.ratelimit, and a structured event per request - Built-in Caching — Optional TTL-based caching reduces API calls
- Flexible Auth — Supports personal tokens, consumer key/secret, or full OAuth 1.0a
- Type Safe — Get autocomplete and IDE support
- Async & Sync — Full support for both synchronous and asynchronous workflows
How it compares
The established alternative is python3-discogs-client. Both load data lazily, paginate automatically, support OAuth 1.0a and back off on HTTP 429, so the differences that matter are:
| discogs-sdk | python3-discogs-client | |
|---|---|---|
| Documented v2 API coverage | Complete | Partial † |
| Sync and async | Both | Sync only |
| Responses | Typed Pydantic models | Untyped attributes |
| Response cache | In-memory or SQLite | None |
† No inventory export or upload, release ratings or have/want stats, collection fields, folder creation, contributions or submissions.
Coming from python3-discogs-client? The migration guide maps each of its idioms to discogs-sdk and names the behaviour that differs.
Quick start
Create a personal access token in your Discogs developer settings and export it; the client reads it from the environment (other auth modes):
export DISCOGS_TOKEN="your-token-here"
from discogs_sdk import Discogs
with Discogs() as client:
release = client.releases.get(352665) # no request yet
print(release.title) # the GET is sent here → "The Downward Spiral"
for rel in client.artists.get(3857).releases.list(sort="year"):
print(f"{rel.title} ({rel.year})")
The async client has the same API. Await a lazy resource to fetch it, and iterate pages with async for:
import asyncio
from discogs_sdk import AsyncDiscogs
async def main():
async with AsyncDiscogs() as client:
release = await client.releases.get(352665)
print(release.title)
async for result in client.search(query="Nine Inch Nails", type="artist"):
print(result.title)
asyncio.run(main())
Behaviors to know
- Lazy loading.
.get()sends no request; the first data attribute access does, and that is whereNotFoundErroris raised. More - Async.
awaita proxy for its model, which has no sub-resources, so keep the proxy to chain and useasync foron pages. More - Pagination cost.
search()and paginated.list()methods fetch one page per GET as you iterate, each against the rate limit unless the cache serves it, sobreakonce you have enough. More - Retries. Reads retry on rate limits and transient failures, honouring
Retry-Afterup to 60 seconds. More - Writes. A mutation is never replayed after an ambiguous failure, so settle its outcome by reading, never by resubmitting. More
- Caching. Off by default; only
GET/HEADresponses are cached, and a successful write clears the cache. More - Field names. Models use Python names for cryptic API keys (
catalog_numberforcatno), andmodel.rawkeeps what Discogs sent. More
Design principles
The SDK does more than relay requests: it pages, retries, renames keys and parses timestamps. These rules say what it may and may not do, and every new feature is checked against them.
- Requests stay predictable. A low-level method sends only the requests its documentation names. Combining calls
is fine; the method documents the cost, and every call is observable through
on_request. Auto-paging sends one GET per page as you iterate (cost), a lazy proxy sends one GET on its first data access (lazy loading), and a retried read emits oneRequestEventwhoseattemptscounts every send (observability). - Data may be normalized (names, types, timestamps, URLs) when nothing is lost, because
rawkeeps the original; when the result is computed from the response alone; and when the normalization is documented. Examples are renamed keys, timestamps parsed into awaredatetimes and the absoluteurion search results. A guess the SDK cannot avoid is measured, documented and checkable throughraw, like theAmerica/Los_Angeleszone it assumes for a timestamp sent without an offset. - Data is never made up. The SDK invents no value that the API did not send or that cannot be computed from what
it sent, such as a guessed currency. A timestamp it cannot parse becomes
None, not an estimate. - Helpers may combine calls for common use cases. They send requests only through the SDK's public methods, so
the cache, retries and
on_requestapply to them as to any other call. - Defaults are safe, sane and unsurprising. In particular, helpers don't cap their effort by default. A caller who wants a bound passes one, and a capped result says whether it is complete.
- Undocumented behaviour is quarantined. A helper that depends on API behaviour Discogs does not document lives in
discogs_sdk.experimental. It documents that dependency and its consequences, such as whether its result can be trusted to be complete; it raises when it detects that the behaviour clearly no longer holds; and it may change or be removed without a deprecation period. Core methods never depend on undocumented behaviour.
Experimental helpers
discogs_sdk.experimental holds helpers that depend on API behaviour Discogs does not document, under the
rules above: each may change or be removed without a deprecation period.
releases_added_since() answers a question the API has no filter for: which releases matching a search were added to
Discogs on or after a date. It takes a Discogs client; async_releases_added_since() takes an AsyncDiscogs one.
from datetime import date
from discogs_sdk import Discogs
from discogs_sdk.experimental import releases_added_since
with Discogs() as client:
result = releases_added_since(client, date(2026, 10, 1), artist="Nine Inch Nails")
for release in result.releases: # newest first
print(release.date_added, release.title)
print(result.complete) # False only when max_requests stopped the scan
- Completeness assumes an undocumented sort. The helper searches with
sort="date_added"andsort_order="desc", which Discogs answers newest-added first without documenting it, and looks up each result to read itsdate_added, stopping at the first release added before the date. Every release it returns has a verified date, but a newer release that Discogs sorts after an older one is never seen. Adate_addednewer than the one before it shows the sort is not holding and raisesDiscogsError. - Cost. One search request per page scanned, plus one lookup per release in the window, plus one for the release
that ends the scan (none when the results run out first). A release without a
date_addedis skipped and costs a lookup too. Cache hits cost nothing, and each retry is one more send. - No cap by default.
max_requests=Nbounds the HTTP sends, retries included. Once the sends reach it the helper returns what it found withcomplete=False; a request already started may still retry, so the total can exceedNby up toclient.max_retries.
A date means the start of that day in UTC; a datetime must be timezone-aware. type may only be "release", and
sort, sort_order and page belong to the helper: passing them raises ValueError before any request.
Documentation
The full guide lives in docs/:
- Getting started — behaviors to know, installation, fetching resources, search, sub-resources, async usage
- Pagination — what a page costs, stopping early, fetching one page, page attributes
- Authentication — personal token, consumer key/secret, OAuth 1.0a, credential precedence
- Collection and marketplace — folders, instances, wantlist, listings, orders
- Errors and retries — rate limits, retries, settling an ambiguous write, the exception hierarchy
- Configuration — every client parameter
- Caching — the response cache,
no_cache(),cache_only(), custom backends - Observability —
client.ratelimitandon_requestevents - Concurrency — sharing a client across threads and tasks
- Custom HTTP clients — injecting an
httpx2client - Models — field naming, search result links, timestamps, original API values
- Migrating from python3-discogs-client — idiom-by-idiom translation and behaviour differences
- Endpoint map — which SDK call reaches which Discogs endpoint, with verbs, access chains and return types
- AI coding agents — installing the packaged agent skill, and a rules snippet for other agents
Examples
The examples/ directory has runnable scripts for every feature:
quickstart.py— first requests, lazy loading, searchauthentication.py— all auth modes including OAuth 1.0adatabase.py— releases, artists, masters, labels, searchmarketplace.py— listings, orders, fees, inventorycollection.py— folders, instances, fields, wantlistasync_usage.py— async client with await and async foradvanced.py— error handling, caching, custom HTTP clients, exports
Contributing
Contributions are welcome. See CONTRIBUTING.md for guidelines.
License
discogs-sdk is licensed under the Apache License 2.0.
Metadata
Release files for discogs-sdk 0.8.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| discogs_sdk-0.8.1.tar.gz | 118.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| discogs_sdk-0.8.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 266.7 kB
Release files / discogs_sdk-0.8.1.tar.gz
| Download URL | discogs_sdk-0.8.1.tar.gz |
|---|---|
| Size | 118.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c97f8fbdd844668ee7975f8268cb21ac6095d203cb88c31e04798644d70a207c
|
|
BLAKE2b-256 checksum How to use checksums |
b5e477f11b67da5b5a2d45b577e15f87efe6d620d866d5e33ebe04f48b87add9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / discogs_sdk-0.8.1-py3-none-any.whl
| Download URL | discogs_sdk-0.8.1-py3-none-any.whl |
|---|---|
| Size | 148.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1eb1aaa55e06632916005b6aa87684c74714334364766c02e341ac56873a36a0
|
|
BLAKE2b-256 checksum How to use checksums |
06f1c9feab78e595d24813156c415668a15d53eac61b5fe5847eb4b59613f587
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency log