How do I read the HTTP status code from an HttpClient (C#) response?
When working with web scraping or API integration in C#, understanding HTTP status codes is crucial for proper error handling and flow control. The HttpClient class provides straightforward access to status codes through the HttpResponseMessage
object.
Quick Answer
The HTTP status code is available through the StatusCode
property of the HttpResponseMessage
object:
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class HttpStatusExample
{
public static async Task Main()
{
using var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://api.example.com/data");
// Access the status code
int statusCode = (int)response.StatusCode;
Console.WriteLine($"Status Code: {statusCode}");
// Or use the enum value
Console.WriteLine($"Status: {response.StatusCode}");
}
}
Understanding HTTP Status Codes
HTTP status codes are three-digit numbers that indicate the result of an HTTP request:
- 1xx (Informational): Request received, continuing process
- 2xx (Success): Request successfully received, understood, and accepted
- 3xx (Redirection): Further action needs to be taken
- 4xx (Client Error): Request contains bad syntax or cannot be fulfilled
- 5xx (Server Error): Server failed to fulfill a valid request
Accessing Status Codes in Different Ways
Using the StatusCode Property
The most common approach is to access the StatusCode
property directly:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class StatusCodeReader
{
public static async Task CheckStatus()
{
using var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://api.example.com/endpoint");
// Get as HttpStatusCode enum
HttpStatusCode statusCode = response.StatusCode;
// Get as integer
int statusCodeInt = (int)response.StatusCode;
Console.WriteLine($"Status Code (enum): {statusCode}");
Console.WriteLine($"Status Code (int): {statusCodeInt}");
Console.WriteLine($"Reason Phrase: {response.ReasonPhrase}");
}
}
Checking for Success
The IsSuccessStatusCode
property provides a boolean check for 2xx status codes:
public static async Task<string> FetchDataSafely(string url)
{
using var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
Console.WriteLine($"Request failed with status: {response.StatusCode}");
return null;
}
}
Handling Different Status Codes
When web scraping or consuming APIs, you'll want to handle different status codes appropriately:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class StatusCodeHandler
{
public static async Task<string> HandleResponse(string url)
{
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(30);
try
{
HttpResponseMessage response = await client.GetAsync(url);
switch (response.StatusCode)
{
case HttpStatusCode.OK: // 200
Console.WriteLine("Success! Processing content...");
return await response.Content.ReadAsStringAsync();
case HttpStatusCode.Created: // 201
Console.WriteLine("Resource created successfully");
return await response.Content.ReadAsStringAsync();
case HttpStatusCode.NoContent: // 204
Console.WriteLine("Success with no content");
return string.Empty;
case HttpStatusCode.MovedPermanently: // 301
case HttpStatusCode.Found: // 302
Console.WriteLine($"Redirected to: {response.Headers.Location}");
return await HandleResponse(response.Headers.Location.ToString());
case HttpStatusCode.BadRequest: // 400
Console.WriteLine("Bad request - check your parameters");
return null;
case HttpStatusCode.Unauthorized: // 401
Console.WriteLine("Authentication required");
return null;
case HttpStatusCode.Forbidden: // 403
Console.WriteLine("Access forbidden");
return null;
case HttpStatusCode.NotFound: // 404
Console.WriteLine("Resource not found");
return null;
case HttpStatusCode.TooManyRequests: // 429
Console.WriteLine("Rate limit exceeded - implement retry logic");
await Task.Delay(5000); // Wait before retry
return await HandleResponse(url);
case HttpStatusCode.InternalServerError: // 500
Console.WriteLine("Server error - may be temporary");
return null;
case HttpStatusCode.ServiceUnavailable: // 503
Console.WriteLine("Service unavailable - retry later");
return null;
default:
Console.WriteLine($"Unhandled status code: {response.StatusCode}");
return null;
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Request error: {ex.Message}");
return null;
}
catch (TaskCanceledException ex)
{
Console.WriteLine($"Request timeout: {ex.Message}");
return null;
}
}
}
Using EnsureSuccessStatusCode
The EnsureSuccessStatusCode()
method throws an HttpRequestException
if the status code is not in the 2xx range:
public static async Task<string> FetchWithException(string url)
{
using var client = new HttpClient();
try
{
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode(); // Throws if not 2xx
return await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP Error: {ex.Message}");
// Extract status code from exception
if (ex.StatusCode.HasValue)
{
Console.WriteLine($"Status Code: {(int)ex.StatusCode.Value}");
}
throw;
}
}
Implementing Retry Logic Based on Status Codes
For web scraping applications, implementing retry logic with HttpClient for specific status codes is essential:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class RetryHandler
{
private static readonly int[] RetryableStatusCodes =
{
429, // Too Many Requests
500, // Internal Server Error
502, // Bad Gateway
503, // Service Unavailable
504 // Gateway Timeout
};
public static async Task<HttpResponseMessage> GetWithRetry(
string url,
int maxRetries = 3)
{
using var client = new HttpClient();
int retryCount = 0;
while (retryCount <= maxRetries)
{
HttpResponseMessage response = await client.GetAsync(url);
int statusCode = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
return response;
}
if (Array.Exists(RetryableStatusCodes, code => code == statusCode))
{
retryCount++;
if (retryCount <= maxRetries)
{
int delayMs = (int)Math.Pow(2, retryCount) * 1000; // Exponential backoff
Console.WriteLine($"Status {statusCode} - Retrying in {delayMs}ms... (Attempt {retryCount}/{maxRetries})");
await Task.Delay(delayMs);
continue;
}
}
return response;
}
throw new Exception("Max retries exceeded");
}
}
Advanced Status Code Monitoring
For production web scraping applications, monitoring status codes helps identify issues:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public class StatusCodeMonitor
{
private readonly Dictionary<int, int> _statusCodeCounts = new();
public async Task<string> MonitoredRequest(string url)
{
using var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
int statusCode = (int)response.StatusCode;
// Track status code occurrences
if (_statusCodeCounts.ContainsKey(statusCode))
{
_statusCodeCounts[statusCode]++;
}
else
{
_statusCodeCounts[statusCode] = 1;
}
// Log status information
LogStatusCode(response);
return response.IsSuccessStatusCode
? await response.Content.ReadAsStringAsync()
: null;
}
private void LogStatusCode(HttpResponseMessage response)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] " +
$"Status: {(int)response.StatusCode} {response.StatusCode} - " +
$"Reason: {response.ReasonPhrase}");
}
public void PrintStatistics()
{
Console.WriteLine("\nStatus Code Statistics:");
foreach (var kvp in _statusCodeCounts)
{
Console.WriteLine($" {kvp.Key}: {kvp.Value} occurrences");
}
}
}
Comparing with HttpWebRequest
For developers migrating from the older HttpWebRequest
API, here's how status code access compares:
// HttpClient (modern approach)
using var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
HttpStatusCode statusCode = response.StatusCode;
// HttpWebRequest (legacy approach)
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
using HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
HttpStatusCode legacyStatusCode = webResponse.StatusCode;
The HttpClient approach is recommended for modern applications due to better async support and resource management.
Best Practices for Status Code Handling
- Always check status codes: Don't assume requests will succeed
- Use appropriate error handling: Different status codes require different responses
- Implement retry logic: For transient errors (5xx codes, 429)
- Respect rate limits: When receiving 429 status codes, implement backoff strategies
- Log status codes: Essential for debugging and monitoring web scraping operations
- Handle redirects properly: Be aware that HttpClient follows redirects by default
- Use structured exception handling: Catch specific exceptions and extract status codes
Working with Web Scraping APIs
When working with web scraping APIs that provide additional error handling features, you can still access standard HTTP status codes while benefiting from enhanced error messages and retry mechanisms. This is particularly useful when dealing with complex scraping scenarios that require managing multiple requests and responses efficiently.
Conclusion
Reading HTTP status codes from HttpClient responses in C# is straightforward using the StatusCode
property. Proper status code handling is essential for building robust web scraping applications that can gracefully handle errors, implement retry logic, and provide meaningful feedback.
The key is to understand what each status code means and respond appropriately—whether that's retrying the request, logging an error, or taking corrective action. By implementing proper status code checking and error handling, you'll create more reliable and maintainable C# applications for web scraping and API integration.