SDK Python para a API REST do Bitrix24, escrito do zero — sem nenhuma
biblioteca de terceiros além de requests. Resolve, de fábrica, os três
problemas que toda integração séria com o Bitrix24 encontra na prática:
rate limit, paginação de bases grandes e operações em massa.
from bitrix24 import Bitrix24Client, CRM, fetch_all, batch
client = Bitrix24Client("https://seuportal.bitrix24.com.br/rest/1/seutoken")
crm = CRM(client)
lead_id = crm.leads.add({"TITLE": "Lead do site", "NAME": "Helena"})
crm.deals.move_stage(42, "WON")
todos = fetch_all(client, "crm.deal.list", {"filter": {"STAGE_ID": "NEW"}})
batch(client, {f"up_{d['ID']}": ("crm.deal.update",
{"id": d["ID"], "fields": {"OPPORTUNITY": 1000}}) for d in todos})Dez linhas: cria lead, move negócio no funil, extrai a base inteira e atualiza tudo em massa — com throttle, retry e paginação acontecendo por baixo, sem você pensar neles.
A API do Bitrix24 é poderosa, mas tem três armadilhas que código ingênuo só descobre em produção:
O portal corta quem passa de ~2 req/s com QUERY_LIMIT_EXCEEDED (HTTP 503).
Scripts sem controle funcionam no teste com 10 registros e quebram na base
real. O Bitrix24Client faz throttle automático (espaça as chamadas) e,
se ainda assim o corte vier, retenta com backoff exponencial (1s, 2s,
4s...) antes de desistir. Erros de autenticação (invalid_token, HTTP 401)
não são retentados — falham rápido com AuthError.
O jeito óbvio de paginar (start=0, 50, 100...) esconde um custo: a cada
página o servidor roda um SELECT COUNT(*) para devolver o campo total. Em
uma base com 200 mil negócios, as últimas páginas ficam ordens de grandeza
mais lentas que as primeiras.
A solução documentada (e pouco conhecida) é start=-1: desliga a contagem e
pagina por cursor de ID — ordena por ID crescente e pede
filter[">ID"] = último_id_visto a cada página. Tempo de página constante do
primeiro ao último registro. É exatamente o que fetch_all() faz por padrão,
com fallback automático para a paginação clássica se o método não suportar.
O método batch aceita até 50 comandos numa única requisição HTTP — 50
operações pelo custo (e pela cota de rate limit) de 1. Atualizar 500 negócios
um a um custa 500 requisições (~4 minutos só de throttle); com batch(), 10.
O SDK monta as querystrings no formato da API (fields[TITLE]=...), divide
lotes maiores que 50 automaticamente e suporta encadeamento com
$result[nome][campo] dentro do mesmo lote.
git clone https://github.com/matheusraull99/bitrix24-python-sdk.git
cd bitrix24-python-sdk
python -m venv .venv
.venv\Scripts\activate # Windows (Linux/macOS: source .venv/bin/activate)
pip install -r requirements.txt- Crie uma conta gratuita em bitrix24.com.br — o plano free já inclui CRM e API REST.
- No portal: Recursos para desenvolvedores → Outros → Webhook de entrada.
- Marque as permissões (
crmé suficiente para este SDK) e copie a URL gerada (https://seuportal.bitrix24.com.br/rest/1/token...). copy .env.example .enve cole a URL emB24_WEBHOOK_URL.
Sem portal? Sem problema. O SDK tem um modo mock completo (
create_mock_client()): um portal fake em memória, semeado com 120 negócios, 40 leads, 30 contatos e 15 itens de Smart Process, que responde no formato exato da API (result/total/next/time), incluindobatchcom resolução de$result[...]. CLI, exemplos e a suíte de testes inteira rodam offline.
python cli.py deals list --stage NEW # lista negócios por estágio
python cli.py leads export --csv saida.csv # exporta todos os leads
python cli.py call crm.deal.fields # qualquer método REST cru
python cli.py --mock deals list # força o portal fake| Script | O que demonstra |
|---|---|
examples/01_exportar_negocios_csv.py |
extrair a base inteira de negócios para CSV com paginação start=-1 |
examples/02_criar_lead_e_mover_funil.py |
criar lead, converter e mover o negócio de estágio no funil |
examples/03_batch_atualizacao_massa.py |
reajuste em massa: dezenas de updates em pouquíssimas requisições via batch |
pip install -r requirements-dev.txt
pytest -v30 testes, todos contra o mock (zero rede), cobrindo: retry com backoff
exponencial e rate limit com relógio fake (nenhum teste dorme de verdade),
paginação start=-1 com verificação do cursor >ID requisição a requisição,
divisão de lotes do batch, encadeamento $result e os helpers de CRM.
bitrix24/
├── client.py # Bitrix24Client: HTTP, rate limit, retry, exceções tipadas
├── exceptions.py # Bitrix24Error, RateLimitError, AuthError
├── pagination.py # fetch_all: start=-1 (cursor por ID) + fallback clássico
├── batch.py # batch: lotes de 50, querystring PHP-style, $result
├── crm.py # helpers: leads, deals, contacts, crm.item.* (Smart Process)
└── mock.py # portal fake em memória (fixtures realistas)
cli.py # linha de comando
examples/ # 3 cenários reais comentados
tests/ # pytest, 100% offline
- Transporte injetável. O client depende de um callable
(url, payload) -> (status, dict), não derequestsdiretamente. É isso que torna o modo mock trivial e os testes 100% determinísticos — e permite trocar a stack HTTP sem tocar na lógica de retry/throttle. - Tempo injetável.
sleepeclocksão parâmetros do client. Os testes de rate limit e backoff usam um relógio fake que "dorme" instantaneamente — a suíte inteira roda em ~1 segundo mesmo testando esperas de vários segundos. - Exceções tipadas em vez de dicionário de erro.
AuthError(não retentável),RateLimitError(retentável, já retentada) eBitrix24Error(genérica) carregamcodeedescriptionda API. O chamador decide por tipo, sem parse de string. call()devolve o payload completo, não sóresult.total,nextetimesão metadados necessários para paginação e diagnóstico; esconder isso atrás de um retorno "conveniente" força gambiarras depois.- Mock com a semântica real, não stubs soltos. O
MockTransportreproduz os formatos do CRM clássico (valores string,"ID": "17") e docrm.item.*(camelCase, envelopeitems), a diferença entrestart=-1e paginação comtotal/next, o limite de 50 comandos dobatche a substituição server-side de$result. Testar contra a semântica certa é o que dá confiança de que o código funciona no portal de verdade. - Sem dependências além de
requests. Paginação, batch e rate limit são regras de negócio da API — implementá-las é o objetivo do projeto, não algo a terceirizar.
MIT — use à vontade.