A Go client for the European Patent Office's Bulk Data Distribution Service (BDDS) REST API - product discovery, delivery listing, and downloading of bulk datasets such as DOCDB, INPADOC, and EP full-text.
- Product discovery - list products, fetch a product with its deliveries, look up a product by name, get the latest delivery.
- File downloads - stream a delivery file to any
io.Writer, with an optional progress callback for large files. - Automatic auth - OAuth2 password grant; tokens are cached and refreshed before expiry. Credentials are optional: free/public products work without them.
- Robust requests - retry with backoff and automatic re-authentication on expiry; typed errors for the common failure cases.
go get github.com/patent-dev/epo-bddsFree/public BDDS products can be listed and downloaded without an account. Paid products require an EPO account with a subscription to the relevant product; the client then authenticates via OAuth2 password grant.
-
Open the BDDS portal to browse the product catalogue, then start sign-in / registration.
-
Sign in with your EPO account, or create one at the EPO login.
-
Subscribe to the products you need (free/public products need no subscription).
-
Export your EPO credentials for the client and demo:
export EPO_BDDS_USERNAME=... export EPO_BDDS_PASSWORD=...
package main
import (
"context"
"fmt"
"log"
bdds "github.com/patent-dev/epo-bdds"
)
func main() {
// Credentials are optional: free/public products (including ListProducts)
// work without them. Pass a Config with Username/Password for paid products.
client, err := bdds.NewClient(nil)
if err != nil {
log.Fatal(err)
}
products, err := client.ListProducts(context.Background())
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)
}
}config := &bdds.Config{
Username: "your-username", // optional, needed for paid products
Password: "your-password", // optional, needed for paid products
BaseURL: "https://publication-bdds.apps.epo.org", // default
UserAgent: "YourApp/1.0", // optional
MaxRetries: 3, // default: 3
RetryDelay: time.Second, // delay between retries, default: 1s
Timeout: 30 * time.Second, // request timeout, default: 30s
}
client, err := bdds.NewClient(config)RetryDelay and Timeout are time.Duration values.
// List all available products.
products, err := client.ListProducts(ctx)
// Get a product with its deliveries.
product, err := client.GetProduct(ctx, 3) // EP DocDB front file
// Find a product by name.
product, err := client.GetProductByName(ctx, "EP DocDB front file")
// Get the most recent delivery for a product.
delivery, err := client.GetLatestDelivery(ctx, 3)GetProduct returns a *ProductWithDeliveries; each delivery lists its files:
for _, d := range product.Deliveries {
fmt.Printf("%s - %d files\n", d.DeliveryName, len(d.Files))
}If you already have the product, delivery, and file IDs, downloads work without credentials:
f, err := os.Create("download.zip")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = client.DownloadFile(ctx, productID, deliveryID, fileID, f)Use DownloadFileWithProgress for a progress callback on large files:
err = client.DownloadFileWithProgress(ctx, 3, 12345, 67890, f,
func(bytesWritten, totalBytes int64) {
fmt.Printf("\rProgress: %.1f%%", float64(bytesWritten)*100/float64(totalBytes))
})| 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 |
An interactive demo exercises every method. Set credentials and run it:
export EPO_BDDS_USERNAME=your-username
export EPO_BDDS_PASSWORD=your-password
cd demo && go run demo.goSee demo/README.md for details.
The library returns typed errors you can match with errors.As:
var authErr *bdds.AuthError
if errors.As(err, &authErr) {
fmt.Printf("auth failed (status %d): %s\n", authErr.StatusCode, authErr.Message)
}
var notFound *bdds.NotFoundError
if errors.As(err, ¬Found) {
fmt.Printf("%s not found: %s\n", notFound.Resource, notFound.ID)
}
var rateLimit *bdds.RateLimitError
if errors.As(err, &rateLimit) {
fmt.Printf("rate limited, retry after %d seconds\n", rateLimit.RetryAfter)
}make test # unit tests (mock HTTP server, race)
make test-integration # integration tests against the real API, needs credentials
make lintIntegration tests require valid EPO BDDS credentials and skip gracefully when they are not set or a product is not accessible for your account:
export EPO_BDDS_USERNAME=your-username
export EPO_BDDS_PASSWORD=your-passwordThe typed code under generated/ is produced from the hand-crafted openapi.yaml
with oapi-codegen. If you change
the spec, regenerate it:
make generatePart of the patent.dev open-source patent data ecosystem:
- epo-ops - EPO Open Patent Services client (bibliographic, full text, families, legal status, images)
- uspto-odp - USPTO Open Data Portal client (patents, PTAB, TSDR, full text)
- dpma-connect-plus - DPMA Connect Plus client (patents, designs, trademarks)
The bulk-file-loader uses these libraries for automated patent data downloads.
MIT - Funktionslust GmbH / patent.dev.