Developers/Documentação da API/API de Categorias

API de Categorias

CRUD completo de categorias hierárquicas via API. Organize produtos em árvore de categorias.

13/02/202612 min de leitura2 visualizações

Gerencie a árvore de categorias do Bunto ERP. Este endpoint permite listar, consultar, criar, atualizar e excluir categorias de forma hierárquica (categorias pai e subcategorias) via API.

Base URL

Produção:

https://api-backend.bunto.com.br/v1/categorias/

Autenticação

Todas as requisições exigem um Token de API no header Authorization:

Authorization: Bearer bnt_seu_token_aqui

Tokens são gerados em Integrações -> Tokens de API no painel do Bunto ERP. Escopo necessário: categorias com a ação correspondente (read, write ou delete).


Endpoints

Listar Categorias

GET /v1/categorias/

Escopo necessário: categorias: read

Retorna a lista paginada de categorias da empresa.

Query Parameters

ParâmetroTipoPadrãoDescrição
paginainteger1Número da página
por_paginainteger25Registros por página (máximo: 100)
buscastring-Busca por nome ou código
ativoboolean-Filtrar por status: true (ativas) ou false (inativas)
raizboolean-true para retornar apenas categorias raiz (sem categoria pai)
categoria_pai_idinteger-Filtrar subcategorias de uma categoria pai específica
ordenarstringnomeCampo de ordenação: nome, ordem, codigo
direcaostringascDireção da ordenação: asc ou desc

Exemplo com cURL

bash
curl 'https://api-backend.bunto.com.br/v1/categorias/?pagina=1&por_pagina=10&raiz=true&ordenar=ordem&direcao=asc' \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Listar categorias raiz ordenadas por ordem resposta = requests.get( f"{BASE_URL}/categorias/", headers=headers, params={ "pagina": 1, "por_pagina": 25, "raiz": "true", "ordenar": "ordem", "direcao": "asc", }, ) dados = resposta.json() if dados["success"]: categorias = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de categorias: {paginacao['total_registros']}") for categoria in categorias: print(f" - [{categoria['codigo']}] {categoria['nome']} (nível {categoria['nivel']})") else: print(f"Erro: {resposta.status_code}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const params = new URLSearchParams({ pagina: "1", por_pagina: "25", raiz: "true", ordenar: "ordem", direcao: "asc", }); const resposta = await fetch(`${BASE_URL}/categorias/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const categorias = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de categorias: ${paginacao.total_registros}`); categorias.forEach((categoria) => { console.log(` - [${categoria.codigo}] ${categoria.nome} (nível ${categoria.nivel})`); }); } else { console.error(`Erro: ${resposta.status}`); }

Resposta (200 OK)

json
{ "success": true, "message": "8 registros encontrados", "data": { "resultados": [ { "id": 1, "nome": "Vestuário", "codigo":"VES", "caminho_completo": "Vestuário", "ativo": true, "ordem": 1, "categoria_pai_id": null, "nivel": 0 }, { "id": 2, "nome": "Calçados", "codigo":"CAL", "caminho_completo": "Calçados", "ativo": true, "ordem": 2, "categoria_pai_id": null, "nivel": 0 }, { "id": 5, "nome": "Acessórios", "codigo":"ACE", "caminho_completo": "Acessórios", "ativo": true, "ordem": 3, "categoria_pai_id": null, "nivel": 0 } ], "paginacao": { "pagina_atual": 1, "total_paginas": 1, "total_registros": 8, "por_pagina": 25, "proxima": null, "anterior": null } } }

Campos da Listagem

CampoTipoDescrição
idintegerIdentificador único da categoria
nomestringNome da categoria
codigostring / nullCódigo da categoria
caminho_completostringCaminho hierárquico completo (ex: "Vestuário > Camisetas > Manga Longa")
ativobooleanSe a categoria está ativa
ordemintegerPosição de ordenação dentro do mesmo nível
categoria_pai_idinteger / nullID da categoria pai (null para categorias raiz)
nivelintegerNível hierárquico (0 = raiz, 1 = primeiro nível, 2 = segundo nível, etc.)

Obter Categoria

GET /v1/categorias/{id}/

Escopo necessário: categorias: read

Retorna os dados completos de uma categoria específica, incluindo a lista de subcategorias diretas.

Exemplo com cURL

bash
curl https://api-backend.bunto.com.br/v1/categorias/1/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } categoria_id = 1 resposta = requests.get(f"{BASE_URL}/categorias/{categoria_id}/", headers=headers) dados = resposta.json() if dados["success"]: categoria = dados["data"] print(f"Categoria: {categoria['nome']}") print(f"Caminho: {categoria['caminho_completo']}") if categoria["subcategorias"]: print("Subcategorias:") for sub in categoria["subcategorias"]: print(f" - {sub['nome']}") else: print(f"Erro: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const categoriaId = 1; const resposta = await fetch(`${BASE_URL}/categorias/${categoriaId}/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const categoria = dados.data; console.log(`Categoria: ${categoria.nome}`); console.log(`Caminho: ${categoria.caminho_completo}`); if (categoria.subcategorias && categoria.subcategorias.length > 0) { console.log("Subcategorias:"); categoria.subcategorias.forEach((sub) => { console.log(` - ${sub.nome}`); }); } } else { console.error(`Erro: ${JSON.stringify(dados)}`); }

Resposta (200 OK)

json
{ "success": true, "message": "Registro encontrado", "data": { "id": 1, "nome": "Vestuário", "codigo":"VES", "caminho_completo": "Vestuário", "ativo": true, "ordem": 1, "categoria_pai_id": null, "nivel": 0, "descricao":"Roupas e peças de vestuário em geral.", "icone": "shirt", "cor": "#4A90D9", "id_externo": null, "data_criacao": "2026-01-10T09:00:00-03:00", "data_atualizacao": "2026-02-05T14:30:00-03:00", "categoria_pai_nome": null, "subcategorias": [ { "id": 3, "nome": "Camisetas", "codigo":"VES-CAM", "ativo": true, "ordem": 1 }, { "id": 4, "nome": "Calças", "codigo":"VES-CAL", "ativo": true, "ordem": 2 }, { "id": 7, "nome": "Bermudas", "codigo":"VES-BER", "ativo": true, "ordem": 3 } ] } }

Campos do Detalhe (adicionais à listagem)

CampoTipoDescrição
descricaostring / nullDescrição da categoria
iconestring / nullNome do ícone associado (máximo 50 caracteres)
corstring / nullCor associada em formato hexadecimal ou nome (máximo 20 caracteres)
id_externostring / nullIdentificador externo para integrações (máximo 50 caracteres)
data_criacaodatetimeData e hora de criação
data_atualizacaodatetimeData e hora da última atualização
categoria_pai_nomestring / nullNome da categoria pai (null para categorias raiz)
subcategoriasarrayLista de subcategorias diretas (id, nome, código, ativo, ordem)

Criar Categoria

POST /v1/categorias/

Escopo necessário: categorias: write

Cria uma nova categoria na empresa do token autenticado. Pode ser uma categoria raiz ou uma subcategoria (informando categoria_pai_id).

Campos do Request Body

CampoTipoObrigatórioDescrição
nomestringSimNome da categoria (máximo 100 caracteres)
codigostringNãoCódigo da categoria (máximo 20 caracteres)
descricaostringNãoDescrição da categoria
categoria_pai_idintegerNãoID da categoria pai (null para categoria raiz)
ativobooleanNãoSe a categoria está ativa (padrão: true)
iconestringNãoNome do ícone (máximo 50 caracteres)
corstringNãoCor associada (máximo 20 caracteres)
ordemintegerNãoPosição de ordenação (padrão: 0)
id_externostringNãoIdentificador externo para integrações (máximo 50 caracteres)

Exemplo com cURL

bash
curl -X POST https://api-backend.bunto.com.br/v1/categorias/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "nome": "Manga Longa", "codigo":"VES-CAM-ML", "descricao":"Camisetas de manga longa para todas as estações.", "categoria_pai_id": 3, "ativo": true, "icone": "shirt", "cor": "#2E7D32", "ordem": 1 }'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } nova_categoria = { "nome": "Manga Longa", "codigo":"VES-CAM-ML", "descricao":"Camisetas de manga longa para todas as estações.", "categoria_pai_id": 3, "ativo": True, "icone": "shirt", "cor": "#2E7D32", "ordem": 1, } resposta = requests.post(f"{BASE_URL}/categorias/", headers=headers, json=nova_categoria) dados = resposta.json() if dados["success"]: categoria = dados["data"] print(f"Categoria criada com sucesso! ID: {categoria['id']}") print(f"Caminho: {categoria['caminho_completo']}") else: print(f"Erro ao criar categoria: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novaCategoria = { nome: "Manga Longa", codigo: "VES-CAM-ML", descricao: "Camisetas de manga longa para todas as estações.", categoria_pai_id: 3, ativo: true, icone: "shirt", cor: "#2E7D32", ordem: 1, }; const resposta = await fetch(`${BASE_URL}/categorias/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novaCategoria), }); const dados = await resposta.json(); if (dados.success) { console.log(`Categoria criada com sucesso! ID: ${dados.data.id}`); console.log(`Caminho: ${dados.data.caminho_completo}`); } else { console.error(`Erro ao criar categoria:`, dados); }

Resposta (201 Created)

json
{ "success": true, "message": "Categoria criada com sucesso", "data": { "id": 12, "nome": "Manga Longa", "codigo":"VES-CAM-ML", "caminho_completo": "Vestuário > Camisetas > Manga Longa", "ativo": true, "ordem": 1, "categoria_pai_id": 3, "nivel": 2, "descricao":"Camisetas de manga longa para todas as estações.", "icone": "shirt", "cor": "#2E7D32", "id_externo": null, "data_criacao": "2026-02-12T15:30:00-03:00", "data_atualizacao": "2026-02-12T15:30:00-03:00", "categoria_pai_nome": "Camisetas", "subcategorias": [] } }

Atualizar Categoria

PUT /v1/categorias/{id}/
PATCH /v1/categorias/{id}/

Escopo necessário: categorias: write

Atualiza uma categoria existente. Use PUT para atualização completa ou PATCH para atualização parcial (apenas os campos enviados serão alterados).

Exemplo com cURL (PATCH - atualização parcial)

bash
curl -X PATCH https://api-backend.bunto.com.br/v1/categorias/12/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "nome": "Manga Longa Inverno", "descricao":"Camisetas de manga longa ideais para o inverno.", "ordem": 2 }'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } categoria_id = 12 # Atualizacao parcial (PATCH) - apenas os campos que mudaram atualizacao = { "nome": "Manga Longa Inverno", "descricao":"Camisetas de manga longa ideais para o inverno.", "ordem": 2, } resposta = requests.patch( f"{BASE_URL}/categorias/{categoria_id}/", headers=headers, json=atualizacao, ) dados = resposta.json() if dados["success"]: categoria = dados["data"] print(f"Categoria atualizada! Nome: {categoria['nome']}") print(f"Caminho: {categoria['caminho_completo']}") else: print(f"Erro ao atualizar: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const categoriaId = 12; const atualizacao = { nome: "Manga Longa Inverno", descricao: "Camisetas de manga longa ideais para o inverno.", ordem: 2, }; const resposta = await fetch(`${BASE_URL}/categorias/${categoriaId}/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Categoria atualizada! Nome: ${dados.data.nome}`); console.log(`Caminho: ${dados.data.caminho_completo}`); } else { console.error(`Erro ao atualizar:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Categoria atualizada com sucesso", "data": { "id": 12, "nome": "Manga Longa Inverno", "codigo":"VES-CAM-ML", "caminho_completo": "Vestuário > Camisetas > Manga Longa Inverno", "ativo": true, "ordem": 2, "categoria_pai_id": 3, "nivel": 2, "descricao":"Camisetas de manga longa ideais para o inverno.", "icone": "shirt", "cor": "#2E7D32", "id_externo": null, "data_criacao": "2026-02-12T15:30:00-03:00", "data_atualizacao": "2026-02-12T16:45:00-03:00", "categoria_pai_nome": "Camisetas", "subcategorias": [] } }

Excluir Categoria (Soft Delete)

DELETE /v1/categorias/{id}/

Escopo necessário: categorias: delete

Desativa a categoria (soft delete). O registro não é removido permanentemente do banco de dados -- o campo ativo é definido como false, preservando o histórico e os vínculos com produtos.

Exemplo com cURL

bash
curl -X DELETE https://api-backend.bunto.com.br/v1/categorias/12/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} categoria_id = 12 resposta = requests.delete(f"{BASE_URL}/categorias/{categoria_id}/", headers=headers) dados = resposta.json() if dados["success"]: print("Categoria excluída com sucesso!") else: print(f"Erro ao excluir: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const categoriaId = 12; const resposta = await fetch(`${BASE_URL}/categorias/${categoriaId}/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Categoria excluída com sucesso!"); } else { console.error(`Erro ao excluir:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Registro excluído com sucesso" }

Importante: Categorias desativadas via soft delete não aparecem mais nas listagens com filtro ativo=true. Para visualizar categorias inativas, filtre por ativo=false.


Erros Comuns

CódigoErroCausaSolução
400VALIDATION_ERRORDados enviados são inválidos (campo obrigatório ausente, formato incorreto, etc.)Verifique os campos obrigatórios e os tipos de dados
401Token invalidoToken ausente, expirado, revogado ou mal formatadoVerifique se o header é Authorization: Bearer bnt_xxx
403Token nao tem permissaoO token não possui o escopo categorias ou a ação necessária (read, write, delete)Verifique os escopos do token no painel
404Nao encontradoCategoria não existe ou pertence a outra empresaConfirme o ID e se a categoria pertence à empresa do token
429Limite de requisicoes excedidoRate limit ultrapassado para o tipo de operaçãoImplemente retry com backoff exponencial

Exemplo de Resposta de Erro (400)

json
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Erro de validacao", "details": { "nome": ["Este campo é obrigatorio."], "codigo": ["Certifique-se de que este campo nao tenha mais de 20 caracteres."] } } }

Exemplo de Resposta de Erro (403)

json
{ "detail": "Token nao tem permissao para este recurso" }

Rate Limiting

A API aplica limites de requisição por token (não por IP). Cada tipo de operação tem um limite diferente.

OperaçãoMétodos HTTPLimite
LeituraGET, HEAD, OPTIONS120 requisições/minuto
EscritaPOST, PUT, PATCH30 requisições/minuto
ExclusãoDELETE10 requisições/minuto

Ao exceder o limite, a API retorna 429 Too Many Requests:

json
{ "detail": "Limite de requisicoes excedido. Tente novamente em 45 segundos." }

Boas práticas

  • Use por_pagina=100 para reduzir o número de requisições ao listar categorias
  • Implemente retry com backoff exponencial ao receber 429
  • Armazene dados em cache local quando possível
  • Para atualizações em lote, use PATCH apenas com os campos alterados para economizar requisições de escrita

Paginação

Todos os endpoints de listagem retornam dados paginados.

Parâmetros

ParâmetroTipoPadrãoMáximoDescrição
paginainteger1-Número da página
por_paginainteger25100Registros por página

Atenção: Os parâmetros são pagina e por_pagina (em português), não page e per_page.

Objeto paginacao na resposta

CampoTipoDescrição
pagina_atualintegerNúmero da página atual
total_paginasintegerTotal de páginas disponíveis
total_registrosintegerTotal de registros encontrados
por_paginaintegerRegistros por página
proximastring / nullURL da próxima página (null se for a última)
anteriorstring / nullURL da página anterior (null se for a primeira)

Exemplo: percorrer todas as páginas (Python)

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} todas_as_categorias = [] url = f"{BASE_URL}/categorias/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todas_as_categorias.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todas_as_categorias)} categorias")
APICategoriasCRUDcURLJavaScriptPythonREST
Recursos para IA