-
-
Save danperrout/b27197056fa38d0d669332647ab89d7a to your computer and use it in GitHub Desktop.
| /* | |
| * @return Acesse radaropcoes.com Retorna a cotação atual de um título específico do Tesouro Direto. | |
| * API: https://radaropcoes.com/ | |
| * Fonte: https://www.tesourodireto.com.br/titulos/precos-e-taxas.htm | |
| **/ | |
| function TESOURODIRETO(bondName, argumento="r") { | |
| let srcURL = "https://api.radaropcoes.com/bonds.json"; | |
| let jsondata = UrlFetchApp.fetch(srcURL); | |
| let parsedData = JSON.parse(jsondata.getContentText()).response; | |
| for(let bond of parsedData.TrsrBdTradgList) { | |
| let currBondName = bond.TrsrBd.nm; | |
| if (currBondName.toLowerCase() === bondName.toLowerCase()) | |
| if(argumento == "r") | |
| return bond.TrsrBd.untrRedVal; | |
| else | |
| return bond.TrsrBd.untrInvstmtVal; | |
| } | |
| throw new Error("Título não encontrado."); | |
| } |
Alegria de pobre dura pouco, o link não atualiza…
Colocaram o Cloudflare na frente ou já tinha mas ativaram para que bots não possam acessar, exigindo aquela verificação de que é humano. :(
o 403 é do Cloudflare, tanto que funciona no navegador mas com scripts falha sempre.
Como eles não fornecem a chave de API para pessoas físicas, talvez a solução é baixar manualmente o arquivo quando se quiser ver a atualização. Alguma outra ideia?
Alegria de pobre dura pouco, o link não atualiza…
aqui ainda funciona (com o selenium)
Coloquei o caminho "https://raw.githubusercontent.com/ghostnetrn/bot-tesouro-direto/714896cbcfc4100e241006cd29fef1bc3fcbcc65/tesouro.json" no script do Google Sheets e funcionou...
Eu coloquei esse link e funcionou no Google Sheets, mas o preço do título veio diferente.
O problema é que esse link é de um JSON estático. Se não me engano dia dia 05/09/2024.
Então ele sempre irá repetir os valores desse dia.
Alguém saberia alguma forma de pegar o JSON para o dia atual?
Pessoal, implementei minha própria abstração do json do Tesouro Direto. Na verdade, é uma cópia exata do JSON fornecidos por eles que pode ser acessado em:
Oficial: https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json
Mirror: https://api.radaropcoes.com/bonds.json
Esse json é atualizado de 15 em 15 minutos, mas o próprio TD só atualiza de hora em hora, por isso as datas podem estar "atrasadas" em algumas horas.
Fiquem a vontade para utilizar, além disso, no site Radar Opções, incluí a possibilidade de COPIAR o nome do Título para a área de transferência:
Alguém saberia alguma forma de pegar o JSON para o dia atual?
Código atualizado com o novo endereço de json: https://api.radaropcoes.com/bonds.json
Alguém sabe explicar como resolver o seguinte erro:
Erro TypeError: Cannot read properties of undefined (reading 'toLowerCase')
TESOURODIRETO @ TESOURODIRETO.gs:12
O erro é por conta do comentário que declara a função, segue a versão corrigida:
/**
* Retorna a cotação atual de um título específico do Tesouro Direto.
*
* @param {string} nome do título
* @return Retorna a cotação atual de um título específico do Tesouro Direto.
* @customfunction
* Fonte: https://www.tesourodireto.com.br/titulos/precos-e-taxas.htm
*/
function TESOURODIRETO(bondName="Tesouro IPCA+ 2029", argumento="r") {
let srcURL = "https://api.radaropcoes.com/bonds.json";
let jsondata = UrlFetchApp.fetch(srcURL);
let parsedData = JSON.parse(jsondata.getContentText()).response;
for(let bond of parsedData.TrsrBdTradgList) {
let currBondName = bond.TrsrBd.nm;
console.log(typeof(currBondName))
if (currBondName.toLowerCase() === bondName.toLowerCase())
if(argumento == "r")
return bond.TrsrBd.untrRedVal;
else
return bond.TrsrBd.untrInvstmtVal;
}
throw new Error("Título não encontrado.");
}
Perfeito Artur! Valeu!
Os valores unitário de cada título está certo para vcs? o meu não mostra no google sheets (c/script) igual a do site do Tesouro.
Os valores unitário de cada título está certo para vcs? o meu não mostra no google sheets (c/script) igual a do site do Tesouro.
O script consegue acessar dois valores, se vc colocar:
=TESOURODIRETO("Nome do Titulo") retorna os valores da aba Resgate
=TESOURODIRETO("Nome do Titulo";"i") retorna os valores da aba Investimento
Os valores unitário de cada título está certo para vcs? o meu não mostra no google sheets (c/script) igual a do site do Tesouro.
O script consegue acessar dois valores, se vc colocar:
=TESOURODIRETO("Nome do Titulo")retorna os valores da aba Resgate=TESOURODIRETO("Nome do Titulo";"i")retorna os valores da aba Investimento
Putz, valeu irmão. Aprendi contigo no seu canal a colocar os dados no script e agora mais essa, ajudou muito. Tudo de bom para vc.
pessoal,
aparentemente o endpoint oficial (https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json) voltou a funcionar para as chamadas a partir do google app script.
[]'s
Aqui está a função do IPCA para quem quisar utilizar.
function IPCA(strBegin, strEnd){ let srcURL = "https://servicodados.ibge.gov.br/api/v1/conjunturais?&d=s&user=ibge&t=1737&v=2266&p="+ strBegin + "," + strEnd+"&ng=1(1)&c="; let jsondata = UrlFetchApp.fetch(srcURL); let parsedData = JSON.parse(jsondata) let dDiv = 1; let dDiv2 = 1; for (let tag of parsedData){ if (tag["p_cod"] == strBegin) { dDiv = tag["v"]; }else dDiv2 = tag["v"]; } return (dDiv2/dDiv) - 1; }
Pra quem está precisando de uma função no backend, php laravel,
use Illuminate\Support\Facades\Http; ////<<<<<<<< declare no seu Helper
public static function IPCA($inicio='202501',$fim='202502'){
try {
$params = [
'd' => 's',
'user' => 'ibge',
't' => '1737',
'v' => '2266',
'p' => Functions::somenteNumeros($inicio).','.Functions::somenteNumeros($fim),
'ng' => '1(1)',
'c' => ''
];
$response = Http::timeout(120)
->withoutVerifying()
->withHeaders(['Accept' => 'application/json'])
->get('https://servicodados.ibge.gov.br/api/v1/conjunturais', $params);
$dDiv = 1; $dDiv2 = 1; $data = $response->json();
foreach ($data as $tag) {
if ($tag['p_cod'] == $inicio) {
$dDiv = floatval($tag['v']);
} else {
$dDiv2 = floatval($tag['v']);
}
}
$ret = ($dDiv2/$dDiv) - 1;
return response()->json(['ipca' => $ret, 'ipca-data'=>$response->json() ?? ($response->json()['message']) ]);
} catch (\Exception $e){
return response()->json(['error'=>[$e->getMessage(), $e->getFile(), $e->getLine()]]);
}
}
public static function somenteNumeros($string){
return preg_replace("/[^0-9]/", "", $string);
}
Aqui está a função do IPCA para quem quisar utilizar.
function IPCA(strBegin, strEnd){ let srcURL = "https://servicodados.ibge.gov.br/api/v1/conjunturais?&d=s&user=ibge&t=1737&v=2266&p="+ strBegin + "," + strEnd+"&ng=1(1)&c="; let jsondata = UrlFetchApp.fetch(srcURL); let parsedData = JSON.parse(jsondata) let dDiv = 1; let dDiv2 = 1; for (let tag of parsedData){ if (tag["p_cod"] == strBegin) { dDiv = tag["v"]; }else dDiv2 = tag["v"]; } return (dDiv2/dDiv) - 1; }Pra quem está precisando de uma função no backend, php laravel,
use Illuminate\Support\Facades\Http; ////<<<<<<<< declare no seu Helper public static function IPCA($inicio='202501',$fim='202502'){ try { $params = [ 'd' => 's', 'user' => 'ibge', 't' => '1737', 'v' => '2266', 'p' => Functions::somenteNumeros($inicio).','.Functions::somenteNumeros($fim), 'ng' => '1(1)', 'c' => '' ]; $response = Http::timeout(120) ->withoutVerifying() ->withHeaders(['Accept' => 'application/json']) ->get('https://servicodados.ibge.gov.br/api/v1/conjunturais', $params); $dDiv = 1; $dDiv2 = 1; $data = $response->json(); foreach ($data as $tag) { if ($tag['p_cod'] == $inicio) { $dDiv = floatval($tag['v']); } else { $dDiv2 = floatval($tag['v']); } } $ret = ($dDiv2/$dDiv) - 1; return response()->json(['ipca' => $ret, 'ipca-data'=>$response->json() ?? ($response->json()['message']) ]); } catch (\Exception $e){ return response()->json(['error'=>[$e->getMessage(), $e->getFile(), $e->getLine()]]); } } public static function somenteNumeros($string){ return preg_replace("/[^0-9]/", "", $string); }
Legal a idéia.
Qual seria o endpoint para resgatar informações mais básicas do IPCA.
Por exemplo, esses três dessa página do IBGE: https://www.ibge.gov.br/explica/inflacao.php
IPCA do último mês, IPCA acumulado de 12 meses, e INPC do último mês.
Teria como ter uma versão modificada?
Grato,
podes usar o Curl pra isso
function obterConteudoElementoJson($url, $elementSelector) {
// Inicializa o cURL
$curl = curl_init();
// Configura as opções do cURL
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // Desativa a verificação SSL (não recomendado em produção)
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // Desativa a verificação do host SSL (não recomendado em produção)
// Executa a requisição cURL
$html = curl_exec($curl);
// Verifica se houve erro
if ($html === false) {
echo 'Erro cURL: ' . curl_error($curl);
curl_close($curl);
return false;
}
// Fecha a conexão cURL
curl_close($curl);
// Cria um objeto DOMDocument para analisar o HTML
$dom = new DOMDocument();
@$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html); // Supressão de erros de parsing (ex: tags não fechadas)
// Cria um objeto DOMXPath para executar consultas XPath
$xpath = new DOMXPath($dom);
// Executa a consulta XPath para encontrar o elemento desejado
$elementList = $xpath->query('//*[contains(@id, "' . str_replace('#', '', $elementSelector) . '")]');
if ($elementList->length > 0) {
$element = $elementList->item(0);
// Extrai os dados relevantes do elemento
$data = [];
$listItems = $xpath->query('./li', $element); // Seleciona todos os <li> dentro do elemento
foreach ($listItems as $listItem) {
$titulo = $xpath->query('./h3[@class="variavel-titulo"]/text()', $listItem)->item(0)->textContent;
$dado = $xpath->query('./p[@class="variavel-dado"]/text()', $listItem)->item(0)->textContent;
$periodo = $xpath->query('./p[@class="variavel-periodo"]/text()', $listItem)->item(0)->textContent;
$data[] = [
'titulo' => trim($titulo),
'dado' => trim($dado),
'periodo' => trim($periodo),
];
}
// Converte o array para JSON
return json_encode($data, JSON_UNESCAPED_UNICODE);
} else {
return false; // Elemento não encontrado
}
}
// Exemplo de uso:
$url = 'https://www.ibge.gov.br/explica/inflacao.php';
$elementSelector = '#dadoBrasil';
$json = obterConteudoElementoJson($url, $elementSelector);
if ($json !== false) {
header('Content-Type: application/json'); // Define o cabeçalho para JSON
echo $json;
} else {
echo "Não foi possível obter o conteúdo do elemento '$elementSelector' do site '$url'.";
}
Aqui está a função do IPCA para quem quisar utilizar.
function IPCA(strBegin, strEnd){ let srcURL = "https://servicodados.ibge.gov.br/api/v1/conjunturais?&d=s&user=ibge&t=1737&v=2266&p="+ strBegin + "," + strEnd+"&ng=1(1)&c="; let jsondata = UrlFetchApp.fetch(srcURL); let parsedData = JSON.parse(jsondata) let dDiv = 1; let dDiv2 = 1; for (let tag of parsedData){ if (tag["p_cod"] == strBegin) { dDiv = tag["v"]; }else dDiv2 = tag["v"]; } return (dDiv2/dDiv) - 1; }Pra quem está precisando de uma função no backend, php laravel,
use Illuminate\Support\Facades\Http; ////<<<<<<<< declare no seu Helper public static function IPCA($inicio='202501',$fim='202502'){ try { $params = [ 'd' => 's', 'user' => 'ibge', 't' => '1737', 'v' => '2266', 'p' => Functions::somenteNumeros($inicio).','.Functions::somenteNumeros($fim), 'ng' => '1(1)', 'c' => '' ]; $response = Http::timeout(120) ->withoutVerifying() ->withHeaders(['Accept' => 'application/json']) ->get('https://servicodados.ibge.gov.br/api/v1/conjunturais', $params); $dDiv = 1; $dDiv2 = 1; $data = $response->json(); foreach ($data as $tag) { if ($tag['p_cod'] == $inicio) { $dDiv = floatval($tag['v']); } else { $dDiv2 = floatval($tag['v']); } } $ret = ($dDiv2/$dDiv) - 1; return response()->json(['ipca' => $ret, 'ipca-data'=>$response->json() ?? ($response->json()['message']) ]); } catch (\Exception $e){ return response()->json(['error'=>[$e->getMessage(), $e->getFile(), $e->getLine()]]); } } public static function somenteNumeros($string){ return preg_replace("/[^0-9]/", "", $string); }Legal a idéia. Qual seria o endpoint para resgatar informações mais básicas do IPCA. Por exemplo, esses três dessa página do IBGE: https://www.ibge.gov.br/explica/inflacao.php IPCA do último mês, IPCA acumulado de 12 meses, e INPC do último mês.
Teria como ter uma versão modificada?
Grato,
Para o IPCA, utilizo essa função do Apps Script que puxa o IPCA acumulado em um período, então para puxar do mês atual e dos últimos 12 meses é só selecionar o período correto ao enviar:
/**
Calcula o IPCA acumulado entre duas datas usando a API do Banco Central.
@param {string} startDate - Data inicial no formato "dd/mm/yyyy"
@param {string} endDate - Data final no formato "dd/mm/yyyy"
@return {number} IPCA acumulado no período (ex.: 0.035 = 3,5%)
@customfunction
*/
function getIPCA(startDate, endDate) {
const fStartDate = startDate instanceof Date ? startDate.toLocaleDateString("pt-BR") : startDate;
const fEndDate = endDate instanceof Date ? endDate.toLocaleDateString("pt-BR") : endDate;
const url = https://api.bcb.gov.br/dados/serie/bcdata.sgs.433/dados?formato=json&dataInicial=${fStartDate}&dataFinal=${fEndDate};
const response = UrlFetchApp.fetch(url);
const data = JSON.parse(response.getContentText());
if (data.length === 0) {
throw new Error("Nenhum dado encontrado para o período informado.");
}
// Calcular o IPCA acumulado com juros compostos
let fator = 1;
data.forEach(entry => {
const valor = parseFloat(entry.valor.replace(",", "."));
fator *= 1 + (valor / 100);
});
const acumulado = fator - 1;
return acumulado;
}
Opa pessoal, somente comigo ou o endpoint do TesouroDireto está indisponível novamente (error 404)?
Desde quinta-feira ou sexta-feira...
https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json
Grato.
Olá pessoal, o endpoint do TD está fora novamente?
Só recebo error 404 desde sexta-feira 15/08/2025.
https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json
Mais alguém?
Grato.
Comigo também está fora do ar!
Será que eles descontinuaram esse acesso?
Pra mim tbm está fora do ar
Achei, mas tive que mudar "um pouco" o código.
`
/**
- =TESOURODIRETO("Tesouro IPCA+ 2029"; "ti")
- argumentos suportados:
-
- "ti" -> taxa para investir (ex.: "7,02")
-
- "tr" -> taxa para resgatar
-
- "min" -> aporte mínimo (número em R$)
-
- "venc"-> data de vencimento (string ISO)
-
- "tipo"-> U/S/M (sem juros, com juros, mensal - Renda+/Educa+)
-
- "raw" -> objeto completo do título (JSON)
- retrocompat:
-
- "i" -> retorna aporte mínimo (PU não está no novo endpoint)
-
- "r" -> retorna aporte mínimo (PU não está no novo endpoint)
- Precisa buscar em https://www.tesourodireto.com.br/o/rentabilidade/resgatar
*/
function TESOURODIRETO(bondName, argumento) {
bondName = (bondName || "Tesouro IPCA+ 2029").toString().trim();
argumento = (argumento || "ti").toString().trim().toLowerCase();
var url = "https://www.tesourodireto.com.br/o/rentabilidade/investir";
var resp = UrlFetchApp.fetch(url, {
muteHttpExceptions: true,
followRedirects: true,
headers: {
"Accept": "application/json, text/plain, /",
"User-Agent": "Mozilla/5.0 (compatible; AppsScriptFetcher/1.0)",
"Referer": "https://www.tesourodireto.com.br/produtos/dados-sobre-titulos/rendimento-dos-titulos"
}
});
var code = resp.getResponseCode();
if (code !== 200) {
throw new Error("HTTP " + code + " ao acessar o endpoint. Corpo: " + resp.getContentText().slice(0, 300));
}
var text = resp.getContentText("UTF-8");
var data;
try {
data = JSON.parse(text);
} catch (e) {
throw new Error("Falha ao parsear JSON do endpoint: " + e + " | trecho: " + text.slice(0, 300));
}
if (!Array.isArray(data)) {
throw new Error("Formato inesperado: esperado array na raiz.");
}
// Procura o título por nome (case-insensitive); tenta match exato e depois "contém"
var titulo = data.find(function (o) {
return (o.treasuryBondName || "").toLowerCase() === bondName.toLowerCase();
}) || data.find(function (o) {
return (o.treasuryBondName || "").toLowerCase().indexOf(bondName.toLowerCase()) >= 0;
});
if (!titulo) {
throw new Error("Título não encontrado: " + bondName);
}
function parsePercentFromIndexer(str) {
if (!str) return null;
var m = str.match(/([+-]?\d+(?:[.,]\d+)?)\s*%/);
if (!m) return null;
return Number(m[1].replace(",", "."));
}
switch (argumento) {
case "ti":
return parsePercentFromIndexer(titulo.investmentProfitabilityIndexerName);
case "tr":
return parsePercentFromIndexer(titulo.redemptionProfitabilityFeeIndexerName);
case "ti_str":
return titulo.investmentProfitabilityIndexerName || "";
case "tr_str":
return titulo.redemptionProfitabilityFeeIndexerName || "";
case "min":
return Number(titulo.investmentBondMinimumValue);
case "venc":
return titulo.maturityDate || "";
case "tipo":
return titulo.typeReceiptInterest || "";
case "raw":
return JSON.stringify(titulo);
case "i":
case "r":
return (Number(titulo.investmentBondMinimumValue))*100;
default:
return titulo.investmentProfitabilityIndexerName || "";
}
}
`
boa tarde, na verdade preciso pegar o PU e não a taxa, alguém conseguiu depois dessa mudança?
Agora, o PU do resgate só é possível pelo download do CSV no link:
https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv
A boa notícia é que, pelo Google Sheets é possível obter o CSV diretamente na planilha sem necessidade de fazer o download.
Basta colocar a seguinte fórmula na primeira célula da planilha:
=IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv?download=true")
Nesse caso, a planilha irá atualizar toda vez que for reaberta, ou, para forçar a atualização é só fazer uma pequena alteração na fórmula, da um enter, voltar o valor correto e dar outro enter.
Para o PU de aplicação, é só usar o link do CSV de investir:
=IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-investir-csv?download=true")
A fórmula IMPORTDATA não faz a divisão de ponto e vírgula, mas aí é só usar a fórmula SPLIT em uma coluna à frente, referenciando as colunas de interesse:
=ARRAYFORMULA(SPLIT(C2:C70; ";"))
Achei, mas tive que mudar "um pouco" o código.
` /**
- =TESOURODIRETO("Tesouro IPCA+ 2029"; "ti")
- argumentos suportados:
- "ti" -> taxa para investir (ex.: "7,02")
- "tr" -> taxa para resgatar
- "min" -> aporte mínimo (número em R$)
- "venc"-> data de vencimento (string ISO)
- "tipo"-> U/S/M (sem juros, com juros, mensal - Renda+/Educa+)
- "raw" -> objeto completo do título (JSON)
- retrocompat:
- "i" -> retorna aporte mínimo (PU não está no novo endpoint)
- "r" -> retorna aporte mínimo (PU não está no novo endpoint)
- Precisa buscar em https://www.tesourodireto.com.br/o/rentabilidade/resgatar
*/
function TESOURODIRETO(bondName, argumento) {
bondName = (bondName || "Tesouro IPCA+ 2029").toString().trim();
argumento = (argumento || "ti").toString().trim().toLowerCase();var url = "https://www.tesourodireto.com.br/o/rentabilidade/investir";
var resp = UrlFetchApp.fetch(url, { muteHttpExceptions: true, followRedirects: true, headers: { "Accept": "application/json, text/plain, /", "User-Agent": "Mozilla/5.0 (compatible; AppsScriptFetcher/1.0)", "Referer": "https://www.tesourodireto.com.br/produtos/dados-sobre-titulos/rendimento-dos-titulos" } });
var code = resp.getResponseCode(); if (code !== 200) { throw new Error("HTTP " + code + " ao acessar o endpoint. Corpo: " + resp.getContentText().slice(0, 300)); }
var text = resp.getContentText("UTF-8"); var data; try { data = JSON.parse(text); } catch (e) { throw new Error("Falha ao parsear JSON do endpoint: " + e + " | trecho: " + text.slice(0, 300)); }
if (!Array.isArray(data)) { throw new Error("Formato inesperado: esperado array na raiz."); }
// Procura o título por nome (case-insensitive); tenta match exato e depois "contém" var titulo = data.find(function (o) { return (o.treasuryBondName || "").toLowerCase() === bondName.toLowerCase(); }) || data.find(function (o) { return (o.treasuryBondName || "").toLowerCase().indexOf(bondName.toLowerCase()) >= 0; });
if (!titulo) { throw new Error("Título não encontrado: " + bondName); }
function parsePercentFromIndexer(str) { if (!str) return null; var m = str.match(/([+-]?\d+(?:[.,]\d+)?)\s*%/); if (!m) return null; return Number(m[1].replace(",", ".")); }
switch (argumento) { case "ti": return parsePercentFromIndexer(titulo.investmentProfitabilityIndexerName); case "tr": return parsePercentFromIndexer(titulo.redemptionProfitabilityFeeIndexerName); case "ti_str": return titulo.investmentProfitabilityIndexerName || ""; case "tr_str": return titulo.redemptionProfitabilityFeeIndexerName || ""; case "min": return Number(titulo.investmentBondMinimumValue); case "venc": return titulo.maturityDate || ""; case "tipo": return titulo.typeReceiptInterest || ""; case "raw": return JSON.stringify(titulo); case "i": case "r": return (Number(titulo.investmentBondMinimumValue))*100; default: return titulo.investmentProfitabilityIndexerName || ""; } }
`
Obrigado por postar essa solução!
Porém, tentei aqui mas só recebo Error 406, vide abaixo.
"
Error
Error: HTTP 406 ao acessar o endpoint. Corpo:
"
Está funcionando normalmente para você?
Grato.
Agora, o PU do resgate só é possível pelo download do CSV no link:
https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv
A boa notícia é que, pelo Google Sheets é possível obter o CSV diretamente na planilha sem necessidade de fazer o download. Basta colocar a seguinte fórmula na primeira célula da planilha:
=IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv?download=true")
Nesse caso, a planilha irá atualizar toda vez que for reaberta, ou, para forçar a atualização é só fazer uma pequena alteração na fórmula, da um enter, voltar o valor correto e dar outro enter.
Para o PU de aplicação, é só usar o link do CSV de investir: =IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-investir-csv?download=true")
A fórmula IMPORTDATA não faz a divisão de ponto e vírgula, mas aí é só usar a fórmula SPLIT em uma coluna à frente, referenciando as colunas de interesse: =ARRAYFORMULA(SPLIT(C2:C70; ";"))
Obrigado pela dica.
Existe uma solução similar a esse (i.e., utilizando formulas GoogleSheets) para resgatar o "ti" (taxa para investir)?
Grato.
Agora, o PU do resgate só é possível pelo download do CSV no link:
https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv
A boa notícia é que, pelo Google Sheets é possível obter o CSV diretamente na planilha sem necessidade de fazer o download. Basta colocar a seguinte fórmula na primeira célula da planilha:
=IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv?download=true")
Nesse caso, a planilha irá atualizar toda vez que for reaberta, ou, para forçar a atualização é só fazer uma pequena alteração na fórmula, da um enter, voltar o valor correto e dar outro enter.
Para o PU de aplicação, é só usar o link do CSV de investir: =IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-investir-csv?download=true")
A fórmula IMPORTDATA não faz a divisão de ponto e vírgula, mas aí é só usar a fórmula SPLIT em uma coluna à frente, referenciando as colunas de interesse: =ARRAYFORMULA(SPLIT(C2:C70; ";"))Obrigado pela dica. Existe uma solução similar a esse (i.e., utilizando formulas GoogleSheets) para resgatar o "ti" (taxa para investir)?
Grato.
O CSV trás essa informação. Você precisa tratar os dados por causa da formatação de vírgula e ponto e vírgula. Mas, está tudo lá.
É possível usar também uma automação no N8N para já entregar os dados tratados, mas ainda não tive tempo pra fazer isso.
=IMPORTDATA("https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv?download=true")
Hmmm, eu testei as duas chamadas (rendimento-resgatar-csv e rendimento-investir-csv), mas em nenhuma das duas consegui localizar o 'ti'.
Por exemplo, para IPCA+ 2029 essas são as duas linhas:
Resgatar:
Tesouro IPCA+ 2029;IPCA + 7 | 82%;R$ 3.434 | 69;15/05/2029
Investir:
Tesouro IPCA+ 2029;IPCA + 7 | 70%;R$ 34 | 48;R$ 3.448 | 90;15/05/2029
Talvez eu esteja sendo leigo, mas não consegui visualizar o 'ti'.
Obrigado.
Adaptei o script do App Script para Google Planilha e está funcionando para meu objetivo de pegar o valor de resgate.
/**
- Retorna informações de um título do Tesouro Direto a partir do CSV oficial de resgate.
- @param {string} bondName – Nome do título, ex.: "Tesouro IPCA+ com Juros Semestrais 2035"
- @param {string} argumento – "Taxa de Investimento" → rendimento anual do título
-
"Valor Unitario de Recompra" → preço unitário de resgate -
"Vencimento" → data de vencimento - @return {number|string} – Retorna o valor correspondente ao argumento solicitado
*/
function TESOURODIRETO(bondName, argumento = 'Taxa de Investimento') {
// Acessa o cache do script para evitar múltiplas requisições desnecessárias
const cache = CacheService.getScriptCache();
const cacheKey =${bondName}_${argumento}; // Chave única para cache
// Tenta obter o resultado do cache
let cachedResult = cache.get(cacheKey);
if (cachedResult) return JSON.parse(cachedResult); // Se existe no cache, retorna imediatamente
// URL do CSV oficial de resgate do Tesouro Direto
const url = 'https://www.tesourodireto.com.br/documents/d/guest/rendimento-resgatar-csv?download=true';
// Faz a requisição e obtém o conteúdo do CSV como texto
const csvText = UrlFetchApp.fetch(url).getContentText('UTF-8');
// Separa o CSV em linhas, removendo espaços extras e linhas vazias
const linhas = csvText.split(/\r?\n/).map(l => l.trim()).filter(l => l);
// Extrai o cabeçalho (primeira linha) e cria um índice para localizar colunas pelo nome
const headers = linhas[0].split(';').map(h => h.trim());
const idx = {};
headers.forEach((h, i) => idx[h] = i);
// Procura a linha que corresponde ao título solicitado
// Faz uma busca aproximada (case-insensitive) para evitar erros de grafia
const linhaTitulo = linhas.slice(1).find(l => {
const t = l.split(';')[idx['Título']].trim().toLowerCase();
return t.includes(bondName.trim().toLowerCase());
});
// Lança erro caso o título não seja encontrado
if (!linhaTitulo) throw new Error('Título não encontrado no CSV: ' + bondName);
// Divide a linha do título em colunas individuais
const colunas = linhaTitulo.split(';');
let resultado;
// Retorna o valor conforme o argumento solicitado
switch (argumento) {
case 'Taxa de Investimento':
// Retorna o rendimento anual do título (string)
resultado = colunas[idx['Rendimento anual do título']].trim();
break;
case 'Valor Unitario de Recompra':
// Retira 'R$', pontos e converte vírgula para ponto antes de transformar em float
resultado = parseFloat(
colunas[idx['Preço unitário de resgate']]
.replace('R$', '')
.replace(/./g, '')
.replace(',', '.')
.trim()
);
break;
case 'Vencimento':
// Retorna a data de vencimento do título
resultado = colunas[idx['Vencimento do Título']].trim();
break;
default:
throw new Error('Argumento inválido para CSV de resgate.');
}
// Salva o resultado no cache por 1 hora (3600 segundos)
cache.put(cacheKey, JSON.stringify(resultado), 3600);
return resultado;
}
// --- Função de teste ---
function teste() {
// Exemplo de retorno da taxa anual do título
const taxa = TESOURODIRETO('Tesouro IPCA+ com Juros Semestrais 2035', 'Taxa de Investimento');
Logger.log('Taxa de Investimento: ' + taxa);
// Exemplo de valor unitário de recompra
const valorRecompra = TESOURODIRETO('Tesouro IPCA+ com Juros Semestrais 2035', 'Valor Unitario de Recompra');
Logger.log('Valor Unitario de Recompra: ' + valorRecompra);
// Exemplo de data de vencimento
const vencimento = TESOURODIRETO('Tesouro IPCA+ com Juros Semestrais 2035', 'Vencimento');
Logger.log('Vencimento: ' + vencimento);
// Exemplo de argumento inválido (mostra que o erro é tratado)
try {
const venc = TESOURODIRETO('Tesouro IPCA+ com Juros Semestrais 2035', 'Vencimentooooooooooo');
Logger.log('Vencimento: ' + venc);
} catch (e) {
Logger.log('Erro esperado: ' + e.message);
}
}

Será que não valeria a pena entrar em contato com o criador do https://github.com/ghostnetrn/bot-tesouro-direto e perguntar como que ele ainda tá conseguindo utilizar a API do TesouroDireto (URL_API=https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json)??