EPO BDDS Go Client

A complete Go client library for the European Patent Office Bulk Data Distribution Service (BDDS).
Getting Started
Authentication
According to EPO, authentication via their CIAM system is mandatory for accessing the Bulk Data Distribution Service. However, in practice:
- Product/Delivery Listing: Requires authentication (subscription needed)
- File Downloads: May work without authentication (observed in testing)
For EPO BDDS subscription and official information:
This library supports both authenticated and unauthenticated usage. Provide credentials for full API access, or omit them for download-only usage (where supported).
Installation
go get github.com/patent-dev/epo-bdds
Quick Start
package main
import (
"context"
"fmt"
"log"
"github.com/patent-dev/epo-bdds"
)
func main() {
// Create client
client, err := bdds.NewClient(&bdds.Config{
Username: "your-epo-username",
Password: "your-epo-password",
})
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// List all products
products, err := client.ListProducts(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d products\n", len(products))
for _, p := range products {
fmt.Printf("- [%d] %s\n", p.ID, p.Name)
}
}
API Methods
Product Discovery
// List all available products
ListProducts(ctx context.Context) ([]*Product, error)
// Get product details with deliveries
GetProduct(ctx context.Context, productID int) (*ProductWithDeliveries, error)
// Find product by name
GetProductByName(ctx context.Context, name string) (*Product, error)
// Get most recent delivery for a product
GetLatestDelivery(ctx context.Context, productID int) (*Delivery, error)
File Downloads
// Download file to writer
DownloadFile(ctx context.Context, productID, deliveryID, fileID int, dst io.Writer) error
// Download with progress callback
DownloadFileWithProgress(ctx context.Context, productID, deliveryID, fileID int,
dst io.Writer, progressFn func(bytesWritten, totalBytes int64)) error
Configuration
config := &bdds.Config{
Username: "your-username", // Required
Password: "your-password", // Required
BaseURL: "https://publication-bdds.apps.epo.org", // Default
UserAgent: "YourApp/1.0", // Optional
MaxRetries: 3, // Default: 3
RetryDelay: 1, // Seconds between retries, default: 1
Timeout: 30, // Request timeout in seconds, default: 30
}
client, err := bdds.NewClient(config)
Features
Automatic Token Management
- OAuth2 authentication handled automatically
- Tokens cached and refreshed before expiry
- No manual token management required
Robust Error Handling
- Automatic retry with exponential backoff
- Graceful handling of rate limits
- Custom error types for different scenarios
Progress Tracking
- Download progress callbacks for large files
- Real-time byte tracking during downloads
Usage Examples
Download Without Authentication
If you only need to download files and already have the product/delivery/file IDs:
// Create client without credentials
client, err := bdds.NewClient(&bdds.Config{})
if err != nil {
log.Fatal(err)
}
// Download file directly
file, err := os.Create("download.zip")
if err != nil {
log.Fatal(err)
}
defer file.Close()
err = client.DownloadFile(ctx, productID, deliveryID, fileID, file)
if err != nil {
log.Fatal(err)
}
List Products
products, err := client.ListProducts(ctx)
if err != nil {
log.Fatal(err)
}
for _, p := range products {
fmt.Printf("Product %d: %s\n", p.ID, p.Name)
fmt.Printf(" %s\n", p.Description)
}
Get Product with Deliveries
product, err := client.GetProduct(ctx, 3) // EP DocDB front file
if err != nil {
log.Fatal(err)
}
fmt.Printf("Product: %s\n", product.Name)
fmt.Printf("Deliveries: %d\n", len(product.Deliveries))
for _, delivery := range product.Deliveries {
fmt.Printf(" %s - %d files\n", delivery.DeliveryName, len(delivery.Files))
}
Download File with Progress
file, err := os.Create("download.zip")
if err != nil {
log.Fatal(err)
}
defer file.Close()
err = client.DownloadFileWithProgress(ctx, 3, 12345, 67890, file,
func(bytesWritten, totalBytes int64) {
percent := float64(bytesWritten) * 100 / float64(totalBytes)
fmt.Printf("\rProgress: %.1f%%", percent)
})
if err != nil {
log.Fatal(err)
}
Find Product by Name
product, err := client.GetProductByName(ctx, "EP DocDB front file")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found product: %d - %s\n", product.ID, product.Name)
Get Latest Delivery
delivery, err := client.GetLatestDelivery(ctx, 3)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Latest delivery: %s\n", delivery.DeliveryName)
fmt.Printf("Published: %s\n", delivery.DeliveryPublicationDatetime)
fmt.Printf("Files: %d\n", len(delivery.Files))
Common Product IDs
| ID |
Name |
Description |
| 3 |
EP DocDB front file |
Bibliographic data (front file) |
| 4 |
EP full-text data - front file |
Full-text patent data |
| 14 |
EP DocDB back file |
Bibliographic data (back file) |
| 17 |
PATSTAT Global |
Patent statistics database |
| 18 |
PATSTAT EP Register |
EP register data |
Error Handling
The library provides custom error types for different scenarios:
// Authentication errors
if authErr, ok := err.(*bdds.AuthError); ok {
fmt.Printf("Auth failed: %s\n", authErr.Message)
}
// Not found errors
if notFoundErr, ok := err.(*bdds.NotFoundError); ok {
fmt.Printf("Resource not found: %s\n", notFoundErr.ID)
}
// Rate limit errors
if rateLimitErr, ok := err.(*bdds.RateLimitError); ok {
fmt.Printf("Rate limited, retry after %d seconds\n", rateLimitErr.RetryAfter)
}
Testing
This library includes comprehensive test coverage:
Unit Tests (Mock Server)
Offline tests using mock HTTP server with realistic responses:
# Run unit tests
go test -v
# Run with coverage
go test -v -cover
# Generate coverage report
go test -cover -coverprofile=coverage.out
go tool cover -html=coverage.out
Integration Tests (Real API)
Tests that make actual requests to the EPO BDDS API:
# Set credentials
export EPO_BDDS_USERNAME=your-username
export EPO_BDDS_PASSWORD=your-password
# Run integration tests
go test -tags=integration -v
# Run specific test
go test -tags=integration -v -run TestIntegration_ListProducts
Note: Integration tests require valid EPO BDDS credentials and will fail gracefully if not set or if specific products are not accessible (based on account).
Implementation
This library follows a clean architecture:
- OpenAPI Specification: Unofficial hand-crafted
openapi.yaml based on actual API behavior
- Code Generation: Types and client generated using oapi-codegen
- Idiomatic Wrapper: Clean Go client wrapping generated code
- Automatic Auth: OAuth2 token management handled transparently
Package Structure
├── client.go # Main client implementation
├── client_test.go # Unit tests with mock server
├── integration_test.go # Integration tests with real API
├── types.go # Public types
├── errors.go # Custom error types
├── utils.go # Internal utilities
├── generated/ # Auto-generated code
│ ├── types_gen.go # Generated types
│ └── client_gen.go # Generated client
└── openapi.yaml # OpenAPI 3.0 specification
Demo Application
An interactive demo application is included to showcase all library features:
# Set credentials
export EPO_BDDS_USERNAME=your-username
export EPO_BDDS_PASSWORD=your-password
# Run demo
cd demo
go run demo.go
The demo provides an interactive menu for:
- Listing all products
- Viewing product details with deliveries
- Finding products by name
- Getting latest delivery information
- Downloading files with progress tracking
See demo/README.md for full documentation.
Development
Regenerating from OpenAPI
If the OpenAPI spec is updated:
# Install generator
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
# Generate types
oapi-codegen -package generated -generate types openapi.yaml > generated/types_gen.go
# Generate client
oapi-codegen -package generated -generate client openapi.yaml > generated/client_gen.go
Similar Projects
This project follows the style and quality standards of:
License
MIT License - see LICENSE file for details.
Credits
Developed by: