A Symfony Bundle providing a complete, type-safe API client for Discogs.com v2.0 API with OAuth 1.0a authentication support.
- ✅ Complete API coverage: Database (artists, releases, labels, masters), User Collections, Wantlists, Marketplace (listings, orders)
- ✅ Dual authentication: User-Token (simple) and OAuth 1.0a (full flow)
- ✅ Value-object models with proper types and immutability
- ✅ Pagination support with iterator interface
- ✅ Rate limit handling and automatic retry with exponential backoff
- ✅ Optional PSR-6 caching with per-endpoint TTL
- ✅ Event dispatching for extensibility (logging, monitoring)
- ✅ Symfony 8.0+ compatible, follows bundle best practices
- ✅ Comprehensive error handling with typed exceptions
composer require tamas-gere/discogs-api-bundleFor Symfony Flex projects (recommended): the bundle is auto-registered.
For manual registration, add to config/bundles.php:
return [
// ...
DiscogsApiBundle\DiscogsApiBundle::class => ['all' => true],
];Copy configuration to your project:
cp vendor/tamas-gere/discogs-api-bundle/config/packages/discogs_api.yaml config/packages/discogs_api.yamlEdit config/packages/discogs_api.yaml:
discogs_api:
user_agent: 'MyApp/1.0' # Required - unique identifier for your app
# Option 1: Personal token (simplest)
user_token:
token: '%env(DISCOGS_USER_TOKEN)%'
# Option 2: OAuth 1.0a (for multi-user apps)
# oauth1:
# consumer_key: '%env(DISCOGS_CONSUMER_KEY)%'
# consumer_secret: '%env(DISCOGS_CONSUMER_SECRET)%'Get your credentials from Discogs Developer Settings.
Set environment variables in .env:
# Option 1 - Simple token
DISCOGS_USER_TOKEN=your_user_token_here
# Option 2 - OAuth app credentials
DISCOGS_CONSUMER_KEY=your_consumer_key
DISCOGS_CONSUMER_SECRET=your_consumer_secret// In controller or service
public function __construct(private DiscogsClient $discogsClient) {}
// Get a release
$release = $this->discogsClient->getRelease(12345);
echo $release->title;
// Search
$results = $this->discogsClient->search('Daft Punk', [
'type' => 'artist',
'per_page' => 10,
]);
foreach ($results as $result) {
echo $result->title;
}
// Get your identity
$user = $this->discogsClient->getIdentity();
// Add to collection
$this->discogsClient->addToCollection('your_username', $releaseId, folderId: 0, rating: 5, notes: 'Want this');// Step 1: Redirect user to authorization
$authUrl = $this->generateUrl('discogs_api_oauth_request_token');
// Or call OAuthController::requestToken() directly
// User redirected to Discogs, authorizes app
// Step 2: Handle callback at route "discogs_api_oauth_callback"
// Controller receives token and verifier, exchanges for access token
// Store token credentials in database (user_id -> token, secret)
// Step 3: Configure with stored tokens
// In config/services.yaml:
discogs_api:
oauth1:
consumer_key: '%env(DISCOGS_CONSUMER_KEY)%'
consumer_secret: '%env(DISCOGS_CONSUMER_SECRET)%'
token: '%user.discogs_token%' # stored per-user token
token_secret: '%user.discogs_token_secret%'
// Now all API calls act on behalf of that user
$collection = $discogsClient->getCollection($username);Enable routes in config/routes.yaml:
discogs_api_oauth:
resource: '@DiscogsApiBundle/Controller/OAuthController.php'
type: annotationRoutes:
GET /oauth/request-token- Get request token & redirect to DiscogsGET /oauth/callback- OAuth callback from DiscogsGET /oauth/token- Inspect current session's tokenPOST /oauth/logout- Clear OAuth session
Inject any service individually:
use DiscogsApiBundle\Service\ArtistService;
public function __construct(private ArtistService $artistService) {}
$artist = $this->artistService->getArtist(12345);
$releases = $this->artistService->getArtistReleases(12345, ['per_page' => 20]);Available services:
ArtistService- artists, artist releases, search (artists)ReleaseService- releases, ratings, statsMasterService- master releases, versionsLabelService- labels, label releasesUserService- user identity, user profilesCollectionService- folders, collection CRUD, ratingsWantlistService- wantlist CRUDMarketplaceService- listings CRUD, orders, messagesInventoryService- inventory listings CRUDOrderService- order management, messagesSearchService- full-text search across all types
List methods return PaginatedResponse:
$paginated = $discogsClient->getCollection('username');
foreach ($paginated as $item) {
// Iterate items
}
echo $paginated->count(); // items on current page
echo $paginated->getPage(); // current page
echo $paginated->getPages(); // total pages
if ($paginated->hasNextPage()) {
$page = $paginated->getNextPage();
}All API errors throw exceptions:
use DiscogsApiBundle\Exception\{
DiscogsApiException,
RateLimitException,
AuthenticationException,
NotFoundException,
ValidationException
};
try {
$release = $discogsClient->getRelease(999999999);
} catch (NotFoundException $e) {
// 404 - resource not found
} catch (AuthenticationException $e) {
// 401/403 - check your token/keys
} catch (RateLimitException $e) {
// 429 - rate limit exceeded
$retryAfter = $e->getRetryAfter();
} catch (DiscogsApiException $e) {
// other API errors
}Enable caching in config:
discogs_api:
cache:
enabled: true
pool: 'cache.app' # any PSR-6 pool
ttl:
artists: 86400 # 24h
releases: 43200 # 12hCache keys are auto-purged on write operations (add/remove collection, create/update listing).
Subscribe to events for logging/metrics:
use DiscogsApiBundle\Event\{
RequestBeforeEvent,
ResponseEvent,
ErrorEvent,
RateLimitEvent
};
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LoggingSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'discogs_api.request.before' => 'onRequestBefore',
'discogs_api.request.after' => 'onRequestAfter',
'discogs_api.error' => 'onError',
'discogs_api.rate_limit.exceeded' => 'onRateLimit',
];
}
public function onRequestBefore(RequestBeforeEvent $event): void
{
// Log outgoing request
}
public function onError(ErrorEvent $event): void
{
// Log errors
}
}vendor/bin/phpunit tests/UnitBy default, live tests are skipped. To run:
# Using env var
DISCOGS_TEST_LIVE=1 vendor/bin/phpunit tests/Integration
# Or with --live flag (if using custom test runner)
vendor/bin/phpunit --group live --liveConfiguration for live tests in phpunit.xml.dist:
<php>
<env name="DISCOGS_USER_TOKEN" value="test_token_here"/>
<env name="DISCOGS_CONSUMER_KEY" value="key"/>
<env name="DISCOGS_CONSUMER_SECRET" value="secret"/>
</php>Discogs enforces rate limits:
- Unauthenticated: 60 requests/minute
- Authenticated: 5000 requests/minute
The bundle automatically tracks X-Ratelimit-Remaining and can retry on 429 with retry_on_rate_limit: true. Configure max_retries for resilience.
Contributions welcome! Please read CONTRIBUTING.md for details.
MIT License. See LICENSE file.