# SDK RUST - APIGratis by API BRASIL 🦀
SDK oficial Rust da plataforma [APIBrasil](https://apibrasil.com.br) — WhatsApp, SMS, consultas de CPF/CNPJ, veículos, CEP, correios, pagamentos PIX/boleto e muito mais.
[](https://crates.io/crates/apibrasil)
[](https://docs.rs/apibrasil)
[](https://github.com/APIBrasil/apigratis-sdk-rust/actions/workflows/ci.yml)
<a href="https://github.com/APIBrasil/apigratis-sdk-rust/issues" target="_blank"><img alt="GitHub issues" src="https://img.shields.io/github/issues/APIBrasil/apigratis-sdk-rust"></a>
<a href="https://github.com/APIBrasil/apigratis-sdk-rust/network" target="_blank"><img alt="GitHub forks" src="https://img.shields.io/github/forks/APIBrasil/apigratis-sdk-rust"></a>
<a href="https://github.com/APIBrasil/apigratis-sdk-rust/stargazers" target="_blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/APIBrasil/apigratis-sdk-rust"></a>
## Canais de suporte (Comunidade)
[](https://whatsapp.com/channel/0029VaMiaT6B4hdX3hrUcz3X)
[](https://t.me/apibrasil1)
## Instalação
```bash
cargo add apibrasil
cargo add tokio --features macros,rt-multi-thread
cargo add serde_json
```
Requer **Rust >= 1.82**. TLS via `rustls` por padrão — sem depender de OpenSSL instalado.
Obtenha suas credenciais em https://apibrasil.com.br
## Começando
```rust
use apibrasil::{ApiBrasil, Config};
use serde_json::json;
#[tokio::main]
async fn main() -> apibrasil::Result<()> {
let api = ApiBrasil::new(
Config::new()
.bearer_token("SEU_BEARER_TOKEN") // JWT do login
.device_token("SEU_DEVICE_TOKEN"), // device dos serviços device-based
);
// WhatsApp
api.whatsapp()
.send_text(json!({ "number": "5511999999999", "text": "Olá! 👋" }))
.await?;
// Consulta CNPJ (por créditos)
let empresa = api.consulta().cnpj(json!({ "cnpj": "00000000000000" })).await?;
println!("{:?}", empresa.data());
Ok(())
}
```
As credenciais também podem vir só do ambiente — `ApiBrasil::from_env()` lê automaticamente `APIBRASIL_BEARER_TOKEN`, `APIBRASIL_DEVICE_TOKEN`, `APIBRASIL_SECRET_KEY` e `APIBRASIL_BASE_URL`.
Também é possível autenticar por email/senha — o token retornado fica guardado no cliente:
```rust
let (api, sessao) = apibrasil::login(
json!({ "email": "voce@empresa.com.br", "password": "******" }),
Default::default(),
)
.await?;
// contas com 2FA:
let api = ApiBrasil::from_env();
let sessao = api.auth().login(json!({ "email": email, "password": senha })).await?;
if apibrasil::requires_2fa(&sessao) {
let challenge = sessao["challenge"].clone();
api.auth().send_2fa(json!({ "challenge": challenge, "method": "email" })).await?;
api.auth().verify_2fa(json!({ "challenge": challenge, "code": "000000" })).await?;
}
```
## Como a plataforma funciona
A API Brasil tem duas famílias de serviços:
| **Device-based** | `Authorization: Bearer` + header `DeviceToken` | WhatsApp, SMS, veículos, CEP, correios, DDD, feriados, tradução, clima, OCR |
| **Por créditos** | apenas `Authorization: Bearer` (debita saldo) | `consulta().cpf`, `consulta().cnpj`, `consulta().veiculos`, Serasa, CNH |
Para os serviços device-based, crie um device com a `SecretKey` da API desejada (painel APIBrasil) e use o `device_token` retornado:
```rust
use apibrasil::RequestOptions;
let device = api
.with_options(RequestOptions::new().secret_key("SUA_SECRET_KEY"))
.devices()
.store(json!({ "device_name": "meu-bot", "type": "server" }))
.await?;
api.set_device_token("device_token_retornado");
```
## Serviços disponíveis
| `api.whatsapp()` | WhatsApp: `start`, `qrcode`, `send_text`, `send_file`, `send_audio`, `send_video`, fila (`queue`)... |
| `api.evolution()` | Evolution API: `request(controller, action, body)` |
| `api.whatsmeow()` | WhatsMeow: `request(action, body)`, `send_text`, `instance_qr`... |
| `api.sms()` | SMS device-based (`send`) e por créditos (`send_with_credits`) |
| `api.dados()` | Dados cadastrais device-based (`cpf`, `cnpj`, `lista_socios`...) |
| `api.vehicles()` | Veículos por placa (`dados`, `fipe`, `consulta_fipe`, `base_dados`) |
| `api.fipe()` | Tabela FIPE (`consultar_marcas`, `consultar_modelos`...) |
| `api.correios()` | Correios (`rastreio`, `request`) |
| `api.cep()` | CEP + geolocalização (`cep`, `cidades`, `estados`, `calcular_distancia`) |
| `api.geolocation()` / `api.geomatrix()` | Geolocalização e matriz de distâncias |
| `api.recognize()` | OCR / Google Vision (`base64`, `uri`) |
| `api.ddd()` / `api.holidays()` / `api.translate()` / `api.weather()` | DDD, feriados, tradução, clima |
| `api.loterias()` | Loterias (`latest`, `resultado`) |
| `api.database_ip()` | GeoIP (`ip`) |
| `api.consulta()` | Consultas por créditos: `cpf`, `cnpj`, `cnh`, `cep`, `veiculos`, `telefone`, `generic(service, body)` |
| `api.ura()` / `api.chip_virtual()` | URA reversa e chip virtual |
| `api.bulk()` | Execução em lote (`direct`, `queue`) |
| `api.auth()` | Login, 2FA, cadastro, recuperação de senha, perfil |
| `api.devices()` | CRUD de devices |
| `api.catalog()` | Catálogo de APIs, planos, documentações, servidores |
| `api.account()` | Saldo, faturas, notificações, tickets |
| `api.payments()` | Recargas e pagamentos PIX/boleto/cartão (Santander, Inter, Mercado Pago, Sicoob) |
| `api.ip_whitelist()` / `api.bearer_rate_limit()` | Segurança da conta |
| `api.reports()` | Relatórios e dashboard de consumo |
Todo método é `async` e aceita o body como `json!({...})`, `None` ou `()`.
### WhatsApp
```rust
// iniciar sessão e obter QR Code
api.whatsapp()
.start(json!({ "webhook_wh_message": "https://seu-webhook.com/mensagens" }))
.await?;
let qr = api.whatsapp().qrcode(None).await?;
println!("{:?}", qr.response()); // data URI base64
// envios
api.whatsapp().send_text(json!({ "number": "5511999999999", "text": "Olá!" })).await?;
api.whatsapp()
.send_file(json!({ "number": "5511999999999", "path": "https://exemplo.com/boleto.pdf" }))
.await?;
api.whatsapp()
.send_location(json!({ "number": "5511999999999", "lat": -23.5, "lng": -46.6 }))
.await?;
// qualquer action do catálogo
api.whatsapp().request("getAllChats", None).await?;
// fila assíncrona
api.whatsapp()
.queue("sendText", json!({ "number": "5511999999999", "text": "vai por fila" }))
.await?;
```
O envelope device-based tem acessores tipados — e continua sendo um JSON:
```rust
let res = api.whatsapp().send_text(json!({ "number": "...", "text": "..." })).await?;
res.is_error(); // bool
res.message(); // Option<&str>
res.response(); // Option<&Json>
res.api_limit(); // Option<&Json>
res["response"]; // acesso direto por chave
#[derive(serde::Deserialize)]
struct Enviado { id: String }
let enviado: Enviado = res.decode()?; // decodifica o campo `response`
```
### Consultas por créditos
```rust
use apibrasil::Consulta;
let cpf = api.consulta().cpf(json!({ "cpf": "00000000000" })).await?;
println!("{:?} {:?}", cpf.balance(), cpf.data());
// o campo `tipo` define o produto consultado
api.consulta()
.cnpj(Consulta::new("lista-socios").field("cnpj", "00000000000000"))
.await?;
// modo homologação (sandbox, sem cobrança)
api.consulta()
.cnpj(Consulta::new("serasa-score-pj").homolog(true).field("cnpj", "00000000000000"))
.await?;
// qualquer serviço do catálogo
api.consulta().generic("cnh", json!({ "cpf": "00000000000" })).await?;
```
### Veículos e FIPE (device-based)
```rust
api.vehicles().dados(json!({ "placa": "ABC1234" })).await?;
api.vehicles().fipe(json!({ "placa": "ABC1234" })).await?;
api.fipe().consultar_marcas(json!({ "codigoTabelaReferencia": 300 })).await?;
```
### SMS
```rust
api.sms().send(json!({ "number": "5511999999999", "message": "Olá!" })).await?;
api.sms().send_with_credits(json!({ "number": "5511999999999", "message": "Olá!" })).await?;
```
### Pagamentos e recargas
```rust
api.payments().recharge(json!({ "amount": 50, "type": "pix" })).await?;
api.payments().pix_generate("santander", json!({ "amount": 50 })).await?;
api.payments().pix_status("santander", "TX_ID").await?;
let pdf = api.payments().boleto_pdf("inter", "ID").await?; // Vec<u8>
std::fs::write("boleto.pdf", pdf)?;
```
### Múltiplos devices
```rust
let bot1 = api.with_device("device_token_1");
let bot2 = api.with_device("device_token_2");
bot1.whatsapp().send_text(json!({ "number": "5511999999999", "text": "do bot 1" })).await?;
bot2.whatsapp().send_text(json!({ "number": "5511999999999", "text": "do bot 2" })).await?;
```
## Tratamento de erros
Toda chamada devolve `apibrasil::Result<T>`; a falha carrega a categoria em `error.kind()`:
| `Validation` | 400/422 — payload inválido |
| `Authentication` | 401 — token ausente/expirado |
| `InsufficientBalance` | 402 — sem saldo/créditos |
| `Permission` | 403 — sem permissão (ex: exige PJ) |
| `NotFound` | 404/410 — sem dados / rota desativada |
| `RateLimit` | 429 — limite atingido (`error.retry_after`) |
| `Server` | 5xx — erro do gateway/provedor |
| `Network` / `Timeout` | falha antes da resposta |
| `Api` | qualquer outra falha da API |
```rust
use apibrasil::ErrorKind;
match api.consulta().cpf(json!({ "cpf": "00000000000" })).await {
Ok(consulta) => println!("{:?}", consulta.data()),
Err(error) => match error.kind() {
ErrorKind::InsufficientBalance => println!("Recarregue seus créditos"),
ErrorKind::RateLimit => println!("Aguarde {:?}", error.retry_after),
// detalhes completos da falha
_ => println!("{} {:?} {:?}", error, error.status, error.code),
},
}
```
Cada categoria também tem o seu predicado: `error.is_insufficient_balance()`, `error.is_rate_limit()`, `error.is_network()`...
## Retry e observabilidade
Por padrão a SDK refaz a chamada em **HTTP 429** e em **falhas de conexão** (2 tentativas extras, backoff exponencial com jitter, respeitando `Retry-After`). Timeouts e erros de negócio nunca são refeitos — evita duplicar cobranças e envios.
```rust
use std::time::Duration;
use apibrasil::{Config, FnHooks, RetryConfig};
let api = ApiBrasil::new(
Config::new()
.retry(RetryConfig {
retries: 3,
min_delay: Duration::from_millis(500),
retry_on_statuses: vec![429, 503],
..Default::default()
}) // ou RetryConfig::none()
.hooks(
FnHooks::new()
.on_request(|info| println!("→ {} {} (#{})", info.method, info.url, info.attempt))
.on_response(|info| println!("← {} em {:?}", info.status, info.duration))
.on_retry(|info| println!("retry em {:?}: {}", info.delay, info.reason)),
),
);
```
Para cancelar ou limitar uma chamada, use as ferramentas do próprio runtime:
```rust
let envio = tokio::time::timeout(
Duration::from_secs(10),
api.whatsapp().send_text(json!({ "number": "...", "text": "..." })),
)
.await;
```
## Opções por requisição
`with_options` devolve um cliente (ou serviço) que usa as opções em todas as chamadas, compartilhando a mesma conexão e credenciais:
```rust
use apibrasil::RequestOptions;
api.with_options(
RequestOptions::new()
.secret_key("SUA_SECRET_KEY")
.header("X-Correlation-Id", "abc-123")
.timeout(Duration::from_secs(5)),
)
.devices()
.store(json!({ "device_name": "meu-bot" }))
.await?;
// também por serviço
api.whatsapp()
.with_options(RequestOptions::new().device_token("outro-device"))
.send_text(json!({ "number": "...", "text": "..." }))
.await?;
```
## Cliente síncrono
Sem `async`/`await`, com a feature `blocking`:
```toml
apibrasil = { version = "1", features = ["blocking"] }
```
```rust
use apibrasil::blocking::ApiBrasil;
fn main() -> apibrasil::Result<()> {
let api = ApiBrasil::from_env()?;
api.whatsapp().send_text(json!({ "number": "5511999999999", "text": "Olá!" }))?;
let empresa = api.consulta().cnpj(json!({ "cnpj": "00000000000000" }))?;
println!("{:?}", empresa.data());
// qualquer método assíncrono, inclusive os sem espelho síncrono
let planos = api.block_on(api.asynchronous().catalog().plans())?;
println!("{planos}");
Ok(())
}
```
## Transporte plugável
O HTTP é feito pelo `reqwest`, mas o trait `Transport` permite trocar a camada inteira (proxy corporativo, instrumentação, mocks de teste):
```rust
use apibrasil::core::transport::{BoxFuture, Transport, TransportRequest, TransportResponse};
struct MeuTransporte;
impl Transport for MeuTransporte {
fn execute<'a>(&'a self, request: TransportRequest) -> BoxFuture<'a, apibrasil::Result<TransportResponse>> {
Box::pin(async move {
// use o cliente HTTP que quiser e devolva status, headers e data
Ok(TransportResponse::json(200, json!({ "ok": true })))
})
}
}
let api = ApiBrasil::new(Config::new().transport(MeuTransporte));
```
Para apenas configurar proxy, TLS ou pool de conexões, reaproveite o transporte padrão:
```rust
use apibrasil::ReqwestTransport;
let http = reqwest::Client::builder()
.proxy(reqwest::Proxy::all("http://proxy.empresa:3128")?)
.build()?;
let api = ApiBrasil::new(Config::new().transport(ReqwestTransport::with_client(http)));
```
## Módulos
```rust
use apibrasil::{ApiBrasil, Config}; // cliente + tipos do dia a dia
use apibrasil::core; // HTTP, transporte, erros, retry, hooks
use apibrasil::services::messaging; // WhatsApp, SMS, Evolution, WhatsMeow
use apibrasil::services::data; // consultas, veículos, CEP, FIPE...
use apibrasil::services::platform; // auth, devices, pagamentos, relatórios
use apibrasil::generated; // catálogo gerado
use apibrasil::legacy; // interface legada
use apibrasil::blocking; // cliente síncrono (feature = "blocking")
```
Cada serviço também pode ser usado isoladamente sobre o mesmo cliente HTTP:
```rust
use std::sync::Arc;
use apibrasil::{Config, HttpClient};
let http = Arc::new(HttpClient::new(Config::new().bearer_token("...")));
let api = ApiBrasil::with_http(http.clone());
```
## Catálogo gerado
As actions de WhatsApp/Evolution/WhatsMeow e os 210+ `tipo` de consulta são gerados do catálogo real da plataforma:
```bash
cargo run --features blocking --bin codegen
```
```rust
use apibrasil::generated;
generated::WHATSAPP_ACTIONS; // &[&str] com todas as actions
generated::service_actions_for("cep"); // ["bairros", "cep", "cidades", ...]
generated::has_action("whatsapp", "sendText"); // true
let meta = generated::consulta_tipo("acerta-essencial").unwrap();
// meta.service = "cpf", meta.fields = ["cpf"]
```
## Endpoint sem método dedicado?
Todo o gateway fica acessível pela porta de saída genérica, já com seus headers de autenticação:
```rust
use apibrasil::Method;
api.request(Method::Post, "/consulta/cpf/credits", json!({ "cpf": "00000000000" })).await?;
api.request(Method::Get, "/reports/quick-stats", None).await?;
```
Documentação completa dos endpoints: https://doc.apibrasil.io
## Configuração avançada
```rust
let api = ApiBrasil::new(Config {
bearer_token: Some("...".into()), // ou APIBRASIL_BEARER_TOKEN
device_token: Some("...".into()), // ou APIBRASIL_DEVICE_TOKEN
secret_key: Some("...".into()), // usada em devices().store (ou APIBRASIL_SECRET_KEY)
base_url: Some("https://gateway.apibrasil.io/api/v2".into()), // padrão (ou APIBRASIL_BASE_URL)
timeout: Some(Duration::from_secs(30)),
..Default::default()
});
```
## Licença
MIT — veja [LICENSE](LICENSE).