Tutorial 19 abr 2026 8 min de leitura

Como Consultar a Cotação de uma Ação da B3 em Python via API

por bolsai · 19 de abril de 2026 · 8 min de leitura

Como consultar cotação ação python é uma das primeiras perguntas de quem começa a automatizar análises do mercado brasileiro. A resposta curta cabe em cinco linhas de código e uma requisição HTTP, mas o caminho robusto envolve escolher a fonte certa, tratar erros comuns, paralelizar consultas de múltiplas ações e economizar rate limit com cache local. Este tutorial cobre os quatro tópicos com exemplos prontos para rodar, usando tickers reais da B3 como PETR4, VALE3 e ITUB4.

Opções para consultar cotação de ações brasileiras em Python

Qual a melhor forma de consultar a cotação de uma ação em Python? Uma API REST dedicada ao mercado brasileiro, autenticada por chave, é a opção mais estável e rápida. Uma chamada GET /stocks/PETR4/quote com o header X-API-Key retorna JSON com abertura, máxima, mínima, fechamento, volume e data do pregão. O código completo cabe em cinco linhas usando a biblioteca requests.

O ecossistema Python oferece três caminhos para obter o preço de uma ação da B3: bibliotecas internacionais como yfinance que adaptam tickers estrangeiros, scraping de páginas públicas como Google Finance ou Yahoo Finance, e APIs REST dedicadas ao mercado brasileiro. Cada alternativa tem um perfil de estabilidade, cobertura e esforço de manutenção bastante distinto. A escolha influencia diretamente a previsibilidade de qualquer rotina que dependa da cotação atualizada.

Planilhas com a função IMPORTHTML ou GOOGLEFINANCE resolvem consultas pontuais, mas não integram com código de produção nem sustentam pipelines que rodam de madrugada. O investidor que quer automatizar alertas, dashboards ou backtests precisa de uma camada programática com contrato estável.

Por que uma API REST é melhor que yfinance ou scraping

A biblioteca yfinance é popular porque é gratuita e conhecida internacionalmente. Para tickers brasileiros, o custo aparece em três lugares. Primeiro, o sufixo .SA precisa ser adicionado manualmente a cada papel: PETR4.SA, VALE3.SA, ITUB4.SA. Esquecer o sufixo devolve dados de outro mercado ou simplesmente um objeto vazio. Segundo, yfinance depende do endpoint não oficial do Yahoo Finance, que já quebrou várias vezes sem aviso e exigiu atualizações emergenciais da biblioteca. Terceiro, FIIs e ETFs brasileiros têm cobertura inconsistente, com campos ausentes dependendo do papel.

Scraping direto de Google Finance ou do site da B3 resolve o ponto da cobertura, mas introduz fragilidade estrutural. Qualquer mudança no HTML quebra o parser; rate limits anti-bot aparecem sem previsibilidade; e a operação fica juridicamente cinza quando o volume de requisições cresce. A API REST autenticada resolve os três problemas: contrato JSON estável, rate limit explícito e relação contratual clara com o provedor dos dados.

A bolsai ingere o arquivo COTAHIST oficial da B3 diariamente e expõe a cotação de cada ticker em um endpoint único. Ações, units e FIIs usam a mesma estrutura de resposta, sem sufixos nem tratamentos especiais por classe de ativo. O comparativo detalhado entre bolsai e brapi explora as alternativas ponto a ponto para quem está decidindo qual adotar.

Setup: instalação e API key

A autenticação da bolsai funciona por chave enviada no header X-API-Key. A criação da conta é gratuita via login Google no site principal; a chave aparece no dashboard logo após o cadastro. O plano gratuito libera 200 requisições por dia, suficiente para um dashboard pessoal ou um protótipo de estratégia com meia dúzia de ações.

As dependências Python são mínimas. O exemplo mais simples usa apenas requests; exemplos de paralelismo exigem httpx com suporte a async:

pip install requests httpx

A documentação completa lista todos os endpoints, códigos de erro e limites de paginação. Os campos retornados pelo endpoint de cotação estão descritos na tabela a seguir, alinhados ao schema StockQuote exposto pela API.

Campo Tipo Descrição
ticker string Código do papel na B3 (ex.: PETR4, VALE3, ITUB4).
trade_date date (ISO) Data do último pregão com cotação disponível, no formato YYYY-MM-DD.
open number Preço de abertura do pregão em reais.
high number Máxima do dia em reais.
low number Mínima do dia em reais.
close number Preço de fechamento em reais. Usado como preço de referência na maioria dos cálculos.
volume integer Quantidade total de ações negociadas no pregão.
traded_amount number Volume financeiro total em reais (volume multiplicado pelo preço médio).
num_trades integer Número de negócios executados durante o pregão.

Primeira consulta: cotação de PETR4 em 5 linhas

O caminho mais curto para obter o preço ação python usa a biblioteca requests, padrão de facto para chamadas HTTP no ecossistema. A cotação de PETR4 sai em cinco linhas úteis, contando o import:

import requests

HEADERS = {"X-API-Key": "sk_sua_chave_aqui"}
r = requests.get("https://api.usebolsai.com/api/v1/stocks/PETR4/quote", headers=HEADERS)
r.raise_for_status()
cotacao = r.json()

print(f"PETR4 fechou a R$ {cotacao['close']:.2f} em {cotacao['trade_date']}")
print(f"OHLC: R$ {cotacao['open']:.2f} / {cotacao['high']:.2f} / {cotacao['low']:.2f} / {cotacao['close']:.2f}")
print(f"Volume: {cotacao['volume']:,} ações  |  Financeiro: R$ {cotacao['traded_amount']:,.2f}")
PETR4 fechou a R$ 45.67 em 2026-04-17 OHLC: R$ 45.12 / 46.03 / 44.89 / 45.67 Volume: 42,318,700 ações | Financeiro: R$ 1,932,045,876.54

O raise_for_status() transforma códigos HTTP de erro (400, 401, 404, 429, 5xx) em exceções Python, o que evita silenciosamente tratar respostas de erro como JSON válido. Todo o restante é consumo direto do payload, que respeita os campos documentados na tabela acima.

O mesmo padrão funciona para qualquer papel listado na B3. Units terminam em 11 (TAEE11, SANB11, BPAC11) e seguem o mesmo contrato; FIIs também. Para consultar preço ação python de vários papéis em uma rotina, o próximo passo é evitar o laço sequencial.

Obtendo múltiplas ações em paralelo com async

Um laço for serial consultando dez tickers gasta dez vezes a latência de uma chamada. Em uma rede com RTT de 120 ms, isso são 1,2 segundos só em espera. Paralelizar com asyncio e httpx.AsyncClient derruba o tempo total para perto da latência do pior pedido:

import asyncio
import httpx

BASE = "https://api.usebolsai.com/api/v1"
HEADERS = {"X-API-Key": "sk_sua_chave_aqui"}
TICKERS = ["PETR4", "VALE3", "ITUB4", "WEGE3", "ABEV3", "BBDC4"]

async def get_quote(client, ticker):
    r = await client.get(f"{BASE}/stocks/{ticker}/quote")
    r.raise_for_status()
    return r.json()

async def main():
    async with httpx.AsyncClient(headers=HEADERS, timeout=10) as client:
        tasks = [get_quote(client, t) for t in TICKERS]
        quotes = await asyncio.gather(*tasks)
    for q in quotes:
        print(f"{q['ticker']:6} R$ {q['close']:>7.2f}  vol {q['volume']:>12,}")

asyncio.run(main())
PETR4 R$ 45.67 vol 42,318,700 VALE3 R$ 62.14 vol 28,745,310 ITUB4 R$ 34.52 vol 24,563,100 WEGE3 R$ 41.09 vol 15,872,450 ABEV3 R$ 13.88 vol 31,204,900 BBDC4 R$ 17.43 vol 19,630,210

O asyncio.gather dispara todas as requisições ao mesmo tempo e reúne os resultados na ordem original da lista. O httpx.AsyncClient reaproveita a mesma conexão TCP via keep-alive, economizando handshake TLS por chamada. Para 100 ações, esse padrão entrega o payload completo em menos de um segundo em conexões razoáveis.

Precisa consumir mais do que 200 requisições por dia? O plano Pro libera 10.000 req/dia e histórico completo desde 1986. Crie a conta gratuita agora e avalie se o free tier resolve seu caso.

Abrir documentação

Tratamento de erros: ticker inválido, rate limit, timeout

Toda API real eventualmente devolve erros. A bolsai segue padrões HTTP convencionais: 404 para ticker inexistente, 401 para API key ausente ou inválida, 429 quando o rate limit é ultrapassado, 5xx em falhas transitórias do backend. Rotinas de produção precisam distinguir entre erros permanentes (um ticker errado não vai existir no próximo retry) e transitórios (um 429 ou 503 se resolve com espera).

O padrão clássico é retry com backoff exponencial, limitado a um número razoável de tentativas. O exemplo abaixo encapsula a consulta em uma função resiliente que separa os dois casos e respeita o header Retry-After quando a API o devolve:

import time
import requests
from requests.exceptions import Timeout, ConnectionError

BASE = "https://api.usebolsai.com/api/v1"
HEADERS = {"X-API-Key": "sk_sua_chave_aqui"}

class TickerInvalido(Exception): ...
class AuthInvalida(Exception): ...

def get_quote(ticker, max_retries=4):
    url = f"{BASE}/stocks/{ticker}/quote"
    for tentativa in range(max_retries):
        try:
            r = requests.get(url, headers=HEADERS, timeout=10)
        except (Timeout, ConnectionError):
            time.sleep(2 ** tentativa)
            continue

        if r.status_code == 200:
            return r.json()
        if r.status_code == 404:
            raise TickerInvalido(f"{ticker} não encontrado na B3")
        if r.status_code == 401:
            raise AuthInvalida("API key ausente ou inválida")
        if r.status_code == 429:
            espera = int(r.headers.get("Retry-After", 2 ** tentativa))
            time.sleep(espera)
            continue
        if 500 <= r.status_code < 600:
            time.sleep(2 ** tentativa)
            continue

        r.raise_for_status()

    raise RuntimeError(f"Falha após {max_retries} tentativas em {ticker}")

# Uso
try:
    q = get_quote("PETR4")
    print(f"PETR4: R$ {q['close']:.2f}")
    q = get_quote("XXXX9")  # ticker inexistente
except TickerInvalido as e:
    print(f"Aviso: {e}")
PETR4: R$ 45.67 Aviso: XXXX9 não encontrado na B3

A lógica cobre os quatro cenários de falha mais comuns. O backoff exponencial (2 ** tentativa) cresce em 1 s, 2 s, 4 s, 8 s entre tentativas, o que dá tempo para um pico de tráfego se dissipar sem sobrecarregar o servidor. Erros de autenticação e ticker inexistente são tratados como fatais em vez de retry, porque repetir não resolve o problema.

Cache local para reduzir consumo

A cotação de fechamento muda no máximo uma vez por dia útil. Consultar o mesmo ticker cinco vezes na mesma rotina desperdiça cinco requisições do rate limit quando uma basta. Um cache local elimina o consumo redundante e deixa o código mais rápido. Para uma sessão Python curta, o functools.lru_cache resolve:

from functools import lru_cache
import requests

BASE = "https://api.usebolsai.com/api/v1"
HEADERS = {"X-API-Key": "sk_sua_chave_aqui"}

@lru_cache(maxsize=256)
def cotacao(ticker):
    r = requests.get(f"{BASE}/stocks/{ticker}/quote", headers=HEADERS, timeout=10)
    r.raise_for_status()
    return r.json()

# A primeira chamada faz requisição HTTP; as demais vêm do cache em memória.
print(cotacao("PETR4")["close"])   # 200 req/dia consumidas: 1
print(cotacao("PETR4")["close"])   # 200 req/dia consumidas: 1 (cache hit)
print(cotacao("VALE3")["close"])   # 200 req/dia consumidas: 2

Em rotinas que rodam por horas ou dias, um cache em SQLite com TTL garante que a cotação de hoje seja reutilizada sem engessar o script entre execuções. A biblioteca requests-cache resolve isso em três linhas, decorando o cliente HTTP com expiração por URL. A própria bolsai aplica cache de cinco minutos no lado do servidor, então consultas seguidas ao mesmo ticker retornam em milissegundos mesmo sem cache local.

Para séries históricas de preço em vez de apenas a cotação atual, o tutorial sobre dados históricos de ações da B3 cobre o endpoint /stocks/{ticker}/history, que devolve OHLC ajustado para splits e serve como base de backtests, cálculo de Sharpe e volatilidade realizada. O guia completo da API gratuita B3 em Python consolida em um lugar todos os endpoints disponíveis no plano gratuito.

Perguntas frequentes

A cotação é em tempo real?

A bolsai entrega a cotação de fechamento do último pregão e atualiza o registro diariamente após o encerramento da sessão regular da B3, por volta das 20h30 de Brasília. Cotação em tempo real com preço de book e ticks intradiários não faz parte do plano gratuito nem do Pro; esse caso exige um vendor especializado em feed ao vivo. Para análise fundamentalista, backtest diário ou dashboard de carteira, o fechamento D-0 é suficiente.

Como consultar cotação de FIIs?

Fundos imobiliários seguem a mesma estrutura de tickers da B3, terminados em 11 (por exemplo HGLG11, MXRF11, KNRI11). O endpoint /stocks/{ticker}/quote aceita esses códigos e retorna o mesmo payload de OHLC e volume. Para indicadores específicos de FIIs como P/VP e dividend yield mensal, existe também o endpoint dedicado /fiis/{ticker}.

Posso usar a API sem autenticação?

O playground embutido na página inicial permite consultas limitadas sem API key para demonstração. Todas as chamadas feitas em scripts de produção exigem o header X-API-Key, que vincula a requisição ao plano da conta e aplica o rate limit correto. A chave é gerada gratuitamente no dashboard após login via Google.

Qual a frequência de atualização da cotação?

O pipeline ETL processa o arquivo COTAHIST da B3 uma vez por dia útil, logo após o encerramento do pregão regular. A cotação de fechamento fica disponível na API a partir das 20h30 de Brasília. Respostas individuais ficam em cache por cinco minutos no Redis, o que torna consultas repetidas ao mesmo ticker praticamente instantâneas e reduz consumo do rate limit.

Comece agora

Consultar cotação ação python api deixou de ser um problema de scraping frágil ou sufixos internacionais. Uma chamada autenticada a /stocks/{ticker}/quote devolve o mesmo conjunto de OHLC e volume que aparece nas plataformas visuais, com contrato JSON estável e rate limit documentado. Os exemplos deste tutorial, da consulta simples ao paralelismo com async, passando por retry com backoff e cache local, cobrem 90% dos casos de uso de quem automatiza análise de carteira, alertas de preço ou backtests diários. O plano gratuito com 200 requisições por dia valida qualquer ideia antes de um eventual upgrade.

Leia também

Este conteúdo é educativo e não constitui recomendação de investimento. Performance passada não garante resultados futuros.