Table of contents

Is there any way to make synchronous requests with HttpClient (C#)?

The Short Answer

No, HttpClient in C# does not provide any built-in synchronous methods for making HTTP requests. All HTTP methods are asynchronous by design, requiring the use of async and await keywords. This design prevents blocking operations that could freeze UI threads or degrade server performance.

Why HttpClient is Async-Only

Microsoft designed HttpClient to be asynchronous because:

  • UI Responsiveness: Prevents freezing user interfaces during network operations
  • Server Scalability: Allows web servers to handle more concurrent requests
  • Resource Efficiency: Avoids blocking threads while waiting for network responses
  • Modern Best Practices: Aligns with async/await patterns introduced in C# 5.0

Recommended Approach: Use Async/Await

The proper way to use HttpClient is with asynchronous methods:

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    private static readonly HttpClient client = new HttpClient();

    static async Task Main(string[] args)
    {
        try
        {
            HttpResponseMessage response = await client.GetAsync("https://api.example.com/data");
            response.EnsureSuccessStatusCode();

            string content = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Response: {content}");
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"Request error: {e.Message}");
        }
    }
}

Forcing Synchronous Execution (Not Recommended)

If you absolutely must call async code synchronously, here are the methods, ranked from most to least problematic:

Option 1: Using .Result (High Risk of Deadlocks)

using System;
using System.Net.Http;

class Program
{
    static void Main(string[] args)
    {
        using var client = new HttpClient();

        try
        {
            // WARNING: High risk of deadlocks, especially in UI/ASP.NET contexts
            HttpResponseMessage response = client.GetAsync("https://api.example.com/data").Result;
            string content = response.Content.ReadAsStringAsync().Result;

            Console.WriteLine(content);
        }
        catch (AggregateException ex)
        {
            // .Result wraps exceptions in AggregateException
            Console.WriteLine($"Error: {ex.InnerException?.Message}");
        }
    }
}

Option 2: Using GetAwaiter().GetResult() (Slightly Better)

using System;
using System.Net.Http;

class Program
{
    static void Main(string[] args)
    {
        using var client = new HttpClient();

        try
        {
            // Slightly better than .Result - doesn't wrap exceptions
            HttpResponseMessage response = client.GetAsync("https://api.example.com/data")
                .GetAwaiter().GetResult();
            string content = response.Content.ReadAsStringAsync()
                .GetAwaiter().GetResult();

            Console.WriteLine(content);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

Option 3: Task.Run with GetAwaiter().GetResult() (Safer)

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        // Run async code on thread pool to avoid deadlocks
        Task.Run(async () =>
        {
            using var client = new HttpClient();

            HttpResponseMessage response = await client.GetAsync("https://api.example.com/data");
            response.EnsureSuccessStatusCode();

            string content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }).GetAwaiter().GetResult();
    }
}

Risks of Synchronous Execution

1. Deadlocks

Most common in UI applications and ASP.NET:

// DON'T DO THIS in WinForms/WPF button click handlers
private void Button_Click(object sender, EventArgs e)
{
    // This will deadlock!
    string result = client.GetAsync("https://api.example.com").Result;
}

// DO THIS instead
private async void Button_Click(object sender, EventArgs e)
{
    string result = await client.GetAsync("https://api.example.com");
}

2. Thread Pool Starvation

In high-load scenarios, blocking threads can exhaust the thread pool:

// Bad: Blocks threads in a web server
public IActionResult GetData()
{
    var result = client.GetAsync("https://api.example.com").Result;
    return Ok(result);
}

// Good: Keeps threads available
public async Task<IActionResult> GetData()
{
    var result = await client.GetAsync("https://api.example.com");
    return Ok(result);
}

3. Performance Impact

Synchronous calls waste system resources and reduce scalability.

Best Practices

  1. Always use async/await when possible
  2. Make your methods async all the way up the call stack
  3. Use ConfigureAwait(false) in library code to avoid deadlocks:
public async Task<string> GetDataAsync()
{
    using var client = new HttpClient();
    var response = await client.GetAsync("https://api.example.com")
        .ConfigureAwait(false);
    return await response.Content.ReadAsStringAsync()
        .ConfigureAwait(false);
}
  1. Handle exceptions properly with try-catch blocks
  2. Reuse HttpClient instances or use IHttpClientFactory

Alternative for Legacy Code

If you're working with legacy synchronous code that can't be refactored, consider using a wrapper service:

public class SyncHttpService
{
    private static readonly HttpClient client = new HttpClient();

    public string GetSync(string url)
    {
        return Task.Run(async () =>
        {
            var response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }).GetAwaiter().GetResult();
    }
}

Remember: The async/await pattern is the foundation of modern C# development. Embrace it for better, more scalable applications.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon