Arash Zand

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.