# HttpClient Basics

HttpClient sends HTTP requests from C#. Give it a base address, await GetAsync for a response, check its status code, and read its body. A failed status is an answer to handle; a server that can't be reached throws an HttpRequestException.

Journeyman · C# · REST API · Updated November 3, 2026

## After this lesson you can

- Send a GET request with HttpClient, and read the response's status code and body
- Handle an unsuccessful status code and a server that can't be reached

## Key takeaways

- `HttpClient` sends requests from C#: set its `BaseAddress`, then `await client.GetAsync(...)` for an `HttpResponseMessage` with a status code and a body.
- A `404` or `400` is an answer, not an error in your program: check `response.IsSuccessStatusCode` before you trust the body.
- When a failed status means you can't go on, call `response.EnsureSuccessStatusCode()`: it throws an `HttpRequestException` for anything but 2xx.
- When the API can't be reached at all, `GetAsync` throws an `HttpRequestException`, so wrap calls in a `try`.
- Make one `HttpClient` and use it for all your requests, rather than a new one for each.

So far, `.http` files have been your API's client. Now a C# program will be. Any program can call an API: a console app, a desktop app, even another web API that needs data from a third one. In .NET, they all use **`HttpClient`**.

The examples in this chapter call the categories API you built in *HTTP and Web APIs*. Start it (**Ctrl+F5** in its own Visual Studio window) and leave it running, then create a new **console app** for the client. Use your API's own address wherever you see `http://localhost:5179`.

## A first request

```csharp
using HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:5179/");

HttpResponseMessage response = await client.GetAsync("api/categories/8");

Console.WriteLine((int)response.StatusCode);
Console.WriteLine(response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

```text
200
OK
{"categoryId":8,"categoryName":"Seafood","description":"Seaweed and fish"}
```

- **`BaseAddress`** is the start of every address. `GetAsync("api/categories/8")` is added to it. (Keep the `/` at the end of the base address, and none at the start of the path, so they join correctly.)
- **`GetAsync`** sends a `GET` and returns an **`HttpResponseMessage`**: the whole response, as an object. It's async, because the answer comes over the network.
- **`response.StatusCode`** is the status, as an `HttpStatusCode` value: `OK`, `NotFound`, `Created`. Cast it to `int` for the number.
- **`response.Content.ReadAsStringAsync()`** reads the body as text: here, JSON. The next lesson turns it into a `Category` object.

## When the answer isn't OK

A `404` or a `400` isn't an error in your program: it's an answer, and your code decides what to do with it. **`IsSuccessStatusCode`** is `true` for any 2xx code:

```csharp
using System.Net;

using HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:5179/");

foreach (int id in new[] { 8, 300 })
{
    HttpResponseMessage response = await client.GetAsync($"api/categories/{id}");
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine($"{id}: found");
    }
    else if (response.StatusCode == HttpStatusCode.NotFound)
    {
        Console.WriteLine($"{id}: there's no such category");
    }
    else
    {
        Console.WriteLine($"{id}: the API answered {(int)response.StatusCode}");
    }
}
```

```text
8: found
300: there's no such category
```

(`HttpStatusCode` lives in `System.Net`, hence the `using System.Net;` at the top.)

When a failed status means you can't go on, **`response.EnsureSuccessStatusCode()`** throws an `HttpRequestException` for anything but 2xx.

## When the server can't be reached

If the API isn't running, or the address is wrong, there's no answer at all. `GetAsync` throws an **`HttpRequestException`**, so wrap calls in a `try`, like any work that leaves your program:

```csharp
using HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:5999/");

try
{
    HttpResponseMessage response = await client.GetAsync("api/categories");
    Console.WriteLine((int)response.StatusCode);
}
catch (HttpRequestException)
{
    Console.WriteLine("The API can't be reached. Is it running?");
}
```

```text
The API can't be reached. Is it running?
```

Nothing listens on port 5999, so the request never gets an answer. Two kinds of failure, two ways to handle them:

| What happened | How you find out |
|---|---|
| The API answered, but not with success: `404`, `400`, `500` | `response.IsSuccessStatusCode` is `false`; check `response.StatusCode` |
| The API couldn't be reached at all | `GetAsync` throws an `HttpRequestException` |

## One HttpClient, used again and again

Make **one** `HttpClient` and use it for all your requests, rather than a new one for each. Each `HttpClient` holds network connections open for a while, and a program that makes thousands of them can run out. A console app keeps one in a variable, as here. (ASP.NET Core apps share them through dependency injection instead, with `builder.Services.AddHttpClient()`; Microsoft's docs show how when you need it.)

## Check yourself

2 questions to try on the lesson page.

## So what

`HttpClient` is the browser's job done by code: send a request, get a response, read its status and body. Check the status before you trust the body, and catch `HttpRequestException` for the times there's no answer at all.

## Key terms

- [HttpClient](https://www.ask-codey.com/glossary/httpclient-c-sharp)
- [HttpResponseMessage](https://www.ask-codey.com/glossary/httpresponsemessage)

## Cheat sheets

- [HttpClient Basics](https://www.ask-codey.com/cheat-sheets/calling-apis-with-httpclient-httpclient-basics-cheat-sheet)

Source: https://www.ask-codey.com/lessons/calling-apis-with-httpclient-httpclient-basics
