Download a module’s FileDescriptorSet#
The BSR can return the compiled form of any module as a google.protobuf.FileDescriptorSet, served directly over HTTP.
This gives you a module’s schema in the native format that every Protobuf runtime understands, without having to install the Buf CLI or compile .proto files yourself.
What is a FileDescriptorSet?#
A FileDescriptorSet is Protobuf’s own representation of a compiled schema: a list of FileDescriptorProto messages that describe every file, message, enum, service, and method.
It’s the artifact protoc writes when you pass -o, and any Protobuf runtime can load one to power reflection, dynamic message decoding, and gRPC server reflection.
For a full treatment of descriptors and how they’re used, see the Descriptors reference.
Note
The BSR returns a pure google.protobuf.FileDescriptorSet here, not a Buf image.
Buf images are wire-compatible with FileDescriptorSets but carry extra Buf-specific metadata, which this endpoint strips.
If you need that metadata, build the module locally with buf build; if you want a plain FileDescriptorSet from local sources, use buf build --as-file-descriptor-set.
When to fetch it from the BSR#
Common reasons to download the descriptor instead of building it locally:
- Populating gRPC server reflection with the schemas your server exposes, without checking
.protofiles into the server’s source tree. - Building schema-driven tooling in a language without first-class Buf support, such as a Ruby or PHP service that decodes messages dynamically at runtime.
- Running checks in CI pipelines that can’t install the Buf CLI; a plain
curlworks from any image. - Feeding other
bufinvocations: the Buf CLI accepts aFileDescriptorSeton stdin, so the descriptor URL pipes straight intobuf lint,buf breaking, and so on.
If none of these apply and you already have the Buf CLI available, buf build --as-file-descriptor-set produces the same artifact from local .proto files.
URL structure#
<host>is your BSR hostname (buf.buildfor the public BSR, or your private BSR domain likeyour-bsr-instance.example.comfor a self-hosted instance).<organization>is the BSR organization that owns the module.<module>is the module name. Each module corresponds to one BSR repository, where labels live.<reference>is a commit ID or label. Each module’s repository has a default label namedmainthat always points at the latest commit on that label, so usemainfor “the latest published schema.”
The path shape is the same on private and self-hosted BSR instances; only the host changes.
This endpoint is rate-limited at 1 request per second with a burst of 2; see Rate limits for the full picture.
Basic usage#
Download the latest commit on the default label to a local file:
Pin to an exact commit instead:
$ curl -sSL "https://buf.build/acme/petapis/descriptor/7abdb7802c8f4737a1a23a35ca8266ef" -o petapis.binpb
Or pipe the response directly into another tool. The Buf CLI accepts a FileDescriptorSet on stdin:
Private modules#
Modules in private repositories require a BSR token in the Authorization header:
$ curl -sSL "https://buf.build/acme/private-apis/descriptor/main" \
-H "Authorization: Bearer ${BUF_TOKEN}" \
-o private-apis.binpb
Public modules don’t require authentication.
Query parameters#
Three query parameters control what ends up in the response. They’re independent and can be combined.
Imports#
Every .proto file that a module transitively imports is represented by its own FileDescriptorProto. That includes files inside the module, files from any dependency, and the Well-Known Types.
By default the response includes all of them, so the returned set is self-contained and a Protobuf runtime can load it without resolving anything else.
If you only want the target module’s own files, add imports=false:
A FileDescriptorSet without its transitive imports usually can’t be loaded by anything that needs the full schema graph, so leave this at the default unless you specifically want a stripped-down set and have the missing files available some other way.
Source code info#
SourceCodeInfo records the file position and comments for every declaration.
It’s what powers documentation generators, comment-aware reflection, and the BSR’s own UI.
It’s also bulky, so the endpoint omits it by default.
Request it with source_info=true:
Source retention options#
Source retention options are options declared with retention = RETENTION_SOURCE.
They’re visible to compilers and code generators but are meant to be stripped before the descriptor is used at runtime.
The endpoint strips them by default.
Set source_retention_options=true if your tooling treats the descriptor as a source-level input rather than a runtime artifact:
$ curl -sSL "https://buf.build/acme/petapis/descriptor/main?source_retention_options=true" -o petapis.binpb
Response formats#
The endpoint supports content negotiation via the Accept and Accept-Encoding headers.
Binary or JSON#
The default response is the FileDescriptorSet in the Protobuf binary format, which is what every Protobuf runtime expects.
Set Accept: application/json if you’d rather inspect the response by eye:
$ curl -sSL "https://buf.build/acme/petapis/descriptor/main" \
-H "Accept: application/json" \
-o petapis.json
For anything that’s going to parse the descriptor programmatically, stick with the binary default.
Compression#
Ask for a gzip-compressed response with Accept-Encoding: gzip:
$ curl -sSL "https://buf.build/acme/petapis/descriptor/main" \
-H "Accept-Encoding: gzip" \
-o petapis.binpb.gz
Connect/gRPC equivalent#
If your environment already speaks Connect or gRPC, the BSR exposes the same operation as the RPC method buf.registry.module.v1.FileDescriptorSetService/GetFileDescriptorSet.
The HTTP endpoint and the RPC return the same descriptor data, but the controls are spelled differently and the RPC has a few extras:
| HTTP query parameter | RPC field |
|---|---|
(default; opt out with imports=false) |
exclude_imports |
source_info=true |
include_source_code_info |
source_retention_options=true |
include_source_retention_options |
| (not available on HTTP) | include_types / exclude_types |
Use the RPC if you need to filter by type; otherwise either surface works.
Using the result#
Once you have the descriptor, any Protobuf runtime can load it. To run Buf checks against a remote module without ever cloning it, fetch the descriptor with source_info=true (lint diagnostics report file/line, which need source-code info) and pipe directly into the CLI:
For programmatic use, see Dynamic messaging for a worked Go example that loads a FileDescriptorSet and decodes an arbitrary message by fully qualified name, and Runtime library support for the equivalent APIs in C++, Java, Python, C#, PHP, and TypeScript.
See also#
- Export modules from the BSR: download the raw
.protosource files instead of a compiled descriptor. - Descriptors: deep dive into what descriptors are and how Protobuf runtimes use them.
- Buf images: Buf’s
FileDescriptorSetsuperset, used when Buf-specific metadata is needed.