Power Query: como conectar em uma API e na Web
Guia prático de Power Query conectar em uma API e na Web: Web.Contents, ler JSON, cabeçalhos, token, paginação e atualização no serviço com RelativePath.
Conectar o Power Query a uma API não é difícil, mas quase todo mundo tropeça na atualização no serviço
Aprender Power Query conectar em uma API e na Web parece simples no começo: você cola uma URL, o Power Query traz o JSON, você expande umas colunas e pronto, tem dados. O relatório funciona no Power BI Desktop, você publica, sorri e vai embora. Dois dias depois chega a mensagem de sempre: "o dataset não atualiza no serviço". É aqui que a maioria descobre que puxar dados de uma API na sua máquina e fazer isso funcionar de forma agendada na nuvem são dois problemas diferentes.
Neste artigo vou mostrar o caminho completo, do jeito que a gente faz em projeto real: o conector Web e a função Web.Contents, como o JSON vira tabela com Json.Document, onde colocar cabeçalhos e token de autenticação, como resolver paginação e, principalmente, os cuidados para o refresh não quebrar no Power BI Service. Vou usar exemplos de código M ao longo do texto. Se você quer o panorama maior de Power BI antes de mergulhar aqui, vale ler nosso guia completo de Power BI para empresas.
O conector Web e o Web.Contents fazem trabalhos diferentes
Quando você clica em Obter Dados e escolhe "Web", o Power BI gera para você uma chamada à função Web.Contents. O conector visual é só uma casca. Quem faz o trabalho pesado é a função M por baixo, e entender ela é o que separa quem monta uma prova de conceito de quem entrega uma solução que aguenta produção.
A forma mais crua funciona assim:
let
Fonte = Web.Contents("https://api.exemplo.com/v1/vendas"),
Json = Json.Document(Fonte)
in
Json
Web.Contents devolve o conteúdo bruto (binário) da resposta HTTP. Json.Document interpreta esse binário como JSON e devolve, dependendo do formato da resposta, um registro (record) ou uma lista (list). A partir daí você usa "Para Tabela" e vai expandindo colunas até chegar na estrutura que quer.
O problema dessa forma crua é que você concatenou tudo numa URL só. Guarde esse detalhe: ele volta a assombrar você na hora da atualização no serviço, e a solução vai ser separar essa URL em duas partes.
Como o JSON vira tabela: Json.Document, registros e listas
APIs REST quase sempre respondem em JSON, e o JSON tem só duas estruturas que importam para o Power Query: objeto e array. No M, o objeto vira um record (pares chave/valor) e o array vira uma list. Saber qual dos dois você recebeu decide o próximo clique.
A tabela abaixo resume o mapeamento que você vai usar o tempo todo:
| Estrutura JSON | Tipo no Power Query | Como transformar |
|---|---|---|
Objeto { } | Record | "Para Tabela" ou expandir os campos direto |
Array [ ] | List | "Para Tabela" e depois expandir os registros |
| Array de objetos | List de records | Table.FromRecords ou "Para Tabela" e expandir |
| Valor simples (texto, número) | Valor escalar | Usar direto, sem expandir |
Na prática, a maioria das APIs devolve um objeto no topo com um campo tipo data ou results que contém o array de verdade. O padrão fica assim:
let
Fonte = Web.Contents("https://api.exemplo.com/v1/vendas"),
Json = Json.Document(Fonte),
Registros = Json[data],
Tabela = Table.FromRecords(Registros)
in
Tabela
Aqui Json[data] acessa o campo data do registro raiz, que é a lista de vendas, e Table.FromRecords transforma essa lista de registros numa tabela de uma vez. Se a sua resposta já vier como um array puro no topo, você pula o Json[data] e joga a lista direto em Table.FromRecords.
Cabeçalhos e token de autenticação vão no argumento Headers
A maioria das APIs sérias exige autenticação, e a forma mais comum hoje é um token no cabeçalho Authorization. O Web.Contents aceita um segundo argumento, um registro de opções, e é dentro dele que mora o campo Headers.
let
Token = "SEU_TOKEN_AQUI",
Fonte = Web.Contents(
"https://api.exemplo.com/v1/vendas",
[
Headers = [
#"Authorization" = "Bearer " & Token,
#"Content-Type" = "application/json"
]
]
),
Json = Json.Document(Fonte)
in
Json
Repare em dois pontos. Primeiro, o nome do cabeçalho Authorization está entre #"..." porque em M todo identificador com caractere especial ou que possa colidir com palavra reservada precisa dessa forma. Segundo, o valor "Bearer " & Token segue o padrão do esquema Bearer: a palavra Bearer, um espaço, e o token.
Um alerta honesto de consultor: nunca deixe o token cravado no código M em produção. Escrever o token direto na query é aceitável para testar, mas em ambiente real ele deve virar um parâmetro da própria consulta, ou entrar pela tela de credenciais da fonte de dados, ou vir de um cofre de segredos numa arquitetura mais robusta. Token no meio do código é senha em texto claro, e alguém vai copiar essa query um dia.
A tabela abaixo mostra os tipos de autenticação que você mais encontra e onde cada um entra:
| Tipo de autenticação | Onde configurar | Observação |
|---|---|---|
| Token Bearer | Headers do Web.Contents | Padrão mais comum em APIs REST modernas |
| Chave de API (API Key) | Headers ou query string | Alguns pedem em cabeçalho próprio, tipo x-api-key |
| Basic (usuário e senha) | Tela de credenciais "Básica" da fonte | O Power BI codifica em Base64 para você |
| Anônima | Tela de credenciais "Anônima" | APIs públicas sem exigência de login |
| OAuth 2.0 | Fluxo próprio, às vezes com token renovável | O mais trabalhoso, costuma exigir passo de renovação |
Paginação: quase nenhuma API entrega tudo de uma vez
APIs raramente devolvem dez mil registros numa resposta só. Elas paginam: você recebe a página 1 com 100 itens, e para pegar o resto precisa pedir a página 2, a 3, e assim por diante. Ignorar isso é um erro clássico: seu relatório mostra 100 linhas, todo mundo confia, e só três semanas depois alguém percebe que faltam 90% dos dados.
Existem dois padrões principais de paginação. No primeiro, a API diz quantas páginas existem ou quantos registros há no total, e você calcula o número de páginas. No segundo, cada resposta traz um "cursor" ou um link para a próxima página, e você segue até esse link vir vazio. Para o segundo caso, List.Generate é a ferramenta certa, porque ela roda um laço enquanto uma condição for verdadeira.
Um esqueleto de paginação por número de página fica assim:
let
ObterPagina = (pagina as number) as list =>
let
Resposta = Web.Contents(
"https://api.exemplo.com/v1/vendas",
[
Query = [ page = Text.From(pagina), per_page = "100" ],
Headers = [ #"Authorization" = "Bearer " & Token ]
]
),
Json = Json.Document(Resposta)
in
Json[data],
Paginas = List.Generate(
() => [ p = 1, dados = ObterPagina(1) ],
each List.Count([dados]) > 0,
each [ p = [p] + 1, dados = ObterPagina([p] + 1) ],
each [dados]
),
Tudo = List.Combine(Paginas),
Tabela = Table.FromRecords(Tudo)
in
Tabela
O que esse código faz, em português: List.Generate começa na página 1, continua enquanto a página trouxer registros (List.Count([dados]) > 0), a cada volta soma 1 no número da página e busca a próxima, e no final devolve só a lista de dados de cada página. List.Combine junta todas as listas numa só, e Table.FromRecords fecha em tabela. Repare que passei page e per_page dentro de Query em vez de colar na URL. Isso não é só estética, e o próximo tópico explica por quê.
O parâmetro RelativePath é o que salva a atualização no serviço
Aqui está o ponto que decide se o seu relatório vai atualizar sozinho na nuvem ou vai virar chamado de suporte toda semana. O Power BI Service, quando vai agendar a atualização, faz uma verificação de segurança nas fontes de dados. Para isso ele precisa reconhecer a URL base da fonte de forma estável. Se a URL muda a cada linha, a cada página, a cada parâmetro, o serviço não consegue casar a credencial com a fonte, e o refresh falha com erros de acesso dinâmico à fonte de dados.
Quando você faz Web.Contents("https://api.exemplo.com/v1/vendas?page=" & Text.From(pagina)), a URL é montada de forma dinâmica dentro da chamada. Isso funciona no Desktop, onde você já autenticou na mão, mas costuma quebrar no serviço. A correção é separar a parte fixa da parte que varia usando RelativePath e Query:
let
Resposta = Web.Contents(
"https://api.exemplo.com",
[
RelativePath = "v1/vendas",
Query = [ page = Text.From(pagina), per_page = "100" ],
Headers = [ #"Authorization" = "Bearer " & Token ]
]
)
in
Json.Document(Resposta)
O primeiro argumento, "https://api.exemplo.com", é fixo e é o que o Power BI enxerga como a fonte de dados. RelativePath completa o caminho, e Query monta os parâmetros da URL de forma que o Power BI entende como conteúdo, não como uma fonte nova. Com essa estrutura, você consegue configurar a credencial uma vez no serviço e o refresh agendado passa a funcionar.
Guarde esta regra prática: URL base fixa no primeiro argumento, tudo que varia dentro de RelativePath e Query. Se você seguir só isso, evita a maior parte das dores de cabeça com atualização. Quando o dado vem de várias APIs, bancos e planilhas ao mesmo tempo, a coisa vira um projeto de engenharia de dados de verdade, com orquestração e monitoramento, e não só umas queries soltas no Power Query.
Passo a passo para colocar de pé uma conexão com API
Juntando tudo, o roteiro que a gente segue em projeto é mais ou menos este:
- Leia a documentação da API primeiro. Descubra a URL base, o método de autenticação, o formato de paginação e os limites de requisição (rate limit). Cinco minutos de leitura aqui poupam horas de tentativa e erro.
- Teste uma chamada simples no Desktop. Use
Web.Contentscom a URL base eJson.Documentpara confirmar que você recebe o JSON esperado e enxerga a estrutura. - Configure a autenticação. Coloque o token ou a chave no
Headers, e valide que a resposta deixou de vir com erro 401 ou 403. - Modele a resposta em tabela. Identifique se o topo é record ou list, acesse o campo certo (tipo
Json[data]) e transforme em tabela comTable.FromRecordsou "Para Tabela". - Resolva a paginação. Escolha
List.Generatepara cursor ou laço por número de página, e valide a contagem total contra o que a API informa. - Refatore para RelativePath e Query. Separe a URL base fixa do resto antes de publicar. Esse passo é obrigatório, não opcional.
- Publique e configure a credencial no serviço. Defina o agendamento de atualização e rode um refresh manual para confirmar que funciona na nuvem, não só na sua máquina.
Se em algum ponto você precisar de automação em volta da API, tipo disparar uma coleta sob demanda ou tratar respostas antes de chegar ao Power BI, aí entra o mundo do Power Platform, que conversa muito bem com esse tipo de integração.
Perguntas frequentes
Preciso saber programar em M para conectar numa API?
Para o básico, não. O conector Web gera a chamada Web.Contents para você e a interface faz boa parte do trabalho de expandir JSON. Mas para autenticação por token, paginação e a correção de RelativePath, você vai precisar editar o código M na barra de fórmulas ou no Editor Avançado. É um M funcional e legível, dá para aprender rápido.
Por que meu relatório atualiza no Power BI Desktop mas falha no serviço?
Quase sempre é URL dinâmica. Se você concatena parâmetros direto na string da URL dentro do Web.Contents, o serviço não reconhece a fonte de forma estável e bloqueia a atualização agendada. A correção é usar a URL base fixa no primeiro argumento e mover o que varia para RelativePath e Query.
Onde eu coloco o token de autenticação com segurança?
Para testar, pode ser direto no código. Para produção, transforme o token em parâmetro da consulta, use a tela de credenciais da fonte quando o método permitir, ou puxe de um cofre de segredos numa arquitetura maior. Nunca deixe o token cravado em texto claro numa query que outras pessoas podem abrir e copiar.
Como sei se recebi um record ou uma list do Json.Document?
Olhe o começo da resposta JSON. Se abre com chave {, é um objeto e vira record no Power Query. Se abre com colchete [, é um array e vira list. No editor, o ícone da célula também mostra "Record" ou "List", e você clica para expandir conforme o caso.
Minha API tem limite de requisições. Isso atrapalha a paginação?
Pode atrapalhar. Se você dispara páginas rápido demais, a API pode responder com erro de limite excedido (429). Vale conferir na documentação quantas requisições por minuto são permitidas e, se necessário, reduzir o per_page para ter menos páginas, ou tratar o erro com uma lógica de nova tentativa. Volume alto e refresh frequente pedem uma conversa sobre arquitetura.
Vale a pena trazer a API direto no Power Query ou é melhor usar outra ferramenta?
Depende do volume e da criticidade. Para dados leves e atualização diária, o Power Query resolve muito bem. Para grandes volumes, muitas fontes e regras pesadas, faz mais sentido levar a ingestão para uma camada de dados dedicada e deixar o Power BI só consumir. Nossa sustentação de BI ajuda a decidir esse limite antes de a coisa virar gargalo.
Fechando
Conectar o Power Query a uma API e à Web é uma habilidade que rende muito, e a fórmula é sempre a mesma: Web.Contents para buscar, Json.Document para ler, Headers para autenticar, List.Generate para paginar e RelativePath para não quebrar o refresh na nuvem. Domine esses cinco e você resolve a grande maioria das integrações que aparecem no dia a dia.
Se a sua integração está mais complexa do que uma query dá conta, ou o refresh vive falhando e ninguém sabe por quê, fale com a gente. A gente faz esse tipo de conexão funcionar de forma estável todos os dias.
Quer aplicar isso na sua empresa?
A Fynx implementa BI, Power BI e Power Platform de ponta a ponta. Conte seu cenário e devolvemos um diagnóstico direto ao ponto.
Falar com um especialista