O Google Sheets é uma ferramenta poderosa para acompanhar seus investimentos de forma automática. Através do Google Apps Script, você pode criar funções personalizadas que buscam cotações diretamente da brapi.dev.
A função GOOGLEFINANCE não funciona para muitos ativos brasileiros, especialmente ETFs como IMAB11, WRLD11, IB5M11 e IRFM11. A integração com a brapi.dev resolve esse problema, oferecendo:
/**
* Busca preços de múltiplos tickers em UMA requisição (recomendado).
* Uso: =BRAPI_BATCH(A2:A20; $A$1)
* @param {Range} tickers - Range com os códigos dos ativos (coluna)
* @param {string} token - Token da API brapi.dev
* @return {number[][]} Coluna com o preço de cada ativo
* @customfunction
*/
function BRAPI_BATCH(tickers, token) {
if (!tickers) return [["Erro: informe tickers"]];
if (token && typeof token === 'object' && token.length) {
token = token[0][0];
}
if (!token) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
token = sh.getRange('A1').getDisplayValue();
}
if (!token) return [["Erro: token brapi.dev ausente"]];
// Monta a lista de tickers a partir do range
var tickerList = [];
for (var i = 0; i < tickers.length; i++) {
var t = String(tickers[i][0] || '').toUpperCase().trim();
if (t) tickerList.push(t);
}
if (tickerList.length === 0) return [["Sem tickers"]];
// Uma única requisição para todos os tickers (separados por vírgula)
var url = 'https://brapi.dev/api/quote/' +
encodeURIComponent(tickerList.join(',')) +
'?token=' + encodeURIComponent(token);
var options = {
muteHttpExceptions: true,
headers: { 'User-Agent': 'GoogleSheets-brapi/1.0' }
};
var response = UrlFetchApp.fetch(url, options);
if (response.getResponseCode() !== 200) {
return tickers.map(function() {
return ["HTTP " + response.getResponseCode()];
});
}
var json = JSON.parse(response.getContentText());
var results = (json && json.results) || [];
// Mapeia símbolo → preço para preservar a ordem do range
var priceMap = {};
for (var j = 0; j < results.length; j++) {
var sym = results[j].symbol;
var price = results[j].regularMarketPrice;
if (price == null) price = results[j].lastPrice;
if (price == null) price = results[j].close;
priceMap[sym] = price != null ? price : null;
}
// Retorna um array 2D na mesma ordem da coluna de entrada
return tickers.map(function(row) {
var key = String(row[0] || '').toUpperCase().trim();
if (!key) return [""];
var val = priceMap[key] != null ? priceMap[key] : priceMap[key + '.SA'];
return [val != null ? val : "Não encontrado"];
});
}
/**
* Busca o preço de UM ticker via brapi.dev (1 requisição por célula).
* Prefira BRAPI_BATCH para economizar requisições.
* Uso: =BRAPI_PRICE("PETR4"; $A$1)
* @param {string} ticker - Código do ativo (ex: "PETR4", "VALE3", "IMAB11")
* @param {string} token - Token da API brapi.dev
* @return {number} Preço atual do ativo
* @customfunction
*/
function BRAPI_PRICE(ticker, token) {
if (!ticker) return "Erro: informe ticker";
if (token && typeof token === 'object' && token.length) {
token = token[0][0];
}
if (!token) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
token = sh.getRange('A1').getDisplayValue();
}
if (!token) return "Erro: token brapi.dev ausente";
ticker = String(ticker).toUpperCase().trim();
var url = 'https://brapi.dev/api/quote/' + encodeURIComponent(ticker) +
'?token=' + encodeURIComponent(token);
var options = {
muteHttpExceptions: true,
headers: { 'User-Agent': 'GoogleSheets-brapi/1.0' }
};
var response = UrlFetchApp.fetch(url, options);
if (response.getResponseCode() !== 200) {
return "HTTP " + response.getResponseCode() + ": " +
response.getContentText().substring(0, 200);
}
var json = JSON.parse(response.getContentText());
var result = (json && json.results && json.results[0]) || null;
if (!result) return "Sem dados retornados";
var price = result.regularMarketPrice;
if (price == null) price = result.lastPrice;
if (price == null) price = result.close;
return price != null ? price : "Preço não encontrado";
}
/**
* Retorna dados completos de um ativo
* Uso: =BRAPI_DATA("PETR4"; $A$1; "symbol")
* @param {string} ticker - Código do ativo
* @param {string} token - Token da API
* @param {string} field - Campo desejado (regularMarketPrice, currency, shortName, etc)
* @return {any} Valor do campo solicitado
* @customfunction
*/
function BRAPI_DATA(ticker, token, field) {
if (!ticker) return "Erro: informe ticker";
if (!field) return "Erro: informe campo";
if (token && typeof token === 'object' && token.length) {
token = token[0][0];
}
if (!token) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
token = sh.getRange('A1').getDisplayValue();
}
if (!token) return "Erro: token ausente";
ticker = String(ticker).toUpperCase().trim();
var url = 'https://brapi.dev/api/quote/' + encodeURIComponent(ticker) +
'?token=' + encodeURIComponent(token);
var options = {
muteHttpExceptions: true,
headers: { 'User-Agent': 'GoogleSheets-brapi/1.0' }
};
var response = UrlFetchApp.fetch(url, options);
if (response.getResponseCode() !== 200) {
return "Erro HTTP " + response.getResponseCode();
}
var json = JSON.parse(response.getContentText());
var result = (json && json.results && json.results[0]) || null;
if (!result) return "Sem dados";
return result[field] || "Campo não encontrado";
}Liste os tickers na coluna A (a partir de A2) e use a fórmula em lote:
=BRAPI_BATCH(A2:A20; $A$1)Isso busca até 20 tickers em uma única requisição à API, economizando sua cota.
Para buscar um único ativo, use:
=BRAPI_PRICE("PETR4"; $A$1)A referência $A$1 garante que o endereço da célula do token não mude ao arrastar a fórmula.
=BRAPI_PRICE("PETR4"; $A$1) # Petrobras PN
=BRAPI_PRICE("VALE3"; $A$1) # Vale ON
=BRAPI_PRICE("ITUB4"; $A$1) # Itaú Unibanco PN
=BRAPI_PRICE("BBDC4"; $A$1) # Bradesco PN=BRAPI_PRICE("IMAB11"; $A$1) # ETF IMA-B
=BRAPI_PRICE("IB5M11"; $A$1) # ETF IMA-B 5+
=BRAPI_PRICE("IRFM11"; $A$1) # ETF IRF-M
=BRAPI_PRICE("B5P211"; $A$1) # ETF NTN-B Principal=BRAPI_PRICE("WRLD11"; $A$1) # ETF Global
=BRAPI_PRICE("IVVB11"; $A$1) # ETF S&P 500
=BRAPI_PRICE("NASD11"; $A$1) # ETF Nasdaq=BRAPI_PRICE("^BVSP"; $A$1) # Índice Ibovespa=BRAPI_DATA("PETR4"; $A$1; "regularMarketPrice") # Preço
=BRAPI_DATA("PETR4"; $A$1; "currency") # Moeda
=BRAPI_DATA("PETR4"; $A$1; "shortName") # Nome curto
=BRAPI_DATA("PETR4"; $A$1; "regularMarketChange") # Variação do dia
=BRAPI_DATA("PETR4"; $A$1; "marketCap") # Valor de mercadoO Google Apps Script impõe limites próprios além dos limites da brapi.dev:
| Recurso | Limite (contas gratuitas) |
|---|---|
UrlFetchApp.fetch por execução | 50 chamadas |
UrlFetchApp.fetch por dia | 20.000 chamadas |
| Tempo de execução por função | 30 segundos |
Por isso a função BRAPI_BATCH é essencial: uma planilha com 30 ativos usando BRAPI_PRICE faz 30 chamadas UrlFetchApp.fetch por atualização; com BRAPI_BATCH, faz apenas 1 chamada.
| Comportamento | O que acontece |
|---|---|
HTTP 429 | Você excedeu o rate limit do seu plano. Aguarde ou faça upgrade. |
HTTP 401 | Token inválido ou ausente. |
| Plano gratuito | 15.000 requisições/mês |
?token= ou headerO Google Apps Script suporta headers HTTP via UrlFetchApp, porém para funções personalizadas (@customfunction) a simplicidade do ?token= na URL é a abordagem mais prática. Para scripts que rodam no backend (triggers, menus), você pode usar o header:
// Em triggers ou menus (NÃO em @customfunction)
var options = {
muteHttpExceptions: true,
headers: {
'Authorization': 'Bearer ' + token,
'User-Agent': 'GoogleSheets-brapi/1.0'
}
};No backend e em produção, use Authorization: Bearer. Token na URL vaza para os logs do servidor e para o histórico do navegador.
BRAPI_BATCH. Uma requisição com vários tickers gasta o mesmo que uma com um só.Para uma ideia de consumo:
| Cenário | Requisições/mês |
|---|---|
30 ativos com BRAPI_BATCH, 3x/dia | ~90 (30 dias × 3 × 1 req) |
30 ativos com BRAPI_PRICE, 3x/dia | ~2.700 (30 dias × 3 × 30 req) |
50 ativos com BRAPI_BATCH, 3x/dia | ~270 (30 dias × 3 × 3 blocos) |
A função em lote reduz o consumo em ~97% para portfólios típicos.
Verifique se o token está na célula A1 ou se está sendo passado corretamente como parâmetro.
Token inválido. Verifique se copiou o token completo do painel da brapi.dev.
Limite de requisições excedido. Migre para BRAPI_BATCH para reduzir o consumo ou considere o plano pago.
Ticker inválido ou ativo não encontrado. Verifique o código do ativo.
A função está demorando mais de 30 segundos. Reduza o número de tickers por chamada ou verifique sua conexão.