A high-performance .NET client for accessing Databento market data, supporting both real-time streaming and historical data queries.
⚠️ Beta Software: This is a newly developed client library. While functional and tested, it should be thoroughly validated in your specific use case before production deployment. Please report any issues to the issue tracker.
📦 Package: Databento.Client on NuGet.org
Install via .NET CLI:
dotnet add package Databento.Client --prereleaseOr via Package Manager Console:
Install-Package Databento.Client -PrereleaseOr add directly to your .csproj:
<ItemGroup>
<PackageReference Include="Databento.Client" Version="4.0.0-beta" />
</ItemGroup>See Building section below for instructions on building from source.
- Live Streaming: Real-time market data with async/await and IAsyncEnumerable support
- Historical Data: Query past market data with time-range filtering
- Cross-Platform: Works on Windows, Linux, and macOS
- High Performance: Built on top of Databento's C++ client library
- Type-Safe: Strongly-typed API with full IntelliSense support
- .NET 8+ Compatible: Modern C# with nullable reference types (.NET 8, .NET 9+)
All 16 DBN record types from databento-cpp are fully implemented:
| Record Type | Status | Size | Description |
|---|---|---|---|
| TradeMessage | ✅ | 48 bytes | Trades (RType 0x00) |
| MboMessage | ✅ | 56 bytes | Market by Order (RType 0xA0) |
| Mbp1Message | ✅ | 80 bytes | Market by Price Level 1 (RType 0x01) |
| Mbp10Message | ✅ | 368 bytes | Market by Price Level 10 (RType 0x0A) |
| OhlcvMessage | ✅ | 56 bytes | OHLCV bars - deprecated, 1s, 1m, 1h, 1d, EOD (RType 0x11, 0x20-0x24) |
| StatusMessage | ✅ | 40 bytes | Trading status (RType 0x12) |
| InstrumentDefMessage | ✅ | 520 bytes | Instrument definitions (RType 0x13) |
| ImbalanceMessage | ✅ | 112 bytes | Order imbalances (RType 0x14) |
| ErrorMessage | ✅ | 320 bytes | Error messages (RType 0x15) |
| SymbolMappingMessage | ✅ | 176 bytes | Symbol mappings (RType 0x16) |
| SystemMessage | ✅ | 320 bytes | System messages & heartbeats (RType 0x17) |
| StatMessage | ✅ | 80 bytes | Market statistics (RType 0x18) |
| BboMessage | ✅ | 80 bytes | Best Bid/Offer - 1s, 1m (RType 0xC3-0xC4) |
| CbboMessage | ✅ | 80 bytes | Consolidated BBO - 1s, 1m (RType 0xC0-0xC1) |
| Cmbp1Message | ✅ | 80 bytes | Consolidated Market by Price (RType 0xB1) |
| TcbboMessage | ✅ | 80 bytes | Trade with Consolidated BBO (RType 0xC2) |
| UnknownRecord | ✅ | Variable | Fallback for unrecognized types |
| Feature | databento-cpp | databento-dotnet | Status |
|---|---|---|---|
| Live Streaming | ✅ | ✅ | Complete |
| Subscribe to datasets | ✅ | ✅ | Complete |
| Multiple symbol subscription | ✅ | ✅ | Complete |
| Schema filtering | ✅ | ✅ | Complete |
| Start/Stop streaming | ✅ | ✅ | Complete |
| Record Deserialization | ✅ | ✅ | Complete |
| All 16 record types | ✅ | ✅ | Complete |
| Fixed-point price conversion | ✅ | ✅ | Complete |
| Timestamp handling | ✅ | ✅ | Complete |
| Helper Utilities | ✅ | ✅ | Complete |
| FlagSet (bit flags) | ✅ | ✅ | Complete |
| Constants & sentinel values | ✅ | ✅ | Complete |
| Schema enums | ✅ | ✅ | Complete |
| Historical Client | ✅ | ✅ | Complete (time-range queries) |
| Time-range queries | ✅ | ✅ | Complete with IAsyncEnumerable |
| Batch downloads | ✅ | ❌ | Not yet implemented |
| Metadata & Symbol Mapping | ✅ | ✅ | Complete |
| Instrument metadata queries | ✅ | ✅ | 10+ metadata API methods working |
| Symbol resolution | ✅ | ✅ | SymbolMappingMessage support for live/historical |
| Advanced Features | |||
| Compression (zstd) | ✅ | ✅ | Handled by native layer |
| SSL/TLS | ✅ | ✅ | Handled by native layer |
| Reconnection logic | ✅ | Delegated to databento-cpp |
- Total Record Types: 16/16 (100%)
- Enumerations: 11/11 (100%)
- Schema, RType, Action, Side, InstrumentClass, MatchAlgorithm, UserDefinedInstrument, SecurityUpdateAction, StatType, StatUpdateAction, SType
- Helper Classes: 3/3 (100%)
- FlagSet, Constants, BidAskPair/ConsolidatedBidAskPair
- Live Streaming: Fully functional
- Binary Deserialization: All DBN formats supported
- Lines of Code: ~2,500 in high-level API, ~500 in P/Invoke layer, ~800 in native wrapper
Latest (November 2025) - Production Ready
- ✅ Reference API implementation (SecurityMaster, AdjustmentFactors, CorporateActions)
- ✅ OpenTelemetry telemetry with retry policies
- ✅ Complete metadata & symbol mapping support (SymbolMappingMessage)
- ✅ All 16 record types with proper deserialization
- ✅ Thread-safe LiveClient with reconnection support
┌─────────────────────────────────────────────────────────────┐
│ Your .NET Application │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────▼────────────────────────────────────┐
│ Databento.Client (High-Level API) │
│ • LiveClient, HistoricalClient │
│ • Async/await, IAsyncEnumerable, Events │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────▼────────────────────────────────────┐
│ Databento.Interop (P/Invoke Layer) │
│ • SafeHandles, Marshaling │
└────────────────────────┬────────────────────────────────────┘
│ P/Invoke
┌────────────────────────▼────────────────────────────────────┐
│ Databento.Native (C Wrapper - CMake) │
│ • C exports wrapping databento-cpp │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────▼────────────────────────────────────┐
│ databento-cpp (Git Submodule) │
│ • Live streaming, Historical queries │
│ • DBN encoding/decoding │
└─────────────────────────────────────────────────────────────┘
Required:
- .NET 8 SDK or later
- CMake 3.24 or later
- C++17 compatible compiler:
- Windows: Visual Studio 2019 or later
- Linux: GCC 9+ or Clang 10+
- macOS: Xcode 11+
Automatically fetched by CMake:
- databento-cpp (via FetchContent)
- OpenSSL 3.0+
- Zstandard (zstd)
- nlohmann_json
Required:
- .NET 8 Runtime or later (.NET 8, .NET 9+)
Compatibility:
- ✅ .NET 8.0
- ✅ .NET 9.0 (tested and confirmed)
Platform Requirements:
Windows:
- No additional prerequisites required - the NuGet package includes all necessary dependencies (including Visual C++ runtime DLLs)
⚠️ Troubleshooting: If you see "DllNotFoundException: databento_native"
This usually means the Visual C++ Runtime failed to load. Try:
- Update Windows - Ensure Windows 10 version 1809+ or Windows 11
- Install VC++ Redistributable (if issue persists):
- Download: Visual C++ 2022 Redistributable (x64)
- This is typically only needed on older/minimal Windows installations
The library includes runtime DLLs, but in rare cases Windows may require the full redistributable package.
Linux:
- glibc 2.31+ (Ubuntu 20.04+, RHEL 8+)
macOS:
- macOS 11.0+ (Big Sur or later)
dotnet add package Databento.Client --prereleaseSet your Databento API key as an environment variable:
Windows:
$env:DATABENTO_API_KEY="your-api-key-here"Linux/macOS:
export DATABENTO_API_KEY="your-api-key-here"Get your API key at https://databento.com/portal/keys
using Databento.Client.Builders;
using Databento.Client.Models;
// Get API key from environment variable (secure)
var apiKey = Environment.GetEnvironmentVariable("DATABENTO_API_KEY")
?? throw new InvalidOperationException("DATABENTO_API_KEY environment variable not set");
// Create live client
await using var client = new LiveClientBuilder()
.WithApiKey(apiKey)
.Build();
// Subscribe to events
client.DataReceived += (sender, e) =>
{
Console.WriteLine($"Received: {e.Record}");
};
// Subscribe to NVDA trades
await client.SubscribeAsync(
dataset: "EQUS.MINI",
schema: Schema.Trades,
symbols: new[] { "NVDA" }
);
// Start streaming
await client.StartAsync();
// Stream records using IAsyncEnumerable
await foreach (var record in client.StreamAsync())
{
// Process records
if (record is TradeMessage trade)
{
Console.WriteLine($"Trade: {trade.InstrumentId} @ {trade.PriceDecimal}");
}
}using Databento.Client.Builders;
using Databento.Client.Models;
// Get API key from environment variable (secure)
var apiKey = Environment.GetEnvironmentVariable("DATABENTO_API_KEY")
?? throw new InvalidOperationException("DATABENTO_API_KEY environment variable not set");
// Create historical client
await using var client = new HistoricalClientBuilder()
.WithApiKey(apiKey)
.Build();
// Define time range - Static trading day: November 11-12, 2025
var startTime = new DateTimeOffset(2025, 11, 11, 0, 0, 0, TimeSpan.Zero); // 11/11/2025 00:00 UTC
var endTime = new DateTimeOffset(2025, 11, 12, 23, 59, 59, TimeSpan.Zero); // 11/12/2025 23:59 UTC
// Query historical trades
await foreach (var record in client.GetRangeAsync(
dataset: "EQUS.MINI",
schema: Schema.Trades,
symbols: new[] { "NVDA" },
startTime: startTime,
endTime: endTime))
{
Console.WriteLine($"Historical record: {record}");
}Try it yourself: Run the included example with:
dotnet run --project examples/Historical.Readme.Example/Historical.Readme.Example.csprojWhen streaming market data, records contain numeric InstrumentId values (e.g., 11667) instead of ticker symbols (e.g., "NVDA"). You must handle SymbolMappingMessage records to resolve these IDs to human-readable symbols.
// What you receive in TradeMessage:
InstrumentId: 11667
Price: 185.97
Size: 100
// What you need to display:
"NVDA: $185.97 x 100"- SymbolMappingMessage records arrive FIRST (before trades/quotes)
- Build a lookup dictionary:
InstrumentId → Symbol - Use the dictionary to resolve symbols in subsequent data records
// ✅ CORRECT
symbolMap[mapping.InstrumentId] = mapping.STypeOutSymbol;
// ❌ WRONG - Will show "ALL_SYMBOLS" for every trade!
symbolMap[mapping.InstrumentId] = mapping.STypeInSymbol;Why? For multi-symbol subscriptions:
STypeInSymbol= Your subscription string ("ALL_SYMBOLS") - same for all recordsSTypeOutSymbol= Actual ticker symbol ("NVDA","AAPL", etc.) - unique per instrument
using System.Collections.Concurrent;
using Databento.Client.Builders;
using Databento.Client.Models;
var apiKey = Environment.GetEnvironmentVariable("DATABENTO_API_KEY")
?? throw new InvalidOperationException("DATABENTO_API_KEY not set");
// Symbol map: InstrumentId → Ticker Symbol
var symbolMap = new ConcurrentDictionary<uint, string>();
// Create live client
await using var client = new LiveClientBuilder()
.WithApiKey(apiKey)
.WithDataset("EQUS.MINI")
.Build();
// Handle incoming records
client.DataReceived += (sender, e) =>
{
// Step 1: Capture symbol mappings (arrive first)
if (e.Record is SymbolMappingMessage mapping)
{
// ⚠️ Use STypeOutSymbol for the actual ticker symbol!
symbolMap[mapping.InstrumentId] = mapping.STypeOutSymbol;
return;
}
// Step 2: Resolve symbols for data records
if (e.Record is TradeMessage trade)
{
var symbol = symbolMap.GetValueOrDefault(
trade.InstrumentId,
trade.InstrumentId.ToString()); // Fallback if not found
Console.WriteLine($"{symbol}: ${trade.PriceDecimal:F2} x {trade.Size}");
}
};
// Calculate most recent market open (9:30 AM ET) for replay mode
var now = DateTimeOffset.UtcNow;
var et = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var etNow = TimeZoneInfo.ConvertTime(now, et);
var replayDate = etNow.Date;
// Go back to most recent weekday
while (replayDate.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday)
replayDate = replayDate.AddDays(-1);
if (etNow.TimeOfDay < TimeSpan.FromHours(9.5))
{
replayDate = replayDate.AddDays(-1);
while (replayDate.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday)
replayDate = replayDate.AddDays(-1);
}
var marketOpen = new DateTimeOffset(
replayDate.Year, replayDate.Month, replayDate.Day,
9, 30, 0, et.GetUtcOffset(replayDate));
// Subscribe with replay mode (works anytime, no market hours required)
await client.SubscribeAsync(
dataset: "EQUS.MINI",
schema: Schema.Trades,
symbols: new[] { "NVDA", "AAPL" },
startTime: marketOpen // Omit this parameter for live mode
);
await client.StartAsync();
// CRITICAL: Must use StreamAsync() to pump records through the pipeline
var timeout = Task.Delay(TimeSpan.FromSeconds(30));
var streamTask = Task.Run(async () =>
{
await foreach (var record in client.StreamAsync())
{
// Records are handled by DataReceived event
}
});
await Task.WhenAny(streamTask, timeout);
await client.StopAsync();NVDA: $185.97 x 100
AAPL: $172.45 x 50
NVDA: $186.02 x 200
...
Symbol lookups are very fast (~20-50 nanoseconds per lookup using ConcurrentDictionary), negligible compared to network I/O.
See examples/LiveSymbolResolution.Example for a complete, tested example with:
- Symbol mapping implementation using replay mode (works anytime, no market hours required)
- Performance measurement
- Error handling
- Validation
The example uses replay mode by default (replaying from most recent market open), with live mode shown as a commented alternative.
dotnet run --project examples/LiveSymbolResolution.Example/LiveSymbolResolution.Example.csproj- API Reference: Symbol Mapping section for detailed documentation
- IntelliSense: Hover over
SymbolMappingMessagein your IDE for inline examples - Example Code:
examples/LiveSymbolResolution.Example/Program.cs
# Windows
./build/build-all.ps1 -Configuration Release
# Linux/macOS
./build/build-all.sh --configuration Release# Windows
./build/build-native.ps1 -Configuration Release
# Linux/macOS
./build/build-native.sh --configuration Releasedotnet build databento-dotnet.sln -c Releasedatabento-dotnet/
├── src/
│ ├── Databento.Native/ # C++ native wrapper
│ │ ├── include/ # C API headers
│ │ ├── src/ # C++ implementation
│ │ └── CMakeLists.txt
│ ├── Databento.Interop/ # P/Invoke layer
│ │ ├── Native/ # P/Invoke declarations
│ │ └── Handles/ # SafeHandle wrappers
│ └── Databento.Client/ # High-level .NET API
│ ├── Live/ # Live streaming
│ ├── Historical/ # Historical queries
│ ├── Reference/ # Reference data APIs
│ ├── Models/ # Data models
│ └── Builders/ # Builder pattern
├── examples/
│ ├── LiveStreaming.Example/
│ ├── HistoricalData.Example/
│ ├── Reference.Example/
│ └── (26+ examples total)
├── build/
│ ├── build-native.ps1 # Native build (Windows)
│ ├── build-native.sh # Native build (Linux/macOS)
│ └── build-all.ps1 # Full solution build
└── databento-dotnet.sln # Visual Studio solution
Prerequisites: Set the DATABENTO_API_KEY environment variable (see API Key Setup above).
# Run live streaming example
dotnet run --project examples/LiveStreaming.Example
# Run historical data example
dotnet run --project examples/HistoricalData.Example
# Run reference data example
dotnet run --project examples/Reference.Example- MBO: Market by order
- MBP-1: Market by price (Level 1)
- MBP-10: Market by price (Level 10)
- Trades: Trade messages
- OHLCV: OHLCV bars (1s, 1m, 1h, 1d)
- Definition: Instrument definitions
- Statistics: Market statistics
- Status: Trading status
- Imbalance: Order imbalances
ILiveClient client = new LiveClientBuilder()
.WithApiKey(apiKey)
.Build();
// Events
client.DataReceived += (sender, e) => { /* ... */ };
client.ErrorOccurred += (sender, e) => { /* ... */ };
// Methods
await client.SubscribeAsync(dataset, schema, symbols);
await client.StartAsync();
await client.StopAsync();
// IAsyncEnumerable streaming
await foreach (var record in client.StreamAsync()) { /* ... */ }IHistoricalClient client = new HistoricalClientBuilder()
.WithApiKey(apiKey)
.Build();
// Query historical data
await foreach (var record in client.GetRangeAsync(
dataset, schema, symbols, startTime, endTime))
{
// Process records
}IReferenceClient client = new ReferenceClientBuilder()
.SetApiKey(apiKey)
.Build();
// Get latest security master data
var records = await client.SecurityMaster.GetLastAsync(
symbols: new[] { "NVDA" },
stypeIn: SType.RawSymbol
);
// Get security master data for a date range
var historicalRecords = await client.SecurityMaster.GetRangeAsync(
start: DateTimeOffset.UtcNow.AddDays(-30),
end: DateTimeOffset.UtcNow,
symbols: new[] { "NVDA" }
);
// Get adjustment factors
var adjustments = await client.AdjustmentFactors.GetRangeAsync(
start: DateTimeOffset.UtcNow.AddDays(-90),
symbols: new[] { "NVDA" }
);
// Get corporate actions
var corporateActions = await client.CorporateActions.GetRangeAsync(
start: DateTimeOffset.UtcNow.AddYears(-1),
symbols: new[] { "NVDA" }
);-
Memory Management: Records are copied from native to managed memory. For high-throughput scenarios, consider batching.
-
Threading: Callbacks fire on native threads. The library marshals them to the .NET thread pool via
Channel<T>. -
Backpressure: The
Channel<T>is unbounded by default. Consider adding bounds for memory-constrained environments. -
Disposal: Always use
await usingto ensure proper resource cleanup.
Ensure the native library is built and copied to the output directory:
# Rebuild native library
./build/build-native.ps1Ensure all prerequisites are installed:
# Windows (with chocolatey)
choco install cmake visualstudio2022buildtools
# Linux (Ubuntu/Debian)
sudo apt-get install cmake build-essential libssl-dev libzstd-dev
# macOS (with Homebrew)
brew install cmake openssl zstdVerify your API key is correct and has the required permissions:
# Test API key
curl -H "Authorization: Bearer your-api-key" https://api.databento.com/v1/metadata.list_datasetsApache 2.0 License. See LICENSE for details.
Built on top of Databento's official C++ client.