BitbucketServer.Net 1.0.0

dotnet add package BitbucketServer.Net --version 1.0.0
                    
NuGet\Install-Package BitbucketServer.Net -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BitbucketServer.Net" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BitbucketServer.Net" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="BitbucketServer.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BitbucketServer.Net --version 1.0.0
                    
#r "nuget: BitbucketServer.Net, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BitbucketServer.Net@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BitbucketServer.Net&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=BitbucketServer.Net&version=1.0.0
                    
Install as a Cake Tool

Bitbucket.Net

NuGet NuGet Downloads CI codecov license alternate text is missing from this package README image alternate text is missing from this package README image

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.Json with source generation (no runtime reflection)
  • CancellationToken on every async method
  • IAsyncEnumerable streaming for paginated endpoints
  • Streaming diffs and raw file content
  • Typed exception hierarchy (BitbucketNotFoundException, etc.)
  • IHttpClientFactory / DI-friendly constructors
  • IDisposable with ownership tracking
  • IBitbucketClient decomposed 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.

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
  • 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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