DEV Community

Arash Zand
Arash Zand

Posted on • Originally published at Medium

Optimizing API Latency in C# .NET Applications

API latency plays a crucial role in performance and user experience. High latency frustrates users, reduces scalability, and increases infrastructure costs. This guide dives deep into causes, measurement, and optimization strategies for C# .NET APIs.


1. Understanding API Latency

API latency is the total time from a client sending a request to receiving a response — covering network transmission, server-side processing, and database interactions.

Types of latency:

  • Network latency — distance, bandwidth, congestion. Fix: CDNs.
  • Processing latency — inefficient code, blocking operations. Fix: async programming.
  • Database latency — slow queries, missing indexes. Fix: query optimization, caching, connection pooling.
Client → (Network) → API Gateway → (Processing) → Database → (DB latency) → API Gateway → Client
Enter fullscreen mode Exit fullscreen mode

2. Measuring API Latency

Stopwatch — quick and surgical:

var stopwatch = Stopwatch.StartNew();
await ProcessRequestAsync();
stopwatch.Stop();
Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} ms");
Enter fullscreen mode Exit fullscreen mode

Middleware timing — covers every request:

public class LatencyMiddleware
{
    private readonly RequestDelegate _next;

    public LatencyMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        var sw = Stopwatch.StartNew();
        await _next(context);
        sw.Stop();
        Console.WriteLine($"Request latency: {sw.ElapsedMilliseconds} ms");
    }
}
Enter fullscreen mode Exit fullscreen mode

Register in Program.cs:

app.UseMiddleware<LatencyMiddleware>();
Enter fullscreen mode Exit fullscreen mode

Application Insights — production-grade:

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(
        Configuration["ApplicationInsights:InstrumentationKey"]);
}
Enter fullscreen mode Exit fullscreen mode

Other tools: Postman (endpoint testing), JMeter (load testing), Grafana + Prometheus (dashboards), Jaeger / OpenTelemetry (distributed tracing).


3. Latency in C# .NET — How Requests Flow

Client → Web Server (Kestrel/IIS) → Middleware Pipeline → Controller → DB/Logic → Middleware → Response
Enter fullscreen mode Exit fullscreen mode

Synchronous vs asynchronous — the single biggest lever:

// ❌ Synchronous — blocks the thread
public IActionResult GetData()
{
    var data = _service.GetData();
    return Ok(data);
}

// ✅ Asynchronous — frees the thread for other requests
public async Task<IActionResult> GetDataAsync()
{
    var data = await _service.GetDataAsync();
    return Ok(data);
}
Enter fullscreen mode Exit fullscreen mode

Common bottlenecks:

Bottleneck Fix
Blocking I/O async/await throughout
Slow DB queries Indexes, AsNoTracking() for reads
Heavy middleware Remove unnecessary steps, async logging
Large serialization System.Text.Json, smaller payloads

4. Optimizing API Latency — Best Practices

Async/Await

public async Task<IActionResult> GetUserDataAsync()
{
    var data = await _databaseService.GetUserDataAsync();
    return Ok(data);
}
Enter fullscreen mode Exit fullscreen mode

In-Memory Caching

public async Task<User> GetUserByIdAsync(int userId)
{
    if (!_memoryCache.TryGetValue(userId, out User user))
    {
        user = await _databaseService.GetUserByIdAsync(userId);
        _memoryCache.Set(userId, user, TimeSpan.FromMinutes(10));
    }
    return user;
}
Enter fullscreen mode Exit fullscreen mode

Response Compression

public void Configure(IApplicationBuilder app)
{
    app.UseResponseCompression();
}
Enter fullscreen mode Exit fullscreen mode

Efficient Serialization

private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

[HttpGet]
public IActionResult GetUser(int userId)
{
    var user = _databaseService.GetUserById(userId);
    return Content(JsonSerializer.Serialize(user, _jsonOptions), "application/json");
}
Enter fullscreen mode Exit fullscreen mode

5. Advanced Techniques

Message Queues — offload non-urgent work

public async Task<IActionResult> ProcessOrderAsync(Order order)
{
    await _messageQueue.SendAsync(order);
    return Accepted(); // responds immediately
}
Enter fullscreen mode Exit fullscreen mode

Distributed Caching with Redis

public async Task<User> GetUserByIdAsync(int userId)
{
    var cache = _redis.GetDatabase();
    var cached = await cache.StringGetAsync(userId.ToString());

    if (!cached.IsNullOrEmpty)
        return JsonSerializer.Deserialize<User>(cached);

    var user = await _databaseService.GetUserByIdAsync(userId);
    await cache.StringSetAsync(
        userId.ToString(),
        JsonSerializer.Serialize(user),
        TimeSpan.FromMinutes(10));

    return user;
}
Enter fullscreen mode Exit fullscreen mode

Database Sharding & HTTP/2

For high-volume systems, sharding distributes data across DB instances. Upgrading to HTTP/2 or HTTP/3 enables multiplexing — multiple requests over a single connection, reducing handshake overhead.


6. Case Study: E-Commerce Checkout API

Problem: Checkout averaging 2–3 seconds. Identified via Application Insights + SQL Profiler:

  • Synchronous inventory service calls
  • Missing indexes on the orders table

Fixes applied:

  1. Refactored inventory check to async/await
  2. Added indexes on orders table, optimized inventory queries
  3. Redis caching for inventory data that rarely changes

Result: Average checkout response dropped from 3 seconds → under 500ms.


7. Monitoring in Production

  • Azure Monitor — API performance and resource utilization
  • Prometheus + Grafana — real-time metrics and dashboards
  • New Relic — end-to-end latency per endpoint
  • Serilog — structured async logging

Set up alerts for latency thresholds so the team can respond to regressions before users notice.


Conclusion

Reducing API latency is not a one-time task. The core levers are: async programming, database optimization, caching (in-memory and distributed), payload compression, and middleware hygiene. Pair those with ongoing monitoring and load testing, and your .NET API stays fast as traffic scales.


Originally published on Medium.

Top comments (0)