Table of Contents

Class ReferenceClient

Namespace
DatabentoDotNet.Reference
Assembly
DatabentoDotNet.Reference.dll

A client for Databento's reference data API: security master, corporate actions, and adjustment factors.

public sealed class ReferenceClient : IAsyncDisposable
Inheritance
ReferenceClient
Implements
Inherited Members

Examples

using DatabentoDotNet;
using DatabentoDotNet.Dbn;
using DatabentoDotNet.Reference;
using NodaTime;

await using var client = new ReferenceClient
{
    ApiKey = new ApiKey(Environment.GetEnvironmentVariable("DATABENTO_API_KEY")!),
};

// Reference data is a separate Databento subscription from live and historical, so a 403 here is
// an entitlement answer rather than a client error.
await foreach (SecurityMaster listing in client.SecurityMaster.GetRangeAsync(
    new SecurityMasterGetRangeParams
    {
        Symbols = Symbols.From("AAPL"),
        StypeIn = SType.RawSymbol,
        DateTimeRange = ReferenceDateTimeRange.Between(
            Instant.FromUtc(2024, 1, 1, 0, 0), Instant.FromUtc(2024, 2, 1, 0, 0)),
    }))
{
    Console.WriteLine($"{listing.Symbol} {listing.SecurityType} {listing.Exchange} {listing.Isin}");
}

CorporateActions and AdjustmentFactors take the same shape of parameters and stream their rows the same way.

Remarks

Port of upstream's reference::Client (reference.rs:34-92) — its key, base_url, gateway and request, which are the whole of that type once its three subclient accessors are set aside.

This client sends through HistoricalClient, and that is the port rather than a shortcut around one. Upstream's reference client is a separate type in the same crate that reuses the historical transport through crate-internal visibility: its request composes v{API_VERSION}/{slug} against a base URL derived from HistoricalGateway, attaches the same HTTP Basic credential, and sends with the same Accept: application/json default header and the same user agent. The reference API is the historical transport with a different set of slugs. Separate .NET assemblies have no equivalent of pub(crate) and this repo declares no InternalsVisibleTo anywhere, so the reuse goes through HistoricalClient's public transport — which is public for reasons of its own, documented on that type.

All three subclient properties, and all six of the reference API's endpoints. AdjustmentFactors arrived with its endpoint in #53, SecurityMaster with its two in #54, and CorporateActions with its two documentation endpoints in #56 and its get_range in #55. A facade with no endpoints on it would be a public empty class, which is why none of the three was declared in #48 — the same call HistoricalClient records for M3.

Thread-safe for concurrent requests once configured, for the same reason HistoricalClient is: everything below the surface is one HttpClient, and the properties are init-only and therefore frozen before the first request.

No builder. Upstream's ClientBuilder<AK> is generic type-state whose only purpose is to make "no API key" unrepresentable. C# 11 required init properties do that natively, checked by the compiler at every construction site. See PORTING.md §2.

Constructors

ReferenceClient()

Creates a client that owns its transport. Configure it through the init properties.

public ReferenceClient()

Remarks

The transport is built on first use, not here. An init accessor runs after the constructor body, so ApiKey does not exist yet at the point where an eager constructor would want to build an HttpClient from it; deferring is what makes required init properties and a fully configured transport compatible at all. ExecutionAndPublication because this type is documented as safe for concurrent requests: two threads racing into the first request must get one transport, not two.

ReferenceClient(HistoricalClient)

Creates a client that sends through an existing HistoricalClient, sharing its connection pool, and does not dispose it.

[SetsRequiredMembers]
public ReferenceClient(HistoricalClient transport)

Parameters

transport HistoricalClient

The client to send through. Not disposed by this one.

Remarks

For a consumer holding both. The reference and historical APIs share a host, a gateway, an API version and an auth scheme, so two independently configured clients mean two HttpClients and two connection pools to the same origin for no reason. This constructor is how they become one.

Ownership does not transfer. DisposeAsync() leaves transport open, because whoever created it is still using it. Disposing it while this client is alive is the caller's mistake to avoid, and it surfaces as ObjectDisposedException from the next request rather than as anything subtler.

The configuration properties below report transport's own settings, so they describe what this client actually does either way. Assigning one of them alongside this constructor throws rather than silently having no effect — see ApiKey.

Exceptions

ArgumentNullException

transport is null.

Properties

AdjustmentFactors

The adjustment_factors.* endpoints — the multipliers that make a price series comparable across splits, dividends and other capital events.

public AdjustmentFactorsClient AdjustmentFactors { get; }

Property Value

AdjustmentFactorsClient

Remarks

The first of this client's three endpoint-group facades (#53); #54–#56 add the rest. Built once and cached, because this client is documented thread-safe for concurrent requests and a bare null-coalescing assignment would let two threads each build one — the same arrangement Metadata and its three siblings use. Its one endpoint costs money, and reference data is a separate Databento product from historical market data: see GetRangeAsync(AdjustmentFactorsGetRangeParams, CancellationToken).

ApiKey

The API key to authenticate with. Validated when it is constructed.

public required ApiKey ApiKey { get; init; }

Property Value

ApiKey

Remarks

The type, never a string: ToString() is redacted, so formatting the object that holds the key cannot leak it. The key reaches the wire in exactly one place in this library — the Authorization header HistoricalClient builds — and nowhere else, not as a query parameter and not as a form field.

Assigning this alongside ReferenceClient(HistoricalClient) throws. That constructor's transport is already built and already carries a credential, so an assignment here could not reach the wire — and a property reporting a key that no request carries is exactly the kind of confidently wrong answer that is worse than an exception. The other four configuration properties refuse an assignment on that path for the same reason.

Exceptions

InvalidOperationException

The client was constructed over an existing HistoricalClient.

BaseUrl

A base URL to send requests to instead of Gateway's, or null to use the gateway.

public Uri? BaseUrl { get; init; }

Property Value

Uri

Remarks

The advanced knob, as upstream documents base_url: it exists for a test harness or a proxy, and it is how this library's own tests reach a mock gateway. A path on it is preserved — BaseUrl does that work and documents why it takes explicit effort.

Exceptions

ArgumentException

The URL is not absolute.

InvalidOperationException

The client was constructed over an existing HistoricalClient.

CorporateActions

The corporate_actions.* endpoints: what happened to a security, and the documentation the server keeps about its own events and enumerations.

public CorporateActionsClient CorporateActions { get; }

Property Value

CorporateActionsClient

Remarks

The largest of this client's three endpoint-group facades — three endpoints where the others have two and one, and a hundred-and-four-field row where the others have fifty and nineteen. #56 shipped ListEventsAsync(CancellationToken) and ListEnumsAsync(CancellationToken); #55 added GetRangeAsync(CorporateActionsGetRangeParams, CancellationToken), which is what makes this a data endpoint group rather than a documentation one. Built once and cached, for the reason AdjustmentFactors gives. Alone among the three, two of its endpoints are not known to cost anything — they return documentation rather than market data, and #57 prices them rather than assuming.

Gateway

The gateway to send requests to. Defaults to Bo1.

public HistoricalGateway Gateway { get; init; }

Property Value

HistoricalGateway

Remarks

Ignored when BaseUrl is set. HistoricalGateway is reused rather than re-declared: upstream's reference client derives its base URL from that same enum (reference.rs:37), and a second gateway type for one API would be a worse public surface than one shared with a sibling package.

Exceptions

InvalidOperationException

The client was constructed over an existing HistoricalClient.

LoggerFactory

Where to send this client's log messages, or null for none.

public ILoggerFactory? LoggerFactory { get; init; }

Property Value

ILoggerFactory

Remarks

How the API's X-Warning header surfaces, and the only route it has. See LoggerFactory, which this is handed to and which documents why a warnings property on every response was rejected.

Exceptions

InvalidOperationException

The client was constructed over an existing HistoricalClient.

SecurityMaster

The security_master.* endpoints — what a listing is, where it trades, and every identifier it is known by.

public SecurityMasterClient SecurityMaster { get; }

Property Value

SecurityMasterClient

Remarks

The second of this client's three endpoint-group facades (#54). Built once and cached, for the reason AdjustmentFactors gives. Both its endpoints cost money, and one property common to both can spend an ISIN entitlement rather than only money — see AllocateIsins.

Transport

The HTTP transport this client sends through — built from the properties above on first read, or the one supplied to ReferenceClient(HistoricalClient).

public HistoricalClient Transport { get; }

Property Value

HistoricalClient

Remarks

Public, and that is a decision rather than an omission — the same one HistoricalClient records for its own transport. The reference API has six endpoints (ROADMAP.md §6 lists them) and a caller who needs a seventh the week it ships should not have to wait for a release: SendAsync(HttpMethod, string, IEnumerable<KeyValuePair<string, string>>?, string?, CancellationToken) and SendZstdJsonLinesAsync<T>(HttpMethod, string, IEnumerable<KeyValuePair<string, string>>?, JsonTypeInfo<T>, CancellationToken) reach any slug the API serves.

It is also what makes the shared-transport constructor legible: a client built over an existing HistoricalClient returns that same instance here.

This is a transport, not a facade. That the historical endpoints are reachable from it is a consequence of them living on the same type upstream puts them on, not an invitation to call them from here.

Exceptions

ObjectDisposedException

The client has been disposed.

UserAgentExtension

Text to append to this library's User-Agent, identifying the application built on it, or null to send the library's own user agent alone.

public string? UserAgentExtension { get; init; }

Property Value

string

Remarks

Port of upstream's user_agent_extension (reference.rs:135-138), which composes it the same way: the library's user agent, a space, then this. An extension that is not a well-formed sequence of user-agent products and comments is rejected when the first request is sent rather than reaching Databento's logs malformed.

Exceptions

InvalidOperationException

The client was constructed over an existing HistoricalClient.

Methods

DisposeAsync()

Releases the transport, if this client created it.

public ValueTask DisposeAsync()

Returns

ValueTask

The transport's own disposal, or a completed task when there is nothing to release.

Remarks

Idempotent, and safe on a client that never sent a request: the transport is built on first use, so there is nothing to release until one has been. Using the client after this throws ObjectDisposedException rather than quietly building a second one.

A transport supplied to ReferenceClient(HistoricalClient) is left open. This client did not create it and does not know who else holds it.