DataBolsa docs

SDK TypeScript

@databolsa/sdk — cliente tipado da Serving API, com tipos gerados do contrato.

npm install @databolsa/sdk
import { DataBolsa } from "@databolsa/sdk";

const db = new DataBolsa("https://api.databolsa.com", {
  apiKey: process.env.DATABOLSA_API_KEY,
});

const petr4 = await db.objects.resolveOne("PETR4", { kind: "equity_security" });
const fechamento = await petr4.facts.history("close", { from: "2026-01-01" });
const issuer = await petr4.issuer();
const indicadores = await issuer.facts.latest();
  • ESM-only, tipos gerados do contrato OpenAPI via openapi-typescript — um método por operação, mesmos nomes da referência (getObjectHistory, rankObjects…).
  • Exporta os tipos de domínio (Stock, Fii, ObjectHistoryResponse, …) e os tipos crus do contrato (paths, components, operations).
  • Erros da API viram exceções com o detail do problem+json; endpoints fora do preview do seu deploy lançam NotInPreviewError.

Objetos: comece pelo que você conhece

db.objects é a entrada object-first. Você resolve um ticker, CNPJ, ISIN ou nome para o objeto do grafo e navega por fatos, relações e capítulos sem saber qual operação responde por baixo. Os métodos flat continuam ao lado, inalterados.

// Papel → cotações, fatos, emissora
const petr4 = await db.objects.resolveOne("PETR4", { kind: "equity_security" });
const quotes = await petr4.market.quotes.history({ from: "2025-01-01" });
const close = await petr4.facts.history("close", { from: "2025-01-01" }); // uma série, com régua
const fatos = await petr4.at("2025-06-30").facts.latest();                // o tempo mora no handle
const petrobras = await petr4.issuer();

// Companhia → instrumentos emitidos → rating do papel
const debentures = await petrobras.instruments({ subkind: "debenture" });
const ratings = await debentures[0].credit.ratings.list({ scale: "national_br" });
const doEmissor = await petrobras.credit.ratings.list({ perAgency: true }); // uma nota por agência e escala

// Relações vêm do vocabulário do grafo: um método por verbo, gerado
const detentores = await petr4.holders({ limit: 20 });   // fundos que detêm o papel
const emitidos = await petrobras.issued();                // papéis, debêntures e ofertas

Descobrir e executar sem conhecer o objeto

Nem toda capacidade tem sujeito, e nem sempre se sabe qual Function serve a pergunta. As três fachadas de descoberta respondem isso sem sair do SDK:

const candidatas = await db.functions.list({ query: "carteira do fundo" });
const spec = await db.functions.describe("funds.holdings.latest");   // input_schema, output_schema
const r = await db.functions.execute("funds.holdings.latest", {
  subject: { resolve: "HGLG11" },
  input: { limit: 20 },
});
r.result;   // o corpo; `r.subject` diz qual objeto respondeu

await db.functions.execute("macro.regime.get", {});                  // Function sem sujeito
await db.capabilities.discover({ query: "criar carteira" });         // primitivas, Functions e Actions
await db.modules.list();                                             // core e extensões instaladas

execute é tipado pelo catálogo: input é o schema da Function e result é o dela, não o de uma rota por baixo. executeAny é a porta sem tipo para Function publicada depois deste SDK.

O que o handle sabe:

ChamadaO que faz
describe()o mapa completo do objeto (getObject), buscado uma vez
facts.latest({ facts? }) · facts.history(medida, { from, to, limit })medidas com data-base, unidade e escala
links(rel, { direction, limit }) · related(rel, direction, { kind })atravessa uma relação; related devolve handles
aspects() · aspect(nome, params, { series })os capítulos do tipo, cada um com available (este objeto tem dado?); executa um pelo nome
at(data)o mesmo objeto numa data: fatos e retratos recebem at, séries recebem to
fn("funds.holdings.latest", params)executa uma Function pelo id; input e resposta tipados pelo SCHEMA DELA no contrato (FunctionsByKind diz o que cada tipo publica; Function fora do tipo não compila). fnAny(id, params) é a porta sem tipo para Function publicada depois do SDK
holders(), issued(), issuer(), holdings()um método por relação do tipo, gerado do vocabulário, tipado nas duas pontas: petr4.holders() devolve FundHandle[]. Uma página por default ({ limit, cursor }); { all: true } segue o cursor até o fim. Arestas lidas uma vez por handle
codeo código negociado (ticker, código da debênture), quando a aresta o trouxe

Handles tipados: EquitySecurityHandle (market.quotes, market.indicators, market.dividends, issuer()), CompanyHandle (securities(), instruments(), paper(ticker), credit.ratings, market.quotes com series), InstrumentHandle (credit.ratings, market.quotes, issuer()) e FundHandle (profile(), market.quotes, portfolio.latest). Todos são atalhos com nome de domínio sobre fn(); a tipagem vem do registry, não de código escrito à mão. Os demais tipos do manifesto chegam como ObjectHandle genérico; tipo que o contrato publique depois do SDK chega no ramo futuro (isFutureKind(h)), com kind de verdade. Sem kind, resolveOne devolve a união discriminada: separe o ramo futuro e o switch (h.kind) estreita.

const h = await db.objects.resolveOne("PETR4");
if (isFutureKind(h)) console.log("tipo novo:", h.kind);
else if (h.kind === "company") await h.credit.ratings.list({ perAgency: true });

O grão e o tipo governam a assinatura: company.market.quotes.history exige series (o preço é do papel); facts.history(medida) e property(nome) só aceitam o que o tipo publica (FactName<K>, PropertyName<K>); medida nova entra por facts.historyAny. at(data) viaja com a execução e a Function decide: capítulo que só responde o vigente recusa o corte com 409 temporal_cut_unsupported em vez de devolver o de hoje como se fosse daquela data. O handle é o sujeito e o tempo: o objeto do handle é o sujeito da chamada, e um parâmetro que tentasse trocá-lo não chega ao servidor. to/at divergente do at() do handle continua sendo TemporalConflictError no cliente — nunca sobrescrito em silêncio.

Carteiras e suitability não pertencem mais ao core. Instale o cliente da extensão Wallet e componha-o sobre a mesma origem e credencial, como mostrado em Extensões: db.use(...). O antigo namespace db.account.portfolios foi removido para que dados e regras da carteira permaneçam no contrato e no armazenamento próprios da extensão.

O grão está na assinatura: uma companhia com PETR3 e PETR4 não tem "um preço". petrobras.market.quotes.history() lança AmbiguousPaperError listando os papéis; escolha com { series: "PN" } ou paper("PETR4").

ErroQuandoTraz
ObjectNotFoundErrornada casou o textoquery
AmbiguousObjectErrormais de um candidato plausívelcandidates[]
AspectUnavailableErroro TIPO não tem o capítulo (sem dado não é erro: responde vazio)available[]
AmbiguousPaperErrorcapítulo de papel sem escolher o papeltickers[]
UnknownFactErrora medida não existe para o objetofact
TemporalConflictErrorto do chamador diverge do at() do handleparameter, at, pedido

As recusas que dependem do dado ou do schema são do SERVIDOR, em application/problem+json com details.code: subject_ambiguous (traz candidates), function_not_applicable (traz kinds), series_required (traz options), temporal_cut_unsupported e invalid_input (traz accepted). O vocabulário inteiro está em Como uma chamada acontece. Uma recusa só, para o SDK, a CLI, o MCP e quem chama a rota direto.

A mesma resposta como tabela

db.objects.table(query) executa uma ObjectQuery (getObjectHistory ou rankObjects) e devolve a tabela canônica: colunas com papel (time, measure, dimension, label, identity, status), tipo e régua, linhas de escalares e, em meta.query, a própria consulta para reexecutar. É o que um notebook desenha sem conhecer a operação por baixo: time no eixo x, measure no y, dimension separa séries.

const t = await db.objects.table({
  operation: "getObjectHistory",
  subject: { entity_id: petr4.id },
  input: { facts: "close", from: "2025-01-01" },
});
t.schema.columns.find((c) => c.role === "measure")?.unit; // "brl"
const rank = await db.objects.table({ operation: "rankObjects", input: { kind: "equity_security", fact: "dy_12m", limit: 10 } });

Séries com réguas diferentes no mesmo lote deixam unit da coluna value nula, trazem a régua por linha e avisam em meta.warnings (mixed_scales). Parâmetro inventado em input é 400, como na operação especializada.

Extensões: db.use(...)

O cliente core compõe com o SDK de uma extensão sobre a mesma origem e credencial:

import { DataBolsa } from "@databolsa/sdk";
import { wallet } from "@databolsa/wallet-sdk";

const db = new DataBolsa("https://api.databolsa.com", { apiKey: process.env.DATABOLSA_API_KEY });
const carteira = db.use(wallet({ workspace: "org_abc123" })); // workspace é opcional

const { data } = await carteira.listPortfolios();

O plugin recebe só { baseUrl, fetch } — o fetch já leva o Authorization do cliente. Veja Wallet.

Chave no servidor, nunca no browser

A chave identifica a SUA conta. Em apps web, chame a API do seu backend (ou de um route handler) e mantenha DATABOLSA_API_KEY como secret do servidor.

Código aberto: packages/core/sdk.