ZetCode

C# GET/POST request

last modified October 16, 2026

In this article we show how to send HTTP GET and POST requests in C# with HttpClient.

HTTP

The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web.

The HTTP GET method requests a representation of the specified resource. Requests using GET should only retrieve data. Parameters are sent in the query string of the URL.

The HTTP POST method sends data to the server in the body of the request. It is often used when submitting a web form or when creating a resource in a REST API.

In the examples, we use httpbin.org, a freely available HTTP request and response service, which returns the details of our requests in JSON, and example.com, a small page reserved for documentation examples.

Note: Older code uses the WebRequest, HttpWebRequest, and WebClient classes. They are obsolete since .NET 6 and the compiler reports the SYSLIB0014 warning for them. New code should use HttpClient.

C# GET request

The GetStringAsync method sends a GET request and returns the body of the response as a string.

Program.cs
using var client = new HttpClient();

string content = await client.GetStringAsync("https://example.com");
Console.WriteLine(content);

The program fetches the contents of a small web page and prints the HTML code to the console.

$ dotnet run
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>

In our examples, we create a client for a single request. In a real application, an HttpClient instance should be created once and reused for many requests, because each instance holds its own pool of connections. In ASP.NET Core and other applications with dependency injection, we would use IHttpClientFactory.

C# response status code

The GetAsync method returns an HttpResponseMessage, which gives access to the status code, the headers, and the body of the response.

Program.cs
using var client = new HttpClient();

using var response = await client.GetAsync("https://httpbin.org/status/404");

Console.WriteLine((int) response.StatusCode);
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.IsSuccessStatusCode);

try
{
    response.EnsureSuccessStatusCode();
}
catch (HttpRequestException e)
{
    Console.WriteLine(e.Message);
}

We request a resource that returns the 404 Not Found status.

Console.WriteLine(response.IsSuccessStatusCode);

The IsSuccessStatusCode property is true for status codes in the range 200-299.

response.EnsureSuccessStatusCode();

The EnsureSuccessStatusCode method throws an HttpRequestException if the request was not successful. Note that GetAsync itself does not throw for error status codes, while GetStringAsync does.

$ dotnet run
404
NotFound
False
Response status code does not indicate success: 404 (NOT FOUND).

C# GET request with query parameters

Query parameters are appended to the URL. Their values must be encoded, because characters such as spaces or & have a special meaning in a URL.

Program.cs
using System.Net.Http.Json;

using var client = new HttpClient();

var name = "John Doe";
var occupation = "gardener & florist";

var url = $"https://httpbin.org/get?name={Uri.EscapeDataString(name)}" +
    $"&occupation={Uri.EscapeDataString(occupation)}";

Console.WriteLine(url);

var res = await client.GetFromJsonAsync<HttpBinResponse>(url);

foreach (var (key, value) in res!.Args)
{
    Console.WriteLine($"{key}: {value}");
}

record HttpBinResponse(Dictionary<string, string> Args);

We send two query parameters. The httpbin service returns them in the args field of its JSON response.

var url = $"https://httpbin.org/get?name={Uri.EscapeDataString(name)}" +
    $"&occupation={Uri.EscapeDataString(occupation)}";

The Uri.EscapeDataString method encodes the values.

var res = await client.GetFromJsonAsync<HttpBinResponse>(url);

The GetFromJsonAsync extension method from the System.Net.Http.Json namespace sends a GET request and deserializes the JSON response into the given type. The property names are matched case-insensitively.

$ dotnet run
https://httpbin.org/get?name=John%20Doe&occupation=gardener%20%26%20florist
name: John Doe
occupation: gardener & florist

C# POST request with JSON data

The PostAsJsonAsync extension method serializes an object to JSON and sends it in the body of a POST request.

Program.cs
using System.Net.Http.Json;

using var client = new HttpClient();

var user = new User("John Doe", "gardener");

using var response = await client.PostAsJsonAsync("https://httpbin.org/post", user);
response.EnsureSuccessStatusCode();

var res = await response.Content.ReadFromJsonAsync<HttpBinResponse>();

Console.WriteLine(res!.Headers["Content-Type"]);
Console.WriteLine(res.Data);
Console.WriteLine(res.Json);

record User(string Name, string Occupation);
record HttpBinResponse(string Data, User Json, Dictionary<string, string> Headers);

We send a user object. The httpbin service echoes the request: the data field contains the raw body and the json field the parsed JSON.

using var response = await client.PostAsJsonAsync("https://httpbin.org/post", user);

The object is serialized with System.Text.Json using the web defaults; therefore, the property names are in camel case. The Content-Type header is set to application/json.

var res = await response.Content.ReadFromJsonAsync<HttpBinResponse>();

We read the response body and deserialize it with ReadFromJsonAsync.

$ dotnet run
application/json; charset=utf-8
{"name":"John Doe","occupation":"gardener"}
User { Name = John Doe, Occupation = gardener }

C# POST request with StringContent

If we already have the JSON as a string, we send it with StringContent.

Program.cs
using System.Text;
using System.Text.Json;

using var client = new HttpClient();

var json = """{"name": "John Doe", "occupation": "gardener"}""";
using var content = new StringContent(json, Encoding.UTF8, "application/json");

using var response = await client.PostAsync("https://httpbin.org/post", content);
string body = await response.Content.ReadAsStringAsync();

using var doc = JsonDocument.Parse(body);
var sent = doc.RootElement.GetProperty("json");

Console.WriteLine(sent.GetProperty("name").GetString());
Console.WriteLine(sent.GetProperty("occupation").GetString());

The JSON data is written as a raw string literal. The StringContent constructor takes the text, the encoding, and the media type. We read the response as a string and parse it with JsonDocument.

$ dotnet run
John Doe
gardener

C# POST request with form data

HTML forms send their data encoded as application/x-www-form-urlencoded. The FormUrlEncodedContent class creates such content from name/value pairs.

Program.cs
using System.Net.Http.Json;

using var client = new HttpClient();

var values = new Dictionary<string, string>
{
    ["name"] = "John Doe",
    ["occupation"] = "gardener"
};

using var data = new FormUrlEncodedContent(values);
using var response = await client.PostAsync("https://httpbin.org/post", data);

var res = await response.Content.ReadFromJsonAsync<HttpBinResponse>();

Console.WriteLine(res!.Headers["Content-Type"]);

foreach (var (key, value) in res.Form)
{
    Console.WriteLine($"{key}: {value}");
}

record HttpBinResponse(Dictionary<string, string> Form, Dictionary<string, string> Headers);

The data is stored in a dictionary. The httpbin service returns the parsed form fields in the form field of the response.

$ dotnet run
application/x-www-form-urlencoded
name: John Doe
occupation: gardener

C# client configuration and headers

A client can be configured with a base address, a timeout, and default headers that are sent with every request.

Program.cs
using System.Net.Http.Json;

using var client = new HttpClient
{
    BaseAddress = new Uri("https://httpbin.org/"),
    Timeout = TimeSpan.FromSeconds(10)
};

client.DefaultRequestHeaders.UserAgent.ParseAdd("ZetCodeApp/1.0");
client.DefaultRequestHeaders.Accept.ParseAdd("application/json");

var res = await client.GetFromJsonAsync<HttpBinResponse>("headers");

Console.WriteLine(res!.Headers["User-Agent"]);
Console.WriteLine(res.Headers["Accept"]);

record HttpBinResponse(Dictionary<string, string> Headers);

The /headers endpoint of httpbin returns the headers it received.

BaseAddress = new Uri("https://httpbin.org/"),

Relative URLs, such as headers, are resolved against the base address. The base address should end with a slash.

client.DefaultRequestHeaders.UserAgent.ParseAdd("ZetCodeApp/1.0");

By default, HttpClient sends no User-Agent header. Some servers reject such requests, so it is a good practice to set it.

$ dotnet run
ZetCodeApp/1.0
application/json

Source

Make HTTP requests with the HttpClient class

In this article we have shown how to send GET and POST requests in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.