BitbucketServer.Net
1.0.0
dotnet add package BitbucketServer.Net --version 1.0.0
NuGet\Install-Package BitbucketServer.Net -Version 1.0.0
<PackageReference Include="BitbucketServer.Net" Version="1.0.0" />
<PackageVersion Include="BitbucketServer.Net" Version="1.0.0" />
<PackageReference Include="BitbucketServer.Net" />
paket add BitbucketServer.Net --version 1.0.0
#r "nuget: BitbucketServer.Net, 1.0.0"
#:package BitbucketServer.Net@1.0.0
#addin nuget:?package=BitbucketServer.Net&version=1.0.0
#tool nuget:?package=BitbucketServer.Net&version=1.0.0
Bitbucket.Net
Modernized C# client for Bitbucket Server (Stash) REST API.
Contributing
Development setup (including the pre-commit formatting hook) is documented in CONTRIBUTING.md.
Fork notice — This is an actively maintained fork of lvermeulen/Bitbucket.Net, which appears to be abandoned (last release 2020). The 1.0.0 API surface is stable; breaking changes follow semver. The library is used in production by the author (as the backend for an MCP Server talking to on-prem Bitbucket Server), but not every endpoint has been verified against a live instance. Contributions, bug reports, and feedback are welcome.
What changed from the original
- .NET 10 target (dropped .NET Framework / .NET Standard)
System.Text.Jsonwith source generation (no runtime reflection)CancellationTokenon every async methodIAsyncEnumerablestreaming for paginated endpoints- Streaming diffs and raw file content
- Typed exception hierarchy (
BitbucketNotFoundException, etc.) IHttpClientFactory/ DI-friendly constructorsIDisposablewith ownership trackingIBitbucketClientdecomposed into 12 domain-specific sub-interfaces- Fluent query builders for pull requests, commits, branches, and projects
- Dedicated request DTOs for write operations
- Input validation on all public API methods
- OpenTelemetry tracing via
ActivitySource - Bitbucket Server 9.0+ blocker-comment (task) support with legacy fallback
- Flurl.Http 4.x
If you're looking for Bitbucket Cloud API, try this repository.
Installation
dotnet add package BitbucketServer.Net
Targets .NET 8.0 (LTS) and .NET 10.0. .NET 9 projects consume the
net8.0 assembly automatically.
Usage
Basic Authentication
var client = new BitbucketClient("https://bitbucket.example.com", "username", "password");
Token Authentication
var client = new BitbucketClient("https://bitbucket.example.com", () => GetAccessToken());
Resource management
BitbucketClient implements IDisposable. Clients created with a URL
own the underlying HTTP connection and dispose it:
using var client = new BitbucketClient("https://bitbucket.example.com", "user", "pass");
var projects = await client.GetProjectsAsync();
When you inject an HttpClient or IFlurlClient, the caller retains
ownership; the client will not dispose it.
Dependency Injection with IHttpClientFactory
For production scenarios, you can inject an externally managed HttpClient to leverage IHttpClientFactory for connection pooling, resilience policies, and centralized configuration.
Standard resilience (recommended)
The simplest approach uses Microsoft.Extensions.Http.Resilience which provides
retry, circuit breaker, and timeout out of the box:
// Requires: dotnet add package Microsoft.Extensions.Http.Resilience
services.AddHttpClient<BitbucketClient>(client =>
{
client.Timeout = TimeSpan.FromMinutes(2);
})
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
options.Retry.Delay = TimeSpan.FromSeconds(1);
options.Retry.BackoffType = DelayBackoffType.Exponential;
options.CircuitBreaker.FailureRatio = 0.5;
options.CircuitBreaker.SamplingDuration = TimeSpan.FromSeconds(30);
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(30);
});
// Register IBitbucketClient for dependency injection
services.AddSingleton<IBitbucketClient>(sp =>
{
var httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient(nameof(BitbucketClient));
return new BitbucketClient(
httpClient,
"https://bitbucket.example.com",
() => sp.GetRequiredService<ITokenProvider>().GetToken());
});
Custom resilience pipeline
For fine-grained control over which responses trigger retries:
services.AddHttpClient<BitbucketClient>(client =>
{
client.Timeout = TimeSpan.FromMinutes(2);
})
.AddResilienceHandler("bitbucket", builder =>
{
builder
.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 3,
BackoffType = DelayBackoffType.Exponential,
Delay = TimeSpan.FromSeconds(1),
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.HandleResult(r => r.StatusCode == HttpStatusCode.TooManyRequests
|| r.StatusCode >= HttpStatusCode.InternalServerError)
})
.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
{
FailureRatio = 0.5,
SamplingDuration = TimeSpan.FromSeconds(30),
BreakDuration = TimeSpan.FromSeconds(15),
})
.AddTimeout(TimeSpan.FromSeconds(30));
});
Advanced: Using IFlurlClient
For fine-grained control over Flurl's configuration:
services.AddSingleton<IFlurlClientCache>(sp => new FlurlClientCache()
.Add("Bitbucket", "https://bitbucket.example.com", builder => builder
.WithSettings(s => s.Timeout = TimeSpan.FromMinutes(5))
.WithHeader("X-Custom-Header", "value")));
services.AddSingleton<IBitbucketClient>(sp =>
{
var flurlClient = sp.GetRequiredService<IFlurlClientCache>().Get("Bitbucket");
return new BitbucketClient(flurlClient, () => GetToken());
});
Streaming with IAsyncEnumerable
For memory-efficient processing of large result sets, use the streaming variants:
// Stream projects without buffering all pages in memory
await foreach (var project in client.GetProjectsStreamAsync())
{
Console.WriteLine(project.Name);
}
// With cancellation support
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
await foreach (var pr in client.GetPullRequestsStreamAsync("PROJ", "repo", cancellationToken: cts.Token))
{
await ProcessPullRequestAsync(pr);
}
// Stream PR activities
await foreach (var activity in client.GetPullRequestActivitiesStreamAsync(
"PROJ", "repo", pullRequestId: 42))
{
ProcessActivity(activity);
}
// Stream dashboard PRs
await foreach (var pr in client.GetDashboardPullRequestsStreamAsync())
{
Console.WriteLine($"#{pr.Id}: {pr.Title}");
}
Fluent query builders
For endpoints with many optional filters, query builders provide a typed alternative to the flat method signatures:
var openPRs = await client.PullRequests("PROJ", "repo")
.InState(PullRequestStates.Open)
.OrderBy(PullRequestOrders.Newest)
.PageSize(25)
.GetAsync();
// Streaming variant
await foreach (var pr in client.PullRequests("PROJ", "repo")
.InState(PullRequestStates.Open)
.StreamAsync())
{
Console.WriteLine(pr.Title);
}
Builders are available for pull requests, commits, branches, and projects. The original flat methods still work and are not deprecated.
Extending the client
The low-level building blocks are protected, so you can subclass
BitbucketClient to add endpoints the library doesn't ship — or customize an
existing call — without forking. Subclasses get the request builders
(GetBaseUrl, the per-feature Get*Url() helpers), the paging helpers
(GetPagedAsync<T> / GetPagedStreamAsync<T>), and the response handlers.
public sealed class ExtendedBitbucketClient(string url, string user, string pass)
: BitbucketClient(url, user, pass)
{
// A custom paged endpoint, built entirely from the protected primitives.
public Task<IReadOnlyList<Repository>> GetCustomReposAsync(
string projectKey, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(projectKey);
var query = new Dictionary<string, object?>(StringComparer.Ordinal)
{
["limit"] = null,
["start"] = null,
};
return GetPagedAsync<Repository>(
GetBaseUrl().AppendPathSegment($"/projects/{projectKey}/custom-repos"),
query,
maxPages: null,
cancellationToken);
}
}
Only access modifiers were widened to protected; no public method signatures
changed.
Filtering archived repositories
The /repos search supports a typed archived-state filter
(ACTIVE / ARCHIVED / ALL):
var archived = await client.GetRepositoriesAsync(
RepositoryArchivedState.Archived, projectName: "PROJ");
Exception handling
Typed exceptions give you precise control over error handling:
try
{
var repo = await client.GetRepositoryAsync("PROJ", "repo");
}
catch (BitbucketNotFoundException ex)
{
Console.WriteLine($"Repository not found: {ex.Context}");
}
catch (BitbucketAuthenticationException)
{
Console.WriteLine("Invalid credentials");
}
catch (BitbucketForbiddenException ex)
{
Console.WriteLine($"Access denied: {ex.Message}");
}
catch (BitbucketApiException ex)
{
Console.WriteLine($"API error {ex.StatusCode}: {ex.Message}");
}
Benchmarks
Performance benchmarks are available in the benchmarks/ folder using BenchmarkDotNet:
cd benchmarks/Bitbucket.Net.Benchmarks
dotnet run -c Release
See benchmarks/README.md for detailed instructions.
Features
- Audit
- Project Events
- Repository Events
- Branches
- Create Branch
- Delete Branch
- Branch Info
- Branch Model
- Builds
- Commits Build Stats
- Commit Build Stats
- Commit Build Status
- Associate Build Status
- Comment Likes
- Repository Comment Likes
- Pull Request Comment Likes
- Core
- Admin
- Groups
- Users
- Cluster
- License
- Mail Server
- Permissions
- Pull Requests
- Application Properties
- Dashboard
- Groups
- Hooks
- Inbox
- Logs
- Markup
- Profile
- Projects
- Projects
- Permissions
- Repos
- Repos
- Branches
- Browse
- Changes
- Commits
- Compare
- Diff
- Files
- Last Modified
- Participants
- Permissions
- Pull Requests
- Raw
- Settings
- Tags
- Webhooks
- Settings
- Repos
- Tasks
- Users
- Admin
- Default Reviewers
- Project Default Reviewers
- Repository Default Reviewers
- Git
- JIRA
- Create JIRA Issue
- Get Commits For JIRA Issue
- Get JIRA Issues For Commits
- Personal Access Tokens
- Ref Restrictions
- Project Restrictions
- Repository Restrictions
- Repository Ref Synchronization
- SSH
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Flurl.Http (>= 4.0.2)
-
net8.0
- Flurl.Http (>= 4.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 85 | 6/5/2026 |
| 0.3.0 | 138 | 2/10/2026 |
| 0.2.0 | 148 | 2/8/2026 |
| 0.1.0-beta.1 | 73 | 2/6/2026 |