Skip to content

Commit bc39456

Browse files
committed
first commit
0 parents  commit bc39456

15 files changed

Lines changed: 872 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- uses: actions/setup-dotnet@v4
15+
with:
16+
dotnet-version: |
17+
7.0.x
18+
8.0.x
19+
20+
- name: Restore
21+
run: dotnet restore
22+
23+
- name: Build
24+
run: dotnet build --no-restore --configuration Release
25+
26+
- name: Test
27+
run: dotnet test --no-build --configuration Release

.github/workflows/publish.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Publish
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
id-token: write # required for Trusted Publishing (OIDC)
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v7
17+
18+
- name: Setup .NET
19+
uses: actions/setup-dotnet@v6
20+
with:
21+
dotnet-version: 8.0.x
22+
23+
- name: Restore
24+
run: dotnet restore src/Fipe.Api.Br/Fipe.Api.Br.csproj
25+
26+
- name: Build
27+
run: dotnet build src/Fipe.Api.Br/Fipe.Api.Br.csproj --configuration Release --no-restore
28+
29+
- name: Pack
30+
run: dotnet pack src/Fipe.Api.Br/Fipe.Api.Br.csproj --configuration Release --no-build --output packages
31+
32+
- name: NuGet login (Trusted Publishing)
33+
id: login
34+
uses: NuGet/login@v1
35+
with:
36+
user: deividfortuna # your nuget.org username
37+
38+
- name: Push to NuGet.org
39+
run: >
40+
dotnet nuget push packages/*.nupkg
41+
--api-key ${{ steps.login.outputs.NUGET_API_KEY }}
42+
--source https://api.nuget.org/v3/index.json
43+
--skip-duplicate

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
bin/
2+
obj/
3+
*.user
4+
.vs/
5+
.idea/
6+
*.nupkg
7+
TestResults/

Fipe.Api.Br.sln

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D9DB2427-3B86-4D53-8790-10D60ACCEFD9}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fipe.Api.Br", "src\Fipe.Api.Br\Fipe.Api.Br.csproj", "{89680D01-9E95-4EF7-997C-16978141000B}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7DC484EC-39C1-4416-B064-319D315A6816}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fipe.Api.Br.Tests", "tests\Fipe.Api.Br.Tests\Fipe.Api.Br.Tests.csproj", "{79304434-8A7D-4001-A9EE-83DA9F0E59BC}"
13+
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{0E335A00-9102-41D8-83E9-0BEBE2F94B1E}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fipe.Api.Br.Examples", "examples\Fipe.Api.Br.Examples\Fipe.Api.Br.Examples.csproj", "{BFCE471B-D951-492A-AD05-41B6EB4FAD3D}"
17+
EndProject
18+
Global
19+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
20+
Debug|Any CPU = Debug|Any CPU
21+
Release|Any CPU = Release|Any CPU
22+
EndGlobalSection
23+
GlobalSection(SolutionProperties) = preSolution
24+
HideSolutionNode = FALSE
25+
EndGlobalSection
26+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
27+
{89680D01-9E95-4EF7-997C-16978141000B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28+
{89680D01-9E95-4EF7-997C-16978141000B}.Debug|Any CPU.Build.0 = Debug|Any CPU
29+
{89680D01-9E95-4EF7-997C-16978141000B}.Release|Any CPU.ActiveCfg = Release|Any CPU
30+
{89680D01-9E95-4EF7-997C-16978141000B}.Release|Any CPU.Build.0 = Release|Any CPU
31+
{79304434-8A7D-4001-A9EE-83DA9F0E59BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32+
{79304434-8A7D-4001-A9EE-83DA9F0E59BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
33+
{79304434-8A7D-4001-A9EE-83DA9F0E59BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
34+
{79304434-8A7D-4001-A9EE-83DA9F0E59BC}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{BFCE471B-D951-492A-AD05-41B6EB4FAD3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{BFCE471B-D951-492A-AD05-41B6EB4FAD3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{BFCE471B-D951-492A-AD05-41B6EB4FAD3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{BFCE471B-D951-492A-AD05-41B6EB4FAD3D}.Release|Any CPU.Build.0 = Release|Any CPU
39+
EndGlobalSection
40+
GlobalSection(NestedProjects) = preSolution
41+
{89680D01-9E95-4EF7-997C-16978141000B} = {D9DB2427-3B86-4D53-8790-10D60ACCEFD9}
42+
{79304434-8A7D-4001-A9EE-83DA9F0E59BC} = {7DC484EC-39C1-4416-B064-319D315A6816}
43+
{BFCE471B-D951-492A-AD05-41B6EB4FAD3D} = {0E335A00-9102-41D8-83E9-0BEBE2F94B1E}
44+
EndGlobalSection
45+
EndGlobal

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Deivid Fortuna
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# FIPE C# SDK
2+
3+
[![CI](https://github.com/fipe-api/csharp-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/fipe-api/csharp-sdk/actions/workflows/ci.yml)
4+
5+
A .NET client for the [FIPE API](https://fipe.api.br) (`/api/v2`), which provides average vehicle prices in the Brazilian market from Fundação Instituto de Pesquisas Econômicas (FIPE). Prices are updated monthly.
6+
7+
Targets `netstandard2.0` — works on .NET Core, .NET 5+, and .NET Framework 4.6.2+.
8+
9+
## Install
10+
11+
```sh
12+
dotnet add package Fipe.Api.Br
13+
```
14+
15+
## Quick start
16+
17+
```csharp
18+
using Fipe.Api.Br;
19+
20+
var client = new FipeClient();
21+
22+
var brands = await client.GetBrandsAsync(VehicleType.Cars);
23+
foreach (var brand in brands)
24+
{
25+
Console.WriteLine($"{brand.Code} {brand.Name}");
26+
}
27+
```
28+
29+
Vehicle types: `VehicleType.Cars`, `VehicleType.Motorcycles`, `VehicleType.Trucks`.
30+
31+
## Authentication
32+
33+
The free tier works without a token but is rate limited. With a subscription token:
34+
35+
```csharp
36+
var client = new FipeClient(subscriptionToken: "your-token");
37+
```
38+
39+
In DI scenarios, pass an `HttpClient` from `IHttpClientFactory`:
40+
41+
```csharp
42+
services.AddHttpClient<FipeClient>();
43+
// or
44+
var client = new FipeClient(httpClientFactory.CreateClient(), subscriptionToken: "your-token");
45+
```
46+
47+
## Endpoints
48+
49+
Drill down brand → model → year → price:
50+
51+
```csharp
52+
var brands = await client.GetBrandsAsync(VehicleType.Cars); // GET /cars/brands
53+
var models = await client.GetModelsAsync(VehicleType.Cars, "59"); // GET /cars/brands/59/models
54+
var years = await client.GetYearsAsync(VehicleType.Cars, "59", "5940"); // GET /cars/brands/59/models/5940/years
55+
var vehicle = await client.GetVehicleAsync(VehicleType.Cars, "59", "5940", "2014-3"); // GET /cars/brands/59/models/5940/years/2014-3
56+
57+
Console.WriteLine($"{vehicle.Model}: {vehicle.Price}");
58+
```
59+
60+
Browse by year:
61+
62+
```csharp
63+
var years = await client.GetYearsByBrandAsync(VehicleType.Cars, "59"); // GET /cars/brands/59/years
64+
var models = await client.GetModelsByBrandYearAsync(VehicleType.Cars, "59", "2014-3"); // GET /cars/brands/59/years/2014-3/models
65+
```
66+
67+
Look up by FIPE code:
68+
69+
```csharp
70+
var years = await client.GetYearsByFipeCodeAsync(VehicleType.Cars, "005340-6"); // GET /cars/005340-6/years
71+
var vehicle = await client.GetVehicleByFipeCodeAsync(VehicleType.Cars, "005340-6", "2014-3"); // GET /cars/005340-6/years/2014-3
72+
var history = await client.GetHistoryByFipeCodeAsync(VehicleType.Cars, "005340-6", "2014-3"); // GET /cars/005340-6/years/2014-3/history
73+
74+
foreach (var entry in history.PriceHistory)
75+
{
76+
Console.WriteLine($"{entry.Month}: {entry.Price}");
77+
}
78+
```
79+
80+
### Reference months
81+
82+
Prices are published per monthly reference table. Every endpoint accepts an optional `reference` to query a past table:
83+
84+
```csharp
85+
var references = await client.GetReferencesAsync(); // GET /references — e.g. Code "308", Month "abril de 2024"
86+
87+
var brands = await client.GetBrandsAsync(VehicleType.Cars, reference: 308);
88+
```
89+
90+
## Error handling
91+
92+
Non-success responses throw `FipeApiException` with the status code and body:
93+
94+
```csharp
95+
try
96+
{
97+
var vehicle = await client.GetVehicleAsync(VehicleType.Cars, "59", "5940", "1900-1");
98+
}
99+
catch (FipeApiException ex) when (ex.IsNotFound)
100+
{
101+
// unknown brand/model/year
102+
}
103+
catch (FipeApiException ex) when (ex.IsRateLimited)
104+
{
105+
// rate limited — back off or use a subscription token
106+
}
107+
catch (FipeApiException ex)
108+
{
109+
Console.WriteLine($"API returned {(int)ex.StatusCode}: {ex.Body}");
110+
}
111+
```
112+
113+
## Example program
114+
115+
A runnable example that lists brands and prints an Amarok price from the live API:
116+
117+
```sh
118+
dotnet run --project examples/Fipe.Examples
119+
```
120+
121+
## License
122+
123+
MIT
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="../../src/Fipe.Api.Br/Fipe.Api.Br.csproj" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Demonstrates the FIPE SDK against the live API: lists car brands,
2+
// drills into VW Amarok models and years, and prints the current price.
3+
using Fipe.Api.Br;
4+
5+
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
6+
var client = new FipeClient();
7+
8+
var brands = await client.GetBrandsAsync(VehicleType.Cars, cancellationToken: cts.Token);
9+
Console.WriteLine($"{brands.Count} car brands available. First 5:");
10+
foreach (var b in brands.Take(5))
11+
{
12+
Console.WriteLine($" {b.Code}: {b.Name}");
13+
}
14+
15+
var vw = brands.FirstOrDefault(b => b.Name.Contains("VolksWagen"))
16+
?? throw new InvalidOperationException("VolksWagen not found in brand list");
17+
18+
var models = await client.GetModelsAsync(VehicleType.Cars, vw.Code, cancellationToken: cts.Token);
19+
var amarok = models.FirstOrDefault(m => m.Name.Contains("AMAROK"))
20+
?? throw new InvalidOperationException("AMAROK not found in model list");
21+
Console.WriteLine($"\nModel: {amarok.Name} (code {amarok.Code})");
22+
23+
var years = await client.GetYearsAsync(VehicleType.Cars, vw.Code, amarok.Code, cancellationToken: cts.Token);
24+
Console.WriteLine($"Years available: {years.Count}");
25+
26+
var vehicle = await client.GetVehicleAsync(VehicleType.Cars, vw.Code, amarok.Code, years[0].Code, cancellationToken: cts.Token);
27+
Console.WriteLine($"\n{vehicle.Model} {vehicle.ModelYear} ({vehicle.Fuel})");
28+
Console.WriteLine($"FIPE code: {vehicle.CodeFipe}");
29+
Console.WriteLine($"Price: {vehicle.Price} ({vehicle.ReferenceMonth})");

src/Fipe.Api.Br/Fipe.Api.Br.csproj

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>10</LangVersion>
6+
<Nullable>enable</Nullable>
7+
<RootNamespace>Fipe.Api.Br</RootNamespace>
8+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
9+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
10+
</PropertyGroup>
11+
12+
<PropertyGroup>
13+
<PackageId>Fipe.Api.Br</PackageId>
14+
<Version>0.1.0</Version>
15+
<Authors>Deivid Fortuna</Authors>
16+
<Description>Client SDK for the FIPE API — average vehicle prices in the Brazilian market (Tabela FIPE), updated monthly.</Description>
17+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
18+
<PackageProjectUrl>https://github.com/fipe-api/csharp-sdk</PackageProjectUrl>
19+
<RepositoryUrl>https://github.com/fipe-api/csharp-sdk</RepositoryUrl>
20+
<RepositoryType>git</RepositoryType>
21+
<PackageTags>fipe;fipe-api;tabela-fipe;sdk;client;api-client;vehicles;veiculos;carros;prices;precos;brazil;brasil</PackageTags>
22+
<PackageReadmeFile>README.md</PackageReadmeFile>
23+
</PropertyGroup>
24+
25+
<ItemGroup>
26+
<None Include="../../README.md" Pack="true" PackagePath="/" />
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<PackageReference Include="System.Text.Json" Version="10.0.10" />
31+
</ItemGroup>
32+
33+
</Project>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Net;
3+
4+
namespace Fipe.Api.Br
5+
{
6+
/// <summary>Thrown when the FIPE API responds with a non-success status code.</summary>
7+
public sealed class FipeApiException : Exception
8+
{
9+
/// <summary>Creates the exception for a failed API response.</summary>
10+
/// <param name="statusCode">The HTTP status code returned by the API.</param>
11+
/// <param name="body">The raw response body.</param>
12+
public FipeApiException(HttpStatusCode statusCode, string body)
13+
: base($"FIPE API returned {(int)statusCode} ({statusCode}){(string.IsNullOrEmpty(body) ? "" : ": " + body)}")
14+
{
15+
StatusCode = statusCode;
16+
Body = body;
17+
}
18+
19+
/// <summary>The HTTP status code returned by the API (e.g. 404, 429, 500).</summary>
20+
public HttpStatusCode StatusCode { get; }
21+
22+
/// <summary>The raw response body, if any.</summary>
23+
public string Body { get; }
24+
25+
/// <summary>True when the requested resource does not exist (HTTP 404).</summary>
26+
public bool IsNotFound => StatusCode == HttpStatusCode.NotFound;
27+
28+
/// <summary>True when the client is being rate limited (HTTP 429).</summary>
29+
public bool IsRateLimited => (int)StatusCode == 429;
30+
}
31+
}

0 commit comments

Comments
 (0)