Início rápido
Como começar: os primeiros passos na API. Este guia leva um pedido do começo ao fim: cotar, enviar, acompanhar e baixar o resultado de uma campanha simulada em Belo Horizonte.
Antes de começar
Seção intitulada “Antes de começar”-
Crie a conta em app.brasilsintetico.com, só com nome, e-mail e senha. Ela já vem com um primeiro projeto; crie outros quando quiser. Projetos separam jobs, chaves e agentes; os créditos são da organização.
-
Tenha créditos. Um crédito é um real. Compre no app, em Créditos e uso, por Pix ou boleto, a partir de R$ 20,00. Na primeira compra, o app pede o CNPJ da empresa e o endereço da nota fiscal.
-
Crie uma chave de API na página do projeto, com os escopos
results:readesimulate. A chave tem a formabs_<prefixo>_<segredo>e aparece uma única vez; guardamos só o hash dela.Janela do terminal export BS_API_KEY="bs_..."Os exemplos em curl leem as respostas com o
jq.
1. Confira a chave
Seção intitulada “1. Confira a chave”curl https://api.brasilsintetico.com/v1/whoami \ -H "Authorization: Bearer $BS_API_KEY"import osimport httpx
api = httpx.Client( base_url="https://api.brasilsintetico.com", headers={"Authorization": f"Bearer {os.environ['BS_API_KEY']}"}, timeout=30,)print(api.get("/v1/whoami").json())const BASE = "https://api.brasilsintetico.com";const headers = { Authorization: `Bearer ${process.env.BS_API_KEY}`, "Content-Type": "application/json",};
const me = await fetch(`${BASE}/v1/whoami`, { headers });console.log(await me.json());A resposta diz o projeto, a organização e os escopos da chave:
{ "project_id": "6f1c…", "organization_id": "0b9e…", "key_prefix": "a1b2c3d4e5f6", "scopes": ["results:read", "simulate"]}2. Escreva o pedido
Seção intitulada “2. Escreva o pedido”O pedido é um cenário de campanha, um documento JSON fechado. Este simula uma mensagem para adultos de 20 a 59 anos de Belo Horizonte, nas faixas de renda 1 a 3, por 30 dias a partir de 1º de novembro de 2026: digital nas duas primeiras semanas e rádio do dia 7 ao 21. Ele pede o alcance e quem repassa a mensagem:
{ "versao_contrato": 3, "release": "v2.0.0", "familia": "recepcao", "geografia": { "ufs": ["MG"], "municipios": ["3106200"] }, "publico": { "idades": [[20, 59]], "faixas_renda": [1, 2, 3] }, "canais": [ { "canal": "digital", "dia_inicio": 1, "dia_fim": 14, "intensidade": 0.5 }, { "canal": "radio", "dia_inicio": 7, "dia_fim": 21, "intensidade": 0.3 } ], "mensagem": { "stim_id": "af9cca9c97fcb5b610476bde8756e849", "template_ver": "tpl-ref", "politica": "pol-ref" }, "horizonte": { "data_inicio": "2026-11-01", "dias": 30 }, "replicas": 2, "grupo_comparacao": "campanha-bh", "kpis": ["alcance", "repassa"], "nome": "Campanha em Belo Horizonte"}A simulação roda sobre Minas Gerais inteira, porque o boca a boca não para na divisa do município; o público só diz a quem a mídia chega. Com dois canais, o motor roda também as variantes que medem o efeito de cada um.
Cada campo está em O cenário de campanha. O JSON Schema completo é público, em GET /v1/schemas/cenario-campanha.
3. Cote antes de gastar
Seção intitulada “3. Cote antes de gastar”POST /v1/jobs/estimate valida o pedido e devolve o preço, sem reservar nada:
curl https://api.brasilsintetico.com/v1/jobs/estimate \ -H "Authorization: Bearer $BS_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"pedido\": $(cat pedido.json)}"import json
pedido = json.load(open("pedido.json"))quote = api.post("/v1/jobs/estimate", json={"pedido": pedido}).json()print(quote["preco_micros"] / 1_000_000, "créditos")import { readFileSync } from "node:fs";
const pedido = JSON.parse(readFileSync("pedido.json", "utf8"));const quote = await fetch(`${BASE}/v1/jobs/estimate`, { method: "POST", headers, body: JSON.stringify({ pedido }),}).then((r) => r.json());console.log(quote.preco_micros / 1_000_000, "créditos");{ "release": "v2.0.0", "ufs": ["MG"], "dias": 30, "variantes": 5, "replicas": 2, "pessoa_dias": 6133511100, "preco_micros": 14499640}Os valores vêm em milionésimos de real: 14499640 é R$ 14,499640. A conta está em Créditos e preço.
4. Envie o job
Seção intitulada “4. Envie o job”POST /v1/jobs reserva o preço e cria o job. O cabeçalho Idempotency-Key é obrigatório: repetir a chamada com a mesma chave e o mesmo pedido devolve o mesmo job (status 200) em vez de cobrar duas vezes.
JOB_ID=$(curl -s https://api.brasilsintetico.com/v1/jobs \ -H "Authorization: Bearer $BS_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: campanha-bh-1" \ -d "{\"pedido\": $(cat pedido.json)}" | jq -r .id)echo "$JOB_ID"job = api.post( "/v1/jobs", json={"pedido": pedido}, headers={"Idempotency-Key": "campanha-bh-1"},).json()print(job["id"], job["estado"])const job = await fetch(`${BASE}/v1/jobs`, { method: "POST", headers: { ...headers, "Idempotency-Key": "campanha-bh-1" }, body: JSON.stringify({ pedido }),}).then((r) => r.json());console.log(job.id, job.estado);O job nasce reservado e espera a vez numa máquina dedicada. Os estados estão em Jobs.
5. Acompanhe
Seção intitulada “5. Acompanhe”Consulte GET /v1/jobs/{id} até o estado ser concluido, falhou ou cancelado. Um job leva de minutos a horas, conforme o tamanho; consultar a cada 30 a 60 segundos basta e respeita o limite de chamadas. Ou ative um webhook no projeto e receba um aviso assinado quando o job terminar.
curl https://api.brasilsintetico.com/v1/jobs/$JOB_ID \ -H "Authorization: Bearer $BS_API_KEY"import time
while job["estado"] not in ("concluido", "falhou", "cancelado"): time.sleep(60) job = api.get(f"/v1/jobs/{job['id']}").json()print(job["estado"])let current = job;while (!["concluido", "falhou", "cancelado"].includes(current.estado)) { await new Promise((r) => setTimeout(r, 60_000)); current = await fetch(`${BASE}/v1/jobs/${job.id}`, { headers }).then((r) => r.json());}console.log(current.estado);Se o job falhar, a reserva inteira volta para a organização.
6. Leia os achados
Seção intitulada “6. Leia os achados”GET /v1/jobs/{id}/findings devolve uma tabela dos achados em linhas: saturacao por padrão, ou alcance_efetivo, alcance_potencial, contribuicao, elasticidade e veredito. Por padrão vêm só os municípios do público do cenário, e limite vai até 1.000 linhas. Não há arquivo para baixar: o resultado de um job são os achados e o veredito, guardados por 365 dias.
curl -s "https://api.brasilsintetico.com/v1/jobs/$JOB_ID/findings?tabela=saturacao" \ -H "Authorization: Bearer $BS_API_KEY" | jq '.linhas[0:3]'found = api.get(f"/v1/jobs/{job['id']}/findings", params={"tabela": "saturacao"}).json()for row in found["linhas"][:3]: print(row)const found = await fetch(`${BASE}/v1/jobs/${job.id}/findings?tabela=saturacao`, { headers }).then((r) => r.json());console.log(found.linhas.slice(0, 3));O que cada tabela traz está em O resultado.

