RestSharp

Production guide to RestSharp: The mature REST and HTTP API client library for .NET

RestSharp Repo stars is one of the most mature and widely-used REST client libraries in the .NET ecosystem, with 9.8k+ GitHub stars and 268 contributors.

InfoLink
LicenseGitHub (Apache 2.0)
DownloadsNuget
Latest VersionGitHub release (latest by date) (113.0)
IssuesGitHub issues
ContributorsGitHub contributors (268)

What RestSharp Adds to HttpClient

RestSharp is a wrapper around HttpClient that provides:

  • Default parameters - Add headers, query parameters, or body data at the client level
  • Multiple serialization formats - JSON (System.Text.Json), XML, CSV with pluggable serializers
  • Rich parameter handling - URL segments, query strings, headers, cookies, body content
  • Authentication support - OAuth 1/2, Basic, JWT, API keys
  • Request/response interceptors - Customize the HTTP pipeline
  • Simpler API - Less boilerplate than raw HttpClient

Available Packages

PackageDescription
RestSharpCore library with System.Text.Json and basic XML serializer
RestSharp.Serializers.NewtonsoftJsonNewtonsoft.Json serializer
RestSharp.Serializers.XmlCustom RestSharp XML serializer
RestSharp.Serializers.CsvHelperCSV serializer

Quick Start

Installation

dotnet add package RestSharp

Basic Usage

using RestSharp;

// Create client with options
RestClientOptions options = new("https://api.example.com")
{
    ThrowOnAnyError = false,  // Don't throw on non-2xx status codes
    Timeout = TimeSpan.FromSeconds(30),
    UserAgent = "MyApp/1.0"
};

RestClient client = new(options);

// GET - Retrieve a single resource
RestRequest getRequest = new("api/v1/tasks/{taskId}");
getRequest.AddUrlSegment("taskId", 123);

RestResponse<TaskDto> getResponse = await client.ExecuteGetAsync<TaskDto>(getRequest);
if (getResponse.IsSuccessful && getResponse.Data is not null)
{
    TaskDto task = getResponse.Data;
    Console.WriteLine($"Task: {task.Title}");
}

// GET - Retrieve collection
RestRequest listRequest = new("api/v1/tasks");
listRequest.AddQueryParameter("status", "pending");
listRequest.AddQueryParameter("page", 1);

TaskDto[]? tasks = await client.GetAsync<TaskDto[]>(listRequest);

// POST - Create resource
CreateTaskRequest createPayload = new("Buy groceries", "Weekly shopping", TaskPriority.Medium);
RestRequest postRequest = new RestRequest("api/v1/tasks")
    .AddJsonBody(createPayload);

TaskDto? createdTask = await client.PostAsync<TaskDto>(postRequest);
Console.WriteLine($"Created task #{createdTask?.Id}");

// PUT - Update resource
UpdateTaskRequest updatePayload = new("Buy organic groceries", "Updated description", TaskPriority.High);
RestRequest putRequest = new RestRequest("api/v1/tasks/{taskId}")
    .AddUrlSegment("taskId", createdTask!.Id)
    .AddJsonBody(updatePayload);

TaskDto? updatedTask = await client.PutAsync<TaskDto>(putRequest);

// DELETE - Remove resource
RestRequest deleteRequest = new RestRequest("api/v1/tasks/{taskId}")
    .AddUrlSegment("taskId", createdTask.Id);

RestResponse deleteResponse = await client.ExecuteDeleteAsync(deleteRequest);
Console.WriteLine($"Deleted: {deleteResponse.IsSuccessful}");

DTOs (Data Transfer Objects)

/// <summary>Represents a task in the system.</summary>
public sealed record TaskDto(
    int Id,
    string Title,
    string? Description,
    bool IsCompleted,
    DateTime CreatedAt,
    TaskPriority Priority);

/// <summary>Request model for creating a task.</summary>
public sealed record CreateTaskRequest(
    string Title,
    string? Description = null,
    TaskPriority Priority = TaskPriority.Medium);

/// <summary>Request model for updating a task.</summary>
public sealed record UpdateTaskRequest(
    string Title,
    string? Description,
    TaskPriority Priority);

/// <summary>Task priority levels.</summary>
public enum TaskPriority { Low, Medium, High, Critical }

Typed API Client Pattern

Encapsulate RestSharp calls in a dedicated client class for better maintainability:

using RestSharp;

/// <summary>
/// Typed client for the Tasks API.
/// Encapsulates all REST operations for task management.
/// </summary>
public sealed class TasksApiClient : IDisposable
{
    private readonly RestClient _client;
    private bool _disposed;

    public TasksApiClient(string baseUrl)
    {
        RestClientOptions options = new(baseUrl)
        {
            ThrowOnAnyError = false,
            Timeout = TimeSpan.FromSeconds(30)
        };

        _client = new RestClient(options);
    }

    public TasksApiClient(RestClient client)
    {
        _client = client;
    }

    /// <summary>Retrieves a task by its identifier.</summary>
    public async Task<TaskDto?> GetTaskByIdAsync(
        int taskId,
        CancellationToken cancellationToken = default)
    {
        RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
            .AddUrlSegment("taskId", taskId);

        return await _client.GetAsync<TaskDto>(request, cancellationToken);
    }

    /// <summary>Retrieves all tasks with optional filtering.</summary>
    public async Task<IReadOnlyList<TaskDto>> GetTasksAsync(
        string? status = null,
        int page = 1,
        int pageSize = 20,
        CancellationToken cancellationToken = default)
    {
        RestRequest request = new RestRequest("api/v1/tasks")
            .AddQueryParameter("page", page)
            .AddQueryParameter("pageSize", pageSize);

        if (status is not null)
        {
            request.AddQueryParameter("status", status);
        }

        TaskDto[]? tasks = await _client.GetAsync<TaskDto[]>(request, cancellationToken);
        return tasks ?? Array.Empty<TaskDto>();
    }

    /// <summary>Creates a new task.</summary>
    public async Task<TaskDto?> CreateTaskAsync(
        CreateTaskRequest createRequest,
        CancellationToken cancellationToken = default)
    {
        RestRequest request = new RestRequest("api/v1/tasks")
            .AddJsonBody(createRequest);

        return await _client.PostAsync<TaskDto>(request, cancellationToken);
    }

    /// <summary>Updates an existing task.</summary>
    public async Task<TaskDto?> UpdateTaskAsync(
        int taskId,
        UpdateTaskRequest updateRequest,
        CancellationToken cancellationToken = default)
    {
        RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
            .AddUrlSegment("taskId", taskId)
            .AddJsonBody(updateRequest);

        return await _client.PutAsync<TaskDto>(request, cancellationToken);
    }

    /// <summary>Deletes a task by its identifier.</summary>
    public async Task<bool> DeleteTaskAsync(
        int taskId,
        CancellationToken cancellationToken = default)
    {
        RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
            .AddUrlSegment("taskId", taskId);

        RestResponse response = await _client.ExecuteDeleteAsync(request, cancellationToken);
        return response.IsSuccessful;
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _client.Dispose();
            _disposed = true;
        }
    }
}

// Usage
using TasksApiClient client = new("https://api.example.com");

IReadOnlyList<TaskDto> tasks = await client.GetTasksAsync(status: "pending");
TaskDto? newTask = await client.CreateTaskAsync(new("New task"));

Advanced Features

Authentication

using RestSharp;
using RestSharp.Authenticators;

// Basic authentication
RestClientOptions basicOptions = new("https://api.example.com")
{
    Authenticator = new HttpBasicAuthenticator("username", "password")
};
RestClient basicClient = new(basicOptions);

// JWT Bearer token
RestClientOptions jwtOptions = new("https://api.example.com")
{
    Authenticator = new JwtAuthenticator("your-jwt-token")
};
RestClient jwtClient = new(jwtOptions);

// OAuth2 with token refresh
RestClientOptions oauth2Options = new("https://api.example.com")
{
    Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("access-token", "Bearer")
};
RestClient oauth2Client = new(oauth2Options);

// API Key in header
RestClient apiKeyClient = new("https://api.example.com");
apiKeyClient.AddDefaultHeader("X-API-Key", "your-api-key");

Request Configuration

using RestSharp;

RestRequest request = new RestRequest("api/v1/products")
    // URL segments
    .AddUrlSegment("version", "v1")
    // Query parameters
    .AddQueryParameter("category", "electronics")
    .AddQueryParameter("inStock", true)
    .AddQueryParameter("minPrice", 100.00m)
    // Headers
    .AddHeader("Accept-Language", "en-US")
    .AddHeader("X-Request-Id", Guid.NewGuid().ToString())
    // Cookies
    .AddCookie("session", "abc123", "/", "api.example.com")
    // JSON body
    .AddJsonBody(new { name = "Product", price = 99.99 });

// Or XML body
request.AddXmlBody(new ProductXmlModel { Name = "Product" });

File Uploads

using RestSharp;

RestRequest uploadRequest = new RestRequest("api/v1/files/upload", Method.Post);

// Upload from file path
uploadRequest.AddFile("document", "path/to/document.pdf", "application/pdf");

// Upload from byte array
byte[] imageBytes = await File.ReadAllBytesAsync("image.png");
uploadRequest.AddFile("image", imageBytes, "image.png", "image/png");

// Upload from stream
await using FileStream stream = File.OpenRead("data.csv");
uploadRequest.AddFile("data", () => stream, "data.csv", "text/csv");

// Add form fields with file
uploadRequest.AddParameter("description", "My uploaded files");
uploadRequest.AddParameter("category", "documents");

RestResponse response = await client.ExecutePostAsync(uploadRequest);

Error Handling

using RestSharp;

RestRequest request = new("api/v1/tasks/999");
RestResponse<TaskDto> response = await client.ExecuteGetAsync<TaskDto>(request);

if (response.IsSuccessful)
{
    TaskDto task = response.Data!;
    Console.WriteLine($"Task: {task.Title}");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
    Console.WriteLine("Task not found");
}
else if (response.ErrorException is not null)
{
    // Network or deserialization error
    Console.WriteLine($"Error: {response.ErrorMessage}");
    Console.WriteLine($"Exception: {response.ErrorException.Message}");
}
else
{
    // HTTP error with response body
    Console.WriteLine($"HTTP {(int)response.StatusCode}: {response.StatusDescription}");
    Console.WriteLine($"Content: {response.Content}");
}

Dependency Injection with IHttpClientFactory

RestSharp 113+ supports Microsoft.Extensions.DependencyInjection:

using Microsoft.Extensions.DependencyInjection;
using RestSharp;

IServiceCollection services = new ServiceCollection();

// Register RestClient with IHttpClientFactory
services.AddRestClient("tasks-api", (IServiceProvider sp, RestClientOptions options) =>
{
    options.BaseUrl = new Uri("https://api.example.com");
    options.Timeout = TimeSpan.FromSeconds(30);
});

// Resolve and use
IServiceProvider provider = services.BuildServiceProvider();
RestClient client = provider.GetRequiredService<RestClient>();
✅ Pros

  • Mature & stable - 13+ years of development, .NET Foundation project
  • Intuitive API - Easy to learn and use
  • Flexible serialization - JSON, XML, CSV with pluggable serializers
  • Rich authentication - OAuth 1/2, Basic, JWT, custom
  • No code generation - Quick prototyping without build-time steps
  • Active community - Discord server, StackOverflow support

⚠️ Considerations

  • Memory overhead - Higher allocations than raw HttpClient
  • No type-safe interface - Manual request building required
  • No compile-time validation - API contracts not verified at build time
  • v107+ breaking changes - Significant API changes from earlier versions

Full Sample

See the full sample on GitHub: https://github.com/BenjaminAbt/dotnet.rest-samples

Further Reading