Skip to content

Instantly share code, notes, and snippets.

@danperrout
Last active September 19, 2025 20:49
Show Gist options
  • Save danperrout/b27197056fa38d0d669332647ab89d7a to your computer and use it in GitHub Desktop.
Save danperrout/b27197056fa38d0d669332647ab89d7a to your computer and use it in GitHub Desktop.
API Função TESOURODIRETO Google Sheets
/*
* @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.");
}
@epeixoto85
Copy link

Para atualização dos títulos existentes do Tesouro Direto, e dos preços e taxas de venda e compra, utilizava os seguintes serviços:

"https://www.tesourodireto.com.br/json/br/com/b3/tesourodireto/service/api/treasurybondsinfo.json"
"https://www.tesourodireto.com.br/b3/tesourodireto/pricesAndFeesHistory?codigo=&periodo="

Ambos estão retornando erro 403, conforme algumas pessoas relataram anteriormente.

Alguém encontrou alguma alternativa?

@wroliveira1
Copy link

Aqui esta fora tambem. Po galera, aquele csv do tesouro transparente nao é confiavel?

É uma merda usar um arquivo de 11mb cheio de lixo, mas achei que fosse uma opcao viavel. Vi que eles atualizaram ele hoje, mas abrindo o csv nao achei nenhum registro com data base 02/09/2024

Eu literalmente implementei um codigo ontem que filtra ele e pega os valores atualizados dos titulos, alguem sabe se é comum esse csv nao ser atualizado direito?

atualizando uma vez por dia pra mim já está bom.
fuçando achei este, não sei se serve: https://ghostnetrn.github.io/bot-tesouro-direto/

@tuliopascoal
Copy link

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)??

@brvfr1
Copy link

brvfr1 commented Sep 6, 2024

Alegria de pobre dura pouco, o link não atualiza…

@alexbenfica
Copy link

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?

@BDonadelli
Copy link

BDonadelli commented Sep 6, 2024

Alegria de pobre dura pouco, o link não atualiza…

aqui ainda funciona (com o selenium)

@tuliopascoal
Copy link

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?

@danperrout
Copy link
Author

danperrout commented Oct 18, 2024

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:

image

@danperrout
Copy link
Author

danperrout commented Oct 21, 2024

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

@WadjoResende
Copy link

Alguém sabe explicar como resolver o seguinte erro:

Erro TypeError: Cannot read properties of undefined (reading 'toLowerCase')
TESOURODIRETO @ TESOURODIRETO.gs:12

@ArturJuan
Copy link

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.");
}

@WadjoResende
Copy link

Perfeito Artur! Valeu!

@kleitonramires
Copy link

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.

@danperrout
Copy link
Author

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

@kleitonramires
Copy link

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.

@diegob-ciandt
Copy link

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

@edermcastro
Copy link

edermcastro commented Mar 20, 2025

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);
}

@tuliopascoal
Copy link

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,

@edermcastro
Copy link

edermcastro commented Mar 20, 2025

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'.";
}

@ArturJuan
Copy link

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;
}

@tuliopascoal
Copy link

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.

@tuliopascoal
Copy link

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.

@adelitofarias
Copy link

Comigo também está fora do ar!
Será que eles descontinuaram esse acesso?

@glauberramos
Copy link

Pra mim tbm está fora do ar

@fabiotirapelli
Copy link

fabiotirapelli commented Aug 18, 2025

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 || "";
}
}

`

@Nauahbanda
Copy link

boa tarde, na verdade preciso pegar o PU e não a taxa, alguém conseguiu depois dessa mudança?

@WadjoResende
Copy link

WadjoResende commented Aug 18, 2025

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; ";"))

@tuliopascoal
Copy link

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.

@tuliopascoal
Copy link

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.

@WadjoResende
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment