- Home
- .NET tutorials
- TypedResults or Results for Minimal API responses?
TypedResults or Results for Minimal API responses?
Published: Monday 25 August 2025
I found out that you can use either TypedResults
or Results
in minimal API responses. I wondered why they were two of them and importantly which one should we be using?
On the surface, it looks like it's doing the same thing:
// ProductDto.cs
public record ProductDto(int Id, string Name);
app.MapGet("/product/results",
() => Results.Ok(new ProductDto(1, "Watch")));
app.MapGet("/product/typed-results",
() => TypedResults.Ok(new ProductDto(1, "Watch")));
I was confused. The only difference is the name of the type. But then I discovered something when looking at the OpenAPI documentation in more detail:
...
"paths": {
"/product/results": {
"get": {
"tags": [
"RoundTheCode.MinimalApi"
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/product/typed-results": {
"get": {
"tags": [
"RoundTheCode.MinimalApi"
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProductDto"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ProductDto": {
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
}
}
}
}
},
...
The TypedResults
path specifies that the content comes from the ProductDto
. The Results
path doesn't do that.
This is confirmed when I look at my Scalar document:

The Results type doesn't return any metadata for the response

The TypedResults type does return any metadata for the response
So with that in mind it would recommended to use the TypedResults
type. But we've only tried it with returning an object type. What about some of the other response types? Do they do the same thing with Results
and TypedResults
?
The different return types
I was overwhelmed by the number of different return types there are. As our time is precious, I've gone through the ones that I think are the most common:
JSON
This returns a response in a JSON format. You can use anonymous types like in this snippet:
// Program.cs
app.MapGet("/json", () => TypedResults.Json(new { Id = 1 }));
By default, minimal API apps will use the web defaults for returning JSON. If you wish to change it, you can call ConfigureHttpJsonOptions
extension method in the IServiceCollection
type.
Here we are ensuring that the property naming policy is camel case, a pretty response is returned and nullable properties are not returned:
// Program.cs
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
There are a large number of JSON options that you can configure. You can also configure JSON serialisation for an individual route.
But I wanted to change one for a specific route! You can do that by creating your own JsonSerializerOptions
class, and then add it to the response.
Here we are changing the JSON naming policy to snake case upper but only for the /json-custom
route:
// Program.cs
app.MapGet("/json-custom",
() => Results.Json(new { Id = 1 }, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseUpper
}));
Custom status code
I found that a number of status codes have their own method like Ok
and NotFound
. But if you find that one doesn't exist, you can use the StatusCode
method to return it, like this one:
// Program.cs
app.MapGet("/im-a-teapot", () => TypedResults.StatusCode(StatusCodes.Status418ImATeapot));
Internal server error
Hopefully you won't need this one. But If you ever have to return an internal server error and a message as part of the response, you can call the InternalServerError
method:
// Program.cs
app.MapGet("/internal-server-error",
() => TypedResults.InternalServerError("Broken"));
Problem and ValidationProblem
When there is a problem with your endpoint, you can use either the Problem
or ValidationProblem
extension method to return a response:
// Program.cs
app.MapGet("/problem",
() => TypedResults.Problem("There is an error", statusCode: StatusCodes.Status403Forbidden));
app.MapGet("/validation-problem",
() => {
var problems = new Dictionary<string, string[]> { { "Id", ["Missing"] } };
return TypedResults.ValidationProblem(problems);
});
ValidationProblem
will always return a 400 response so this is ideal for form requests that have errors. With the Problem
extension method, you can choose which status code to return.
Text
Returning plain text responses is done by using the Text
extension method. I've used this in the past to return dynamic robots.txt
files.
// Program.cs
app.MapGet("/robots.txt", () => TypedResults.Text("User-agent: *"));
Redirect
The Redirect
extension method allows you to redirect a route to another route. And you can choose whether to make it permanent by setting the permanent
parameter. If it's set to true
, it will return a 301 response. Otherwise, it will return a 302 response.
// Program.cs
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true));
app.MapGet("/temporary-redirect", () => TypedResults.Redirect("/", permanent: false));
File
If you want to download a file, you can return the File
extension method.
However it works differently if you use Results
or TypedResults
. If you use Results
, you can specify the file name. But if you use TypedResults
, you need to download the file into a stream and then return that stream as part of the response.
You can also specify the mime type and the name of the file downloaded for each of these:
// Program.cs
app.MapGet("/file", () =>
{
var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}/File/RoundTheCode.txt";
return Results.File(
filePath,
contentType: "text/plain",
fileDownloadName: "RoundTheCode.txt"
);
});
app.MapGet("/file-from-stream", async() =>
{
var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}/File/RoundTheCode.txt";
return TypedResults.File(
await File.ReadAllBytesAsync(filePath),
contentType: "text/plain",
fileDownloadName: "RoundTheCode.txt"
);
});
Returning different response types
I needed to return different response types for a route. I wanted to throw a not found error if I couldn't find an ID, otherwise I would return a ProductDto
type as an OK response.
This is the clever part as you can specify that the Results
type is going to be returned. But you can also set generic types within that. This examples specifies that the endpoint will return an Ok
type with a ProductDto
type, as well as a NotFound
type.
// Program.cs
app.MapGet("/different-responses/{id:int}",
Results<Ok<ProductDto>, NotFound>
(int id) =>
{
if (id > 1)
{
return TypedResults.NotFound();
}
return TypedResults.Ok(new ProductDto(id, "Abc"));
});
Manually specifying the response types
Even with using TypedResults
, there are some response types that can have a number of status codes returned. For example, the Redirect
response type can return a 301 or 302 response code. As a result, the Scalar document will output an expected 200 response if there is more than one. This also happens with the StatusCode
and Problem
response types that we covered.
But you can be specific about which response codes are returned in an minimal API route. This is done by calling the Produces
extension method. And you can call it multiple times if you have more than one expected response code:
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true))
.Produces(StatusCodes.Status301MovedPermanently);
You can also add attributes to the endpoint handler to override the endpoint summary, description and specify which response code it returns:
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true))
.Produces(StatusCodes.Status301MovedPermanently);
You'll see how it looks in the Scalar document when you watch this video:
I also cover TypedResults
and Results
in it as well as going through some of the different response types.
And whilst watching the video, you can download the code example so you can follow along and try these examples out for yourself.
Final thoughts
So TypedResults
or Results
? Well it has to be TypedResults
as it returns a more specific response type and it will often add the expected response code to the OpenAPI documentation.
As mentioned, I found that some TypedResults
methods like Redirect
didn't add the correct response codes to the OpenAPI schema. But that is because more than one status code could be returned.
In addition, I'm surprised about the number of response types that have their own methods. Just because it's called minimal APIs doesn't mean you have a minimal number of response types to return.
Latest tutorials

