WebApiClientCore and the two problems it takes on alongside interface declaration
A REST API library with better functionality, performance, and scalability than refit
At a glance
- What is it?
- WebApiClientCore is a REST client library for .NET that declares an API as a C# interface and generates the HTTP calls from it, in the lineage of Refit, which its README names as the product it aims to beat on functionality, performance and scalability. The parts worth evaluating are the source generators that make trimming and ahead-of-time publishing work, the Roslyn analyzers that check your interface declarations, and the benchmark results committed to the repository.
- Who is it for?
- Adopt WebApiClientCore if you have several REST APIs to consume from .NET and want a generated client with analyzers that catch declaration mistakes at compile time rather than at the first request. Do not adopt it for a single API or if you need to inspect or hand-modify the outgoing request in ways the generated shape does not expose.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 75 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Interface declaration as the whole client surface
The first feature in the README is semantic declaration, and the claim is that client development only requires a semantic declaration of C# interfaces. That is the model: you write an interface whose methods carry attributes describing the HTTP verb, the route and the return type, and the library produces the implementation.
The README names the comparison product directly, describing the library as one with better functionality, performance and scalability than Refit, and the topic list includes both Refit and Retrofit, which is a deliberate pair of names from two different ecosystems for the same idea. Declaring an HTTP client as an interface is a pattern with a pedigree, and the reason it exists is that the alternative, a hand-written client class per API, produces hundreds of lines of near-identical code whose only interesting content is the route string.
The value of generation here is not that it saves typing. It is that an interface is a thing a compiler can check, and that is where the rest of this repository's design comes from. A method signature cannot be wrong about its return type. A route can be wrong about a parameter name, and that is a bug that only appears when someone calls that method at runtime. A library that takes an interface can attach a compiler analyzer to the interface and check the declaration itself, and there is a project in this repository whose entire job is doing that.
So the pattern is worth reading in three layers rather than one. The interface is the declaration. The source generator turns it into an implementation, which is what makes trimming and ahead-of-time publishing possible because the code exists at compile time rather than being produced by reflection at run time. The analyzer validates the declaration, which is what makes the declaration safe to rely on. Each layer is a separate project in the top-level listing, and each solves a different failure mode.
The fourth layer is what the interface does not cover, which is everything around the call. Interceptors, filters, logging, retries, caching, token management and authorisation are the concerns that turn a generated client into a production one, and the README lists them as supported aspects rather than as features of the declaration.
Source generators, and why trimming is the test that matters
The feature list says full trimmed and AOT publishing of .NET 8, and the top-level listing shows two source generator projects, one general and one specifically for OpenAPI. That pairing is the most technically interesting thing in the repository.
The problem source generators solve is specific and it is a real production problem. A client library that reflects over an interface at run time to decide which HTTP call to make cannot be trimmed. The .NET trimming and ahead-of-time publishing tools remove code they cannot prove is reachable, and an interface whose only reference is a string in an attribute is exactly the kind of code the trimmer is designed to delete. So a reflection-based generated client either loses its implementations in a trimmed publish or requires a set of preservation attributes scattered through the codebase, which is exactly the kind of manual annotation work the interface model was supposed to avoid.
A source generator moves the work to compile time. The generator sees the interface, and emits the implementation class as C# that the compiler then sees like any other code. The trimmer can prove the implementation is reachable because it is a normal type. Nothing is reflected, nothing needs preserving, and the ahead-of-time compiler can do the same. That is why the feature is phrased as full trimmed and AOT rather than as partial support, and why it is listed as a feature at all rather than as an implementation detail.
The OpenAPI generator is a second use of the same mechanism with a different input. Instead of a human writing the interface, the generator parses a local or remote OpenAPI document and emits the interface. The README describes this as Swagger to code, and lists it as a feature that simplifies the workload of interface declaration. The pipeline is then two generators in series: a document becomes an interface, and the interface becomes an implementation.
That pipeline is where a real project would find its leverage. A service with a large OpenAPI document has an interface generated in seconds rather than written by hand over weeks, and the analyzer then checks the generated code as if a human had written it, which means the two stages catch each other's mistakes. A generator that produces code the analyzer rejects is a generator you fix, not a bug you work around at the call site.
The cost is that a source generator is a compiler component. It runs on every build, in the compiler process, and a generator with a bug produces build errors rather than runtime errors, which is a better failure mode but a more visible one. It also means the library's build tooling has to keep pace with the compiler's own versioning, and the repository pins a particular toolchain accordingly.
The analyzers, which are the feature most likely to save you time
One project in the top level is a Roslyn analyzer package, and the README describes it as code syntax analysis that provides syntax analysis and prompts for interface code declarations to help developers avoid using improper syntax when declaring interfaces.
The description is unglamorous and the feature is the most valuable one in the list, for a reason that is specific to this kind of library. When an interface method is the specification of an HTTP call, the ways to declare it wrongly are all silent. A return type of a complex object where the service returns a string produces a deserialisation error at the first call. A missing route template parameter produces a URL with a literal placeholder in it. An attribute applied to the interface rather than the method, or a method whose name conflicts with an inherited member, produces a generator that either fails at build time or, worse, succeeds and generates the wrong call.
None of those are findable by reading the interface. They are findable by a tool that knows the rules, and that is exactly what a Roslyn analyzer is: a component that runs during compilation, sees the semantic model, and reports diagnostics. The developer sees a squiggle in their editor while they are writing the interface, rather than an exception in production after somebody calls the method.
What makes this feature coherent with the rest of the design is that it is the same idea as the source generator, applied to the declaration instead of the implementation. Both are compile-time components operating on the semantic model. One emits code, the other reports problems. Together they mean the interface is checked in the same build that consumes it, and a project using this library has a single place where an incorrect declaration becomes visible.
For an evaluator, the analyzer package is also the cheapest thing to try. You do not need to wire up an HTTP endpoint or point a generator at an OpenAPI document. You declare one interface, and either the analyzer accepts it or it tells you what is wrong with it. That is a five-minute evaluation with a real answer, and it is the fastest way to find out whether the library's idea of a correct declaration matches yours.
Two benchmark projects, and what a 2.X figure does and does not establish
The README's performance claim is specific enough to be checked: in the BenchmarkDotNet results committed to the repository, the performance is stated to be 2.X times ahead of the similar product Refit under various requests.
Committing the benchmark results is the right thing to do and it is still not the same as trusting the number. BenchmarkDotNet produces output for a specific configuration, on specific hardware, with specific parameters, and the committed results belong to whoever ran it on whatever machine they had. A 2.X figure is meaningless without the machine, because an HTTP client library's overhead is largely fixed cost per request, and fixed cost measured on a fast machine with a warm connection pool says little about a slow one.
The listing helps here. There are two benchmark projects, one general and one specifically for AOT, plus an application project and an AOT application project. The existence of a separate AOT benchmark set is the interesting part, because it tells you the project treats ahead-of-time publishing as a distinct performance characteristic worth measuring rather than a build option. Ahead-of-time publishing can change startup time, reflection behaviour and code layout, and a client library that claims AOT support should show numbers for it.
So the honest reading of the claim is this. The project has measured itself against a named alternative, has committed the raw results, and has separated the AOT case. That is more than most libraries do and it means the number is falsifiable. What it does not mean is that your application will be twice as fast, because the benefit of a lower-overhead client is a fixed saving per request, and whether that saving is visible depends entirely on how much of your request time is the network.
The practical test is cheap. Both benchmark projects are in the repository, so you can run them yourself against the same version of Refit on your own hardware and with your own parameters. If your workload is many small requests, per-request overhead is a large fraction of the total and the difference will show. If it is a few large requests, it will not, and no library change will make it.
Serialisation, extensions, and what the package list says about scope
The feature list mentions diverse serialisation supporting json, xml, form and other custom serialisation methods, and the top-level listing shows how that becomes packages.
There is a core abstractions project and a core project, which is a sensible split: the abstractions hold the interfaces and attributes, and the core holds the implementation. A consumer who wants to substitute part of the behaviour depends only on the abstractions. Then there is an extensions project for Newtonsoft JSON, which is the compatibility package for applications that have not moved to the built-in JSON serialiser, and an extensions project for JSON-RPC, which is the same generation model applied to a different protocol shape. An OAuth extensions project covers the token management the README mentions. A source generator extensions project and an OpenAPI source generator project, already discussed, handle code generation.
The serialiser list deserves a note. JSON, XML and form encoding are not three configurations of one thing. JSON is the default and the easy case. XML requires you to decide on a root element name, a namespace and a date format, and get any of them wrong and the service rejects the document while looking correctly formed. Form encoding is the one that causes bugs most often, because the difference between a field being a query parameter and being a form field is invisible in the interface unless the library makes you say which. A library that supports all three explicitly, and makes the choice a declaration rather than a default, is solving a real problem that a JSON-only client hides.
Two entries in the listing are worth noting for what they imply. There is a test project, so the library tests itself, and there is a skills directory, which in a .NET repository most likely means agent-facing instructions or tooling for contributors. There is also a Chinese-language README alongside the English one and a documentation site, so the project is maintained bilingually with its own docs rather than relying on the repository alone.
The scope question follows from all of this. This is a client library that also carries an aspect pipeline, an authorisation integration, a code generator and an analyzer. Those are four concerns, and each is defensible on its own. Whether they belong in one library is a judgement call, and the answer for most consumers is to take the core and one extension rather than to adopt the whole surface.
Release history, and the two-year gap inside a 2.1.x line
The release list is short and has a shape worth reading carefully.
The three most recent tags are Core_2.1.6 in July 2026, Core_2.1.4 in July 2024 and Core_2.1.3 in June 2024. The last push to the repository was on 2026-07-17, days after the newest tag, and the repository is not archived.
Two things stand out. The tag names are prefixed with the package, so this is a multi-package repository where each package is versioned and tagged independently, which is the right arrangement when an extension package needs to ship a fix without releasing the core. And there is a two-year gap between 2.1.4 and 2.1.6, with the versions on either side of it five weeks apart. A 2.1.x line with a two-year hole is not a project in trouble, since the last push is recent and the newest tag is from the same month, but it does mean the release history is not a reliable signal of development cadence. Code moved and the version number did not.
The 2.1 line itself also matters. A library on a 2.x version has a compatibility promise, and unlike most of the projects in this series that promise is actually being kept across a long gap rather than abandoned at 0.9. For a library you are putting between your application and a third-party service, that is the more valuable property than frequent releases.
The licence is the MIT License, with the text in a `LICENSE` file at the repository root, and the homepage is a documentation site under the project's own organisation. Nothing in the README constrains commercial use, and the repository lives in the same community organisation as the other .NET community projects, so the governance is community rather than vendor.
What the release pattern suggests overall is a project that maintains a stable surface and adds capability through extensions, on a schedule driven by when something needs shipping rather than by a calendar. For an evaluator that is a reasonable profile, provided the extensions you need are the ones being maintained.
Editorial conclusion
Adopt WebApiClientCore if you have several REST APIs to consume from .NET and want a generated client with analyzers that catch declaration mistakes at compile time rather than at the first request. Do not adopt it for a single API or if you need to inspect or hand-modify the outgoing request in ways the generated shape does not expose. Verify first by declaring one interface, building with trimming enabled to confirm the generators emit everything reflection would have lost, running the committed BenchmarkDotNet results on your own hardware rather than trusting the 2.X figure, and reading the analyzer diagnostics, which are the feature most likely to save you the most time.
Frequently asked questions
What is WebApiClientCore?
It is a REST API client library for .NET that declares the API as C# interfaces and generates the HTTP calls from them, described in the README as having better functionality, performance and scalability than Refit. The topic list names both Refit and Retrofit as the pattern's ancestors in different ecosystems.
How does WebApiClientCore support trimming and AOT publishing?
Through source generators. The repository contains a general source generator project and an OpenAPI source generator project, and the generators emit the client implementation at compile time rather than producing it by reflection at run time, which is what allows full trimmed and ahead-of-time publishing of .NET 8 without manual preservation attributes.
What do the WebApiClientCore analyzers do?
They are a Roslyn analyzer project providing syntax analysis and prompts for interface code declarations, to help developers avoid improper syntax when declaring interfaces. Because a wrong interface declaration is otherwise silent until the method is called, the diagnostic surfaces the problem at compile time.
How does WebApiClientCore compare to Refit on performance?
The README states that in BenchmarkDotNet results committed to the repository, performance is 2.X times ahead of Refit under various requests. There are two benchmark projects, one general and one for AOT, so the comparison is falsifiable, but the figure depends on hardware and on how much of a request's time is fixed client overhead.
What serialisation formats does WebApiClientCore support?
JSON, XML, form encoding and custom serialisation methods. The package list includes an extensions project for Newtonsoft JSON for compatibility with applications not on the built-in serialiser, and one for JSON-RPC, plus an OAuth extensions project for token management and authorisation.
When was WebApiClientCore last released, and what licence is it under?
The most recent tags are Core_2.1.6 on 2026-07-09, Core_2.1.4 on 2024-07-01 and Core_2.1.3 on 2024-06-23, with the last push on 2026-07-17. Tag names are prefixed by package because each is versioned independently. The licence is the MIT License, in a LICENSE file at the repository root.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dotnetcore-webapiclient)