API de Clientes
CRUD completo de clientes via API. Gerencie cadastro de clientes com conformidade LGPD.
Gerencie o cadastro de clientes da sua empresa via API. Consulte, crie, atualize e exclua registros de clientes de forma programática.
Visão Geral
| Propriedade | Valor |
|---|---|
| Base URL (produção) | https://api-backend.bunto.com.br/v1/clientes/ |
| Autenticação | Authorization: Bearer bnt_xxx |
| Formato | JSON |
| Escopo necessário (leitura) | clientes: read |
| Escopo necessário (escrita) | clientes: write |
| Escopo necessário (exclusão) | clientes: delete |
Endpoints
| Método | Endpoint | Descrição | Escopo |
|---|---|---|---|
| GET | /v1/clientes/ | Listar clientes | clientes: read |
| GET | /v1/clientes/{id}/ | Detalhar cliente | clientes: read |
| POST | /v1/clientes/ | Criar cliente | clientes: write |
| PUT | /v1/clientes/{id}/ | Atualizar cliente (completo) | clientes: write |
| PATCH | /v1/clientes/{id}/ | Atualizar cliente (parcial) | clientes: write |
| DELETE | /v1/clientes/{id}/ | Excluir cliente | clientes: delete |
Proteção de Dados (LGPD)
O Bunto ERP segue a Lei Geral de Proteção de Dados (LGPD). Os campos sensíveis dos clientes recebem tratamento especial:
Campos criptografados
Os seguintes campos são armazenados de forma criptografada no banco de dados:
cnpj_cpf(CPF ou CNPJ)emailcelulartel_fixo(telefone fixo)rgdata_nascimentocpf_pai,cpf_mae
Comportamento por endpoint
| Endpoint | Campos sensíveis |
|---|---|
| Listagem (GET /clientes/) | Não retornados - proteção contra exposição em massa |
| Detalhe (GET /clientes/{id}/) | Descriptografados automaticamente e incluídos na resposta |
| Criação (POST /clientes/) | Criptografados automaticamente antes de salvar no banco |
| Atualização (PUT/PATCH /clientes/{id}/) | Criptografados automaticamente antes de salvar no banco |
Importante: Na listagem, campos como CPF/CNPJ, e-mail e telefone não são retornados. Para acessar esses dados, utilize o endpoint de detalhe (GET /v1/clientes/{id}/).
Listar Clientes
GET /v1/clientes/
Retorna uma lista paginada de clientes da empresa. Por segurança (LGPD), campos sensíveis como CPF/CNPJ, e-mail e telefone não são incluídos na listagem.
Parâmetros de consulta
| Parâmetro | Tipo | Descrição |
|---|---|---|
busca | string | Busca por nome ou nome fantasia |
tipo_pessoa | integer | Filtrar por tipo: 1 = Pessoa Física, 2 = Pessoa Jurídica |
cidade | string | Filtrar por cidade (busca parcial, case-insensitive) |
uf | string | Filtrar por UF (2 caracteres, ex: SP, RJ) |
ordenar | string | Campo de ordenação: nome, cidade, uf |
direcao | string | Direção da ordenação: asc (padrão) ou desc |
pagina | integer | Número da página (padrão: 1) |
por_pagina | integer | Registros por página (padrão: 25, máximo: 100) |
Campos retornados na listagem
| Campo | Tipo | Descrição |
|---|---|---|
id | integer | ID único do cliente |
nome | string | Nome completo ou razão social |
fantasia | string ou null | Nome fantasia |
codigo | string ou null | Código interno do cliente |
tipo_pessoa | integer | 1 = Pessoa Física, 2 = Pessoa Jurídica |
tipo_pessoa_label | string | Label legível: "Pessoa Física" ou "Pessoa Jurídica" |
consumidor_final | boolean | Indica se é o consumidor final padrão da empresa |
cidade | string ou null | Cidade do cliente |
uf | string ou null | Unidade federativa (2 caracteres) |
Exemplo com cURL
bashcurl 'https://api-backend.bunto.com.br/v1/clientes/?busca=silva&tipo_pessoa=1&uf=SP&por_pagina=10' \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Listar clientes pessoa física de Sao Paulo resposta = requests.get( f"{BASE_URL}/clientes/", headers=headers, params={ "tipo_pessoa": 1, "uf": "SP", "ordenar": "nome", "direcao": "asc", "por_pagina": 25, }, ) dados = resposta.json() if dados["success"]: clientes = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de clientes: {paginacao['total_registros']}") for cliente in clientes: print(f" - {cliente['nome']} ({cliente['tipo_pessoa_label']}) - {cliente['cidade']}/{cliente['uf']}") else: print(f"Erro: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const params = new URLSearchParams({ tipo_pessoa: "1", uf: "SP", ordenar: "nome", direcao: "asc", por_pagina: "25", }); const resposta = await fetch(`${BASE_URL}/clientes/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const clientes = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de clientes: ${paginacao.total_registros}`); clientes.forEach((cliente) => { console.log(` - ${cliente.nome} (${cliente.tipo_pessoa_label}) - ${cliente.cidade}/${cliente.uf}`); }); }
Resposta de sucesso (200)
json{ "success": true, "message": "47 registros encontrados", "data": { "resultados": [ { "id": 142, "nome": "Ana Carolina da Silva", "fantasia": null, "codigo":"CLI-0142", "tipo_pessoa": 1, "tipo_pessoa_label": "Pessoa Física", "consumidor_final": false, "cidade": "Sao Paulo", "uf": "SP" }, { "id": 87, "nome": "Distribuidora Santos Ltda", "fantasia": "Santos Distribuidora", "codigo":"CLI-0087", "tipo_pessoa": 2, "tipo_pessoa_label": "Pessoa Jurídica", "consumidor_final": false, "cidade": "Campinas", "uf": "SP" } ], "paginacao": { "pagina_atual": 1, "total_paginas": 2, "total_registros": 47, "por_pagina": 25, "proxima": "https://api-backend.bunto.com.br/v1/clientes/?pagina=2&por_pagina=25&uf=SP", "anterior": null } } }
Detalhar Cliente
GET /v1/clientes/{id}/
Retorna todos os dados de um cliente, incluindo campos sensíveis descriptografados (CPF/CNPJ, e-mail, telefone, etc.).
Parâmetros de rota
| Parâmetro | Tipo | Descrição |
|---|---|---|
id | integer | ID do cliente |
Campos retornados no detalhe
Além de todos os campos da listagem, o detalhe inclui:
| Campo | Tipo | Descrição | LGPD |
|---|---|---|---|
cnpj_cpf | string ou null | CPF (Pessoa Física) ou CNPJ (Pessoa Jurídica) | Sim |
email | string ou null | E-mail do cliente | Sim |
celular | string ou null | Número do celular | Sim |
tel_fixo | string ou null | Número do telefone fixo | Sim |
cep | string ou null | CEP do endereço principal | Não |
endereco | string ou null | Logradouro | Não |
numero | string ou null | Número do endereço | Não |
complemento | string ou null | Complemento do endereço | Não |
bairro | string ou null | Bairro | Não |
insc_estadual | string ou null | Inscrição estadual | Não |
ie_isento | boolean | Indica se é isento de inscrição estadual | Não |
limite_credito | decimal ou null | Limite de crédito em R$ | Não |
observacoes | string ou null | Observações gerais | Não |
Exemplo com cURL
bashcurl https://api-backend.bunto.com.br/v1/clientes/142/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } resposta = requests.get(f"{BASE_URL}/clientes/142/", headers=headers) dados = resposta.json() if dados["success"]: cliente = dados["data"] print(f"Nome: {cliente['nome']}") print(f"CPF/CNPJ: {cliente['cnpj_cpf']}") print(f"E-mail: {cliente['email']}") print(f"Celular: {cliente['celular']}") print(f"Cidade: {cliente['cidade']}/{cliente['uf']}") else: print(f"Erro: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const resposta = await fetch(`${BASE_URL}/clientes/142/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const cliente = dados.data; console.log(`Nome: ${cliente.nome}`); console.log(`CPF/CNPJ: ${cliente.cnpj_cpf}`); console.log(`E-mail: ${cliente.email}`); }
Resposta de sucesso (200)
json{ "success": true, "message": "Registro encontrado", "data": { "id": 142, "nome": "Ana Carolina da Silva", "fantasia": null, "codigo":"CLI-0142", "tipo_pessoa": 1, "tipo_pessoa_label": "Pessoa Física", "consumidor_final": false, "cidade": "Sao Paulo", "uf": "SP", "cnpj_cpf": "123.456.789-00", "email": "ana.silva@email.com.br", "celular": "(11) 98765-4321", "tel_fixo": "(11) 3456-7890", "cep": "01310-100", "endereco": "Avenida Paulista", "numero":"1578", "complemento": "Sala 302", "bairro": "Bela Vista", "insc_estadual": null, "ie_isento": true, "limite_credito": "5000.00", "observacoes":"Cliente preferencial desde 2024" } }
Criar Cliente
POST /v1/clientes/
Cria um novo registro de cliente. Campos sensíveis (CPF/CNPJ, e-mail, telefone) são criptografados automaticamente antes de serem armazenados no banco de dados.
Escopo necessário: clientes: write
Campos do body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
nome | string | Sim | Nome completo ou razão social (máximo 255 caracteres) |
fantasia | string | Não | Nome fantasia (máximo 255 caracteres) |
codigo | string | Não | Código interno do cliente (máximo 50 caracteres) |
tipo_pessoa | integer | Não | 1 = Pessoa Física (padrão), 2 = Pessoa Jurídica |
consumidor_final | boolean | Não | Indica se é consumidor final padrão (apenas 1 por empresa) |
cnpj_cpf | string | Não | CPF (ex: 123.456.789-00) ou CNPJ (ex: 12.345.678/0001-90) |
email | string | Não | E-mail do cliente |
celular | string | Não | Número do celular (ex: (11) 98765-4321) |
tel_fixo | string | Não | Número do telefone fixo (ex: (11) 3456-7890) |
cep | string | Não | CEP do endereço (ex: 01310-100) |
uf | string | Não | Unidade federativa (2 caracteres, ex: SP) |
cidade | string | Não | Nome da cidade |
bairro | string | Não | Nome do bairro |
endereco | string | Não | Logradouro |
numero | string | Não | Número do endereço |
complemento | string | Não | Complemento do endereço |
insc_estadual | string | Não | Inscrição estadual |
ie_isento | boolean | Não | true se isento de inscrição estadual |
limite_credito | decimal | Não | Limite de crédito em R$ (ex: 5000.00) |
observacoes | string | Não | Observações gerais sobre o cliente |
Tipo de pessoa
O campo tipo_pessoa define se o cliente é Pessoa Física ou Jurídica. Isso impacta a validação do campo cnpj_cpf:
| tipo_pessoa | Valor | Documento esperado | Formato |
|---|---|---|---|
1 | Pessoa Física | CPF | 123.456.789-00 |
2 | Pessoa Jurídica | CNPJ | 12.345.678/0001-90 |
Exemplo com cURL
bashcurl -X POST https://api-backend.bunto.com.br/v1/clientes/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "nome": "Fernanda Oliveira Santos", "tipo_pessoa": 1, "cnpj_cpf": "987.654.321-00", "email": "fernanda.santos@email.com.br", "celular": "(21) 99876-5432", "cep": "20040-020", "uf": "RJ", "cidade": "Rio de Janeiro", "bairro": "Centro", "endereco": "Rua da Assembleia", "numero":"10", "complemento": "Bloco B, Sala 501", "limite_credito": "3000.00", "observacoes":"Cliente indicado por parceiro comercial" }'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Criar cliente Pessoa Jurídica novo_cliente = { "nome": "Comercial Brasil Eireli", "fantasia": "Brasil Comercial", "tipo_pessoa": 2, "cnpj_cpf": "12.345.678/0001-90", "email": "contato@brasilcomercial.com.br", "celular": "(11) 91234-5678", "tel_fixo": "(11) 3210-9876", "cep": "04543-011", "uf": "SP", "cidade": "Sao Paulo", "bairro": "Itaim Bibi", "endereco": "Rua Funchal", "numero":"418", "complemento": "Andar 15", "insc_estadual": "123.456.789.012", "ie_isento": False, "limite_credito": "50000.00", "observacoes":"Conta corporativa - faturamento mensal", } resposta = requests.post(f"{BASE_URL}/clientes/", json=novo_cliente, headers=headers) dados = resposta.json() if dados["success"]: cliente_criado = dados["data"] print(f"Cliente criado com ID: {cliente_criado['id']}") print(f"Nome: {cliente_criado['nome']}") else: print(f"Erro ao criar cliente: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novoCliente = { nome: "Comercial Brasil Eireli", fantasia: "Brasil Comercial", tipo_pessoa: 2, cnpj_cpf: "12.345.678/0001-90", email: "contato@brasilcomercial.com.br", celular: "(11) 91234-5678", tel_fixo: "(11) 3210-9876", cep: "04543-011", uf: "SP", cidade: "Sao Paulo", bairro: "Itaim Bibi", endereco: "Rua Funchal", numero: "418", complemento: "Andar 15", insc_estadual: "123.456.789.012", ie_isento: false, limite_credito: "50000.00", observacoes: "Conta corporativa - faturamento mensal", }; const resposta = await fetch(`${BASE_URL}/clientes/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novoCliente), }); const dados = await resposta.json(); if (dados.success) { console.log(`Cliente criado com ID: ${dados.data.id}`); console.log(`Nome: ${dados.data.nome}`); }
Resposta de sucesso (201)
json{ "success": true, "message": "Registro criado com sucesso", "data": { "id": 203, "nome": "Comercial Brasil Eireli", "fantasia": "Brasil Comercial", "codigo":null, "tipo_pessoa": 2, "tipo_pessoa_label": "Pessoa Jurídica", "consumidor_final": false, "cidade": "Sao Paulo", "uf": "SP", "cnpj_cpf": "12.345.678/0001-90", "email": "contato@brasilcomercial.com.br", "celular": "(11) 91234-5678", "tel_fixo": "(11) 3210-9876", "cep": "04543-011", "endereco": "Rua Funchal", "numero":"418", "complemento": "Andar 15", "bairro": "Itaim Bibi", "insc_estadual": "123.456.789.012", "ie_isento": false, "limite_credito": "50000.00", "observacoes":"Conta corporativa - faturamento mensal" } }
Atualizar Cliente
Atualização completa
PUT /v1/clientes/{id}/
Atualiza todos os campos do cliente. Campos não enviados serão definidos como null (exceto campos obrigatórios).
Atualização parcial
PATCH /v1/clientes/{id}/
Atualiza apenas os campos enviados no body. Campos omitidos permanecem inalterados.
Escopo necessário: clientes: write
Exemplo com cURL (PATCH)
bashcurl -X PATCH https://api-backend.bunto.com.br/v1/clientes/142/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "email": "ana.carolina.novo@email.com.br", "celular": "(11) 91111-2222", "limite_credito": "8000.00" }'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Atualizar parcialmente (PATCH) - apenas os campos informados atualizacao = { "email": "ana.carolina.novo@email.com.br", "celular": "(11) 91111-2222", "limite_credito": "8000.00", } resposta = requests.patch(f"{BASE_URL}/clientes/142/", json=atualizacao, headers=headers) dados = resposta.json() if dados["success"]: cliente = dados["data"] print(f"Cliente {cliente['nome']} atualizado com sucesso") else: print(f"Erro: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const atualizacao = { email: "ana.carolina.novo@email.com.br", celular: "(11) 91111-2222", limite_credito: "8000.00", }; const resposta = await fetch(`${BASE_URL}/clientes/142/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Cliente ${dados.data.nome} atualizado com sucesso`); }
Resposta de sucesso (200)
json{ "success": true, "message": "Registro atualizado com sucesso", "data": { "id": 142, "nome": "Ana Carolina da Silva", "fantasia": null, "codigo":"CLI-0142", "tipo_pessoa": 1, "tipo_pessoa_label": "Pessoa Física", "consumidor_final": false, "cidade": "Sao Paulo", "uf": "SP", "cnpj_cpf": "123.456.789-00", "email": "ana.carolina.novo@email.com.br", "celular": "(11) 91111-2222", "tel_fixo": "(11) 3456-7890", "cep": "01310-100", "endereco": "Avenida Paulista", "numero":"1578", "complemento": "Sala 302", "bairro": "Bela Vista", "insc_estadual": null, "ie_isento": true, "limite_credito": "8000.00", "observacoes":"Cliente preferencial desde 2024" } }
Excluir Cliente
DELETE /v1/clientes/{id}/
Exclui permanentemente um cliente. A exclusão será recusada se o cliente possuir registros vinculados (pedidos de venda, propostas, contas financeiras, etc.).
Escopo necessário: clientes: delete
Exemplo com cURL
bashcurl -X DELETE https://api-backend.bunto.com.br/v1/clientes/203/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} resposta = requests.delete(f"{BASE_URL}/clientes/203/", headers=headers) dados = resposta.json() if dados["success"]: print("Cliente excluído com sucesso") else: print(f"Erro: {dados['message']}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const resposta = await fetch(`${BASE_URL}/clientes/203/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Cliente excluído com sucesso"); } else { console.error(`Erro: ${dados.message}`); }
Resposta de sucesso (200)
json{ "success": true, "message": "Registro excluído com sucesso" }
Erros
Validação (400)
Retornado quando os dados enviados são inválidos.
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Erro de validacao", "details": { "nome": ["Este campo é obrigatorio."], "cnpj_cpf": ["Ja existe um cliente com este CPF"] } } }
CPF/CNPJ duplicado (400)
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Ja existe um cliente com este CNPJ", "details": { "cnpj_cpf": ["Ja existe um cliente com este CNPJ"] } } }
E-mail duplicado (400)
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Ja existe um cliente com este email", "details": { "email": ["Ja existe um cliente com este email"] } } }
Consumidor final duplicado (400)
Apenas um cliente pode ser marcado como consumidor final por empresa.
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Ja existe um consumidor final nesta empresa", "details": { "consumidor_final": ["Ja existe um consumidor final nesta empresa"] } } }
Não autenticado (401)
json{ "detail": "Token invalido" }
Sem permissão (403)
Retornado quando o token não possui o escopo necessário.
json{ "detail": "Token nao tem permissao para este recurso" }
Não encontrado (404)
Retornado quando o cliente não existe ou pertence a outra empresa.
json{ "success": false, "error": { "code": "NOT_FOUND", "message": "Registro nao encontrado" } }
Cliente em uso (400)
Retornado ao tentar excluir um cliente que possui registros vinculados.
json{ "success": false, "error": { "code": "PROTECTED_ERROR", "message": "Nao é possivel excluir o cliente pois está em uso", "details": { "registros_em_uso": [ "Pedido de Venda PV-001523", "Proposta PC-000042", "Conta a Receber CR-000891" ] } } }
Rate limit excedido (429)
json{ "detail": "Limite de requisicoes excedido. Tente novamente em 45 segundos." }
Rate Limiting
Os limites de requisição são aplicados por token.
| Operação | Métodos | Limite |
|---|---|---|
| Leitura | GET, HEAD, OPTIONS | 120 requisições/minuto |
| Escrita | POST, PUT, PATCH | 30 requisições/minuto |
| Exclusão | DELETE | 10 requisições/minuto |
Ao exceder o limite, a API retorna 429 Too Many Requests. Implemente retry com backoff exponencial para lidar com esses casos.
Paginação
Endpoints de listagem retornam dados paginados.
| Parâmetro | Tipo | Padrão | Máximo | Descrição |
|---|---|---|---|---|
pagina | integer | 1 | - | Número da página |
por_pagina | integer | 25 | 100 | Registros por página |
Nota: Os parâmetros são pagina e por_pagina (em português), não page e per_page.
Navegação entre páginas (Python)
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} todos_clientes = [] url = f"{BASE_URL}/clientes/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todos_clientes.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todos_clientes)} clientes")
Exemplos Completos
Sincronizar clientes com sistema externo (Python)
pythonimport os import time import requests class BuntoClientes: """Cliente para o endpoint de clientes da API v1 do Bunto ERP.""" def __init__(self, token=None, base_url=None): self.token = token or os.environ["BUNTO_API_TOKEN"] self.base_url = base_url or "https://api-backend.bunto.com.br/v1" self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {self.token}", "Content-Type": "application/json", }) def _requisicao(self, metodo, endpoint, **kwargs): """Executa requisicao com retry para rate limit.""" url = f"{self.base_url}/{endpoint.lstrip('/')}" for tentativa in range(3): resposta = self.session.request(metodo, url, **kwargs) if resposta.status_code == 429: tempo = 2 ** tentativa * 15 print(f"Rate limit. Aguardando {tempo}s...") time.sleep(tempo) continue return resposta raise Exception("Limite de tentativas excedido") def listar(self, **filtros): """Lista clientes com filtros opcionais.""" resposta = self._requisicao("GET", "/clientes/", params=filtros) return resposta.json() def obter(self, cliente_id): """Obtém detalhes de um cliente (inclui dados sensíveis).""" resposta = self._requisicao("GET", f"/clientes/{cliente_id}/") return resposta.json() def criar(self, dados): """Cria um novo cliente.""" resposta = self._requisicao("POST", "/clientes/", json=dados) return resposta.json() def atualizar(self, cliente_id, dados): """Atualiza parcialmente um cliente.""" resposta = self._requisicao("PATCH", f"/clientes/{cliente_id}/", json=dados) return resposta.json() def excluir(self, cliente_id): """Exclui um cliente.""" resposta = self._requisicao("DELETE", f"/clientes/{cliente_id}/") return resposta.json() def listar_todos(self, **filtros): """Lista todos os clientes (com paginacao automatica).""" filtros["por_pagina"] = 100 filtros["pagina"] = 1 todos = [] while True: resultado = self.listar(**filtros) if not resultado["success"]: break todos.extend(resultado["data"]["resultados"]) paginacao = resultado["data"]["paginacao"] if paginacao["proxima"] is None: break filtros["pagina"] += 1 return todos # Uso api = BuntoClientes(ambiente="staging") # Listar todos os clientes PJ de Sao Paulo clientes_sp = api.listar_todos(tipo_pessoa=2, uf="SP") print(f"Total de PJs em SP: {len(clientes_sp)}") # Criar novo cliente resultado = api.criar({ "nome": "Padaria Bom Dia Ltda", "fantasia": "Padaria Bom Dia", "tipo_pessoa": 2, "cnpj_cpf": "98.765.432/0001-10", "email": "contato@padariabomdia.com.br", "celular": "(11) 95555-1234", "uf": "SP", "cidade": "Guarulhos", }) if resultado["success"]: novo_id = resultado["data"]["id"] print(f"Cliente criado: ID {novo_id}") # Obter detalhes completos detalhes = api.obter(novo_id) if detalhes["success"]: cliente = detalhes["data"] print(f"CNPJ: {cliente['cnpj_cpf']}") print(f"E-mail: {cliente['email']}")
Importar clientes de planilha (JavaScript/Node.js)
javascriptclass BuntoClientes { constructor(token, baseUrl = null) { this.token = token ?? process.env.BUNTO_API_TOKEN; this.baseUrl = baseUrl ?? "https://api-backend.bunto.com.br/v1"; this.headers = { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json", }; } async requisicao(metodo, endpoint, opcoes = {}) { const url = `${this.baseUrl}/${endpoint.replace(/^\//, "")}`; for (let tentativa = 0; tentativa < 3; tentativa++) { const resposta = await fetch(url, { method: metodo, headers: this.headers, ...opcoes, }); if (resposta.status === 429) { const tempo = 2 ** tentativa * 15 * 1000; console.log(`Rate limit. Aguardando ${tempo / 1000}s...`); await new Promise((resolve) => setTimeout(resolve, tempo)); continue; } return resposta.json(); } throw new Error("Limite de tentativas excedido"); } async criar(dados) { return this.requisicao("POST", "/clientes/", { body: JSON.stringify(dados), }); } async atualizar(clienteId, dados) { return this.requisicao("PATCH", `/clientes/${clienteId}/`, { body: JSON.stringify(dados), }); } } // Importar clientes de uma lista const api = new BuntoClientes(undefined, "staging"); const clientesParaImportar = [ { nome: "Roberto Mendes", tipo_pessoa: 1, cnpj_cpf: "111.222.333-44", email: "roberto.mendes@email.com.br", celular: "(31) 98888-7777", uf: "MG", cidade: "Belo Horizonte", }, { nome: "Tech Solutions SA", fantasia: "Tech Solutions", tipo_pessoa: 2, cnpj_cpf: "11.222.333/0001-44", email: "contato@techsolutions.com.br", tel_fixo: "(41) 3333-4444", uf: "PR", cidade: "Curitiba", }, ]; for (const cliente of clientesParaImportar) { const resultado = await api.criar(cliente); if (resultado.success) { console.log(`Criado: ${cliente.nome} (ID: ${resultado.data.id})`); } else { console.error(`Erro ao criar ${cliente.nome}:`, resultado); } // Respeitar rate limit de escrita (30/min) await new Promise((resolve) => setTimeout(resolve, 2100)); }
Códigos de Status HTTP
| Código | Significado | Quando ocorre |
|---|---|---|
| 200 | Sucesso | Consulta, atualização ou exclusão bem-sucedida |
| 201 | Criado | Cliente criado com sucesso via POST |
| 400 | Erro de validação | Dados inválidos, CPF/CNPJ duplicado, cliente em uso |
| 401 | Não autenticado | Token ausente, inválido, expirado ou revogado |
| 403 | Sem permissão | Token não tem escopo para clientes |
| 404 | Não encontrado | Cliente não existe ou pertence a outra empresa |
| 429 | Muitas requisições | Rate limit excedido |
| 500 | Erro interno | Erro inesperado no servidor |