Porque é que o seletor de datas do Google Search Console mostra o ano de 1969
A falha no seletor de datas do relatório de desempenho do Google Search Console mostra a fragilidade de depender apenas da interface web. A 13 de outubro de 2026, Barry Schwartz descreveu no Search Engine Roundtable que, ao definir um intervalo de datas personalizado, o relatório rejeita o formulário com a mensagem Date can't be after 1969-12-31, ou seja: a data não pode ser posterior a 31 de dezembro de 1969. O seletor foi corrigido, voltou a falhar a 14 de outubro e, algumas horas depois, estava de novo a funcionar. Enquanto a falha dura, os analistas perdem a possibilidade de filtrar dados históricos no navegador.
A Google não indicou a causa, mas a própria data diz muito. Na norma POSIX, o tempo Unix é o número de segundos decorridos desde a meia-noite de 1 de janeiro de 1970 UTC. Se um campo de data em JavaScript recebe um valor nulo, uma cadeia vazia ou um objeto ainda não preparado, acaba facilmente no valor 0. Em fusos horários com desvio negativo face ao UTC, esse ponto zero cai a 31 de dezembro de 1969, nos fusos norte-americanos entre as 16:00 e as 19:00. Por isso, a explicação mais provável é um carimbo de data/hora igual a zero que chegou ao validador como limite superior do intervalo.
Partindo deste pressuposto, o validador compara a data escolhida com um limite fixado em 1969. Qualquer data real é posterior, pelo que a regra considera o intervalo inválido e bloqueia o gráfico.
Para sites de grande escala operacional, lojas de comércio eletrónico e agências que gerem dezenas de portais, uma falha destas paralisa o diagnóstico diário. O trabalho das equipas de SEO não pode depender da estabilidade de uma única aplicação de página única (SPA). A solução que garante continuidade analítica total é a integração direta com a Google Search Console API oficial.
Arquitetura de acesso aos dados do Search Console
[Interface Web GSC] [Search Analytics API v1]
│ │
Erros de UI, limite de 1000 linhas Endpoint REST estável
Problema Unix Epoch (1969) 25 000 linhas por pedido
Sem exportação automática 50 000 linhas por dia
│ │
▼ ▼
[Navegador Web] [BD Local / BigQuery]Quais são os limites da interface do Google Search Console em comparação com a API
A interface gráfica do Google Search Console foi concebida para uma consulta rápida dos indicadores básicos e não para engenharia de dados avançada. Mesmo quando o seletor de datas funciona corretamente, o painel web impõe restrições que distorcem a imagem da visibilidade do domínio.
A primeira restrição é o limite rígido de apresentação. A tabela do painel do Search Console mostra no máximo 1000 linhas. Em portais com centenas de páginas e dezenas de milhares de consultas de cauda longa (long-tail keywords), o utilizador vê apenas uma parte das expressões que trazem tráfego.
O segundo problema é a retenção de dados. O Google Search Console elimina os registos com mais de 16 meses. Se o portal não tiver um mecanismo automático de arquivo, as comparações homólogas ao longo de vários anos tornam-se impossíveis. O terceiro fator é a anonimização de consultas raras (anonymized queries): a Google filtra-as por motivos de privacidade no painel, na API e na exportação para o BigQuery. Em contrapartida, a API não tem o limite de 1000 linhas da tabela, e os pedidos feitos separadamente para cada dia devolvem até 50 000 linhas por dia e por tipo de pesquisa (documentação da Google).
| Característica | Interface web GSC | Search Analytics API |
|---|---|---|
| Limite de linhas por pedido | 1 000 linhas | 25 000 linhas |
| Número máximo de linhas | 1 000 (limite rígido da tabela) | 50 000 por dia e por tipo de pesquisa (paginação com startRow) |
| Período de retenção | 16 meses | 16 meses, mais tempo depois de guardados na sua própria base de dados |
| Vulnerabilidade a erros de frontend | Elevada (erros de UI, bloqueios JS) | Sem camada de UI (resposta JSON de um endpoint REST) |
| Automação de relatórios | Exige cliques manuais | Automação total (cron, workers, CI) |
| Análise multidimensional | Filtros cruzados limitados | Combinação total: page + query + device + country |
A utilização da API torna os relatórios independentes dos erros de frontend da Google e dá aos programadores controlo sobre o arquivo de dados.
Como configurar uma service account para a Google Search Console API
Para aceder à Search Analytics API sem ter de iniciar sessão no navegador a cada vez com o protocolo OAuth2 para utilizadores finais, é necessário configurar uma conta de serviço (Service Account) na Google Cloud Platform. A conta de serviço funciona como uma entidade técnica autónoma com o seu próprio par de chaves criptográficas.
O primeiro passo é criar um projeto na consola da Google Cloud (console.cloud.google.com). Depois de criado o projeto, acede-se à secção APIs & Services, pesquisa-se Google Search Console API e clica-se no botão que ativa o serviço.
O segundo passo é gerar a conta de serviço na secção IAM & Admin, na subsecção Service Accounts. Atribui-se à conta um nome único, por exemplo gsc-analytics-extractor. Depois de criada a conta, abre-se o separador Keys e cria-se uma nova chave privada em formato JSON. O ficheiro é descarregado para o disco local e contém o identificador da chave, o endereço de email da conta de serviço e a chave privada RSA em formato PEM.
{
"type": "service_account",
"project_id": "analytics-core-pipeline",
"private_key_id": "8a9b0c1d2e3f4a5b",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDE...\n-----END PRIVATE KEY-----\n",
"client_email": "gsc-analytics-extractor@analytics-core-pipeline.iam.gserviceaccount.com",
"client_id": "103847562910384756291",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}O terceiro passo é autorizar a conta de serviço no painel do Google Search Console. Copia-se o endereço client_email e abre-se o Search Console da propriedade verificada. Acede-se a Definições (Settings), escolhe-se Utilizadores e autorizações (Users and permissions) e adiciona-se o novo endereço de email com a função Restrito (Restricted) ou Total (Full). A partir deste momento, a conta de serviço pode consultar os endpoints do Search Console para essa propriedade.
O ficheiro JSON com a chave privada nunca pode chegar a um repositório de código público. Deve ser adicionado ao .gitignore ou o seu conteúdo deve ser passado como variável de ambiente encriptada no servidor de produção.
Como verificar a que propriedades do Search Console a conta de serviço tem acesso
A causa mais frequente de um erro 403 depois de configurar a conta de serviço é uma discrepância entre o endereço da propriedade no script e aquele que o Search Console conhece. Em vez de adivinhar, vale a pena perguntar à API. O método sites.list devolve a lista de propriedades a que a conta tem acesso, e cada entrada contém dois campos: siteUrl, escrito exatamente como os restantes métodos o esperam, e permissionLevel (documentação do recurso sites).
O campo permissionLevel assume um de quatro valores: siteOwner (proprietário), siteFullUser (permissões totais), siteRestrictedUser (permissões restritas) e siteUnverifiedUser (utilizador sem propriedade verificada). Se a propriedade não aparecer na lista, o email da conta de serviço não foi adicionado nas definições do Search Console ou foi adicionado a outra propriedade, por exemplo ao prefixo https://www. em vez da propriedade de domínio.
const { data } = await searchconsole.sites.list();
for (const site of data.siteEntry || []) {
console.log(site.siteUrl, site.permissionLevel);
}
// sc-domain:wppoland.com siteFullUserO mesmo recurso tem os métodos add, delete e get. Para obter dados basta o âmbito webmasters.readonly e um list executado uma vez no arranque do script: se o siteUrl devolvido for diferente do configurado, o script pode terminar com uma mensagem clara antes de disparar centenas de pedidos com erro.
Como obter dados de sc-domain e de prefixo de URL em Node.js
Ao trabalhar com a Search Console API, os programadores deparam-se muitas vezes com um erro 403 ou 404 causado por um formato incorreto do identificador do site (siteUrl). A Google suporta dois tipos de propriedade distintos:
- Propriedade de domínio (Domain Property): agrega todos os protocolos e subdomínios. Na API, o identificador tem de ter o prefixo
sc-domain:, por exemplosc-domain:wppoland.com. - Propriedade de prefixo de URL (URL-prefix Property): diz respeito a um protocolo e a um caminho concretos. Na API, o identificador é o URL completo com barra final, por exemplo
https://wppoland.com/pt-pt/.
Em sites multilingues, a boa prática é consultar as propriedades de prefixo de cada versão linguística ou a propriedade de domínio e filtrar depois os caminhos de URL no código.
O script em Node.js abaixo mostra uma implementação completa da extração de dados analíticos com paginação e gravação num ficheiro JSON. Utiliza a biblioteca oficial googleapis.
import fs from "node:fs";
import path from "node:path";
import { google } from "googleapis";
async function exportarMetricasSearchConsole() {
const caminhoCredenciais = path.resolve("./scripts/gsc-service-account.json");
const credenciais = JSON.parse(fs.readFileSync(caminhoCredenciais, "utf8"));
if (credenciais.private_key) {
credenciais.private_key = credenciais.private_key.replace(/\\n/g, "\n");
}
const auth = new google.auth.GoogleAuth({
credentials: credenciais,
scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
});
const cliente = await auth.getClient();
const searchconsole = google.searchconsole({ version: "v1", auth: cliente });
const siteUrl = "sc-domain:wppoland.com";
// Margem de 3 dias para permitir a consolidacao final dos dados pelo Google
const dataFim = new Date(Date.now() - 3 * 864e5).toISOString().slice(0, 10);
const dataInicio = new Date(Date.now() - 33 * 864e5).toISOString().slice(0, 10);
console.log(`A iniciar extracao para ${siteUrl} entre ${dataInicio} e ${dataFim}`);
const todasAsLinhas = [];
let startRow = 0;
const limiteLinhas = 25000;
while (true) {
const resposta = await searchconsole.searchanalytics.query({
siteUrl,
requestBody: {
startDate: dataInicio,
endDate: dataFim,
dimensions: ["page", "query", "device", "country"],
rowLimit: limiteLinhas,
startRow,
},
});
const linhas = resposta.data.rows || [];
todasAsLinhas.push(...linhas);
console.log(`Lote obtido: ${linhas.length} linhas (Total acumulado: ${todasAsLinhas.length})`);
if (linhas.length < limiteLinhas) {
break;
}
startRow += limiteLinhas;
}
const ficheiroDestino = "./reports/gsc-performance-data.json";
fs.mkdirSync(path.dirname(ficheiroDestino), { recursive: true });
fs.writeFileSync(ficheiroDestino, JSON.stringify(todasAsLinhas, null, 2), "utf8");
console.log(`Extracao terminada. Guardados ${todasAsLinhas.length} registos em ${ficheiroDestino}`);
}
exportarMetricasSearchConsole().catch((erro) => {
console.error("Falha na extracao do Search Console:", erro.message);
process.exit(1);
});Este código percorre as páginas de resultados sucessivas com o parâmetro startRow. Quando a resposta da API contém menos linhas do que o limite declarado de 25 000, o ciclo termina. Assim obtém o histórico completo de consultas sem perder os dados da cauda longa.
Como extrair dados do Google Search Console em Python
Para engenheiros de dados e equipas que trabalham com Pandas e Jupyter, um script em Python é a ferramenta mais prática para extrair e limpar dados do Search Console. O exemplo abaixo obtém os dados divididos por dia e grava-os no formato otimizado Parquet, que ocupa menos espaço do que o CSV e preserva os tipos das colunas.
Para executar o script são necessárias as bibliotecas google-api-python-client, google-auth, pandas e pyarrow:
pip install google-api-python-client google-auth pandas pyarrowImplementação do script:
import datetime
import pandas as pd
from google.oauth2 import service_account
from googleapiclient.discovery import build
def extrair_dados_pesquisa(site_url: str, dias: int = 30) -> pd.DataFrame:
ficheiro_chave = "scripts/gsc-service-account.json"
credenciais = service_account.Credentials.from_service_account_file(
ficheiro_chave,
scopes=["https://www.googleapis.com/auth/webmasters.readonly"]
)
servico = build("searchconsole", "v1", credentials=credenciais)
data_fim = (datetime.date.today() - datetime.timedelta(days=3)).strftime("%Y-%m-%d")
data_inicio = (datetime.date.today() - datetime.timedelta(days=dias + 3)).strftime("%Y-%m-%d")
linhas_acumuladas = []
start_row = 0
row_limit = 25000
while True:
corpo = {
"startDate": data_inicio,
"endDate": data_fim,
"dimensions": ["date", "page", "query", "country", "device"],
"rowLimit": row_limit,
"startRow": start_row
}
resposta = servico.searchanalytics().query(
siteUrl=site_url,
body=corpo
).execute()
registos = resposta.get("rows", [])
if not registos:
break
for reg in registos:
linhas_acumuladas.append({
"date": reg["keys"][0],
"page": reg["keys"][1],
"query": reg["keys"][2],
"country": reg["keys"][3],
"device": reg["keys"][4],
"clicks": reg["clicks"],
"impressions": reg["impressions"],
"ctr": reg["ctr"],
"position": round(reg["position"], 2)
})
print(f"Obtidas {len(registos)} linhas (startRow={start_row})")
if len(registos) < row_limit:
break
start_row += row_limit
df = pd.DataFrame(linhas_acumuladas)
return df
if __name__ == "__main__":
identificador = "sc-domain:wppoland.com"
df_dados = extrair_dados_pesquisa(identificador, dias=60)
if not df_dados.empty:
df_dados["date"] = pd.to_datetime(df_dados["date"])
df_dados.to_parquet("reports/gsc_performance.parquet", index=False)
print(f"Sucesso. {len(df_dados)} registos guardados em Parquet.")
else:
print("Sem dados disponiveis para exportacao.")A gravação no formato colunar Parquet permite agregar rapidamente e agrupar consultas por categoria de produto ou por modelo de página no WordPress, sem sobrecarregar a memória do servidor.
Como obter dados recentes e por hora da Search Console API
Os scripts acima ignoram os últimos três dias, porque a Google indica que os dados ficam normalmente disponíveis ao fim de 2 a 3 dias (guia para obter todos os dados). O mesmo guia recomenda um teste simples: um pedido com a dimensão date para os últimos 10 dias mostra qual é o último dia disponível.
Se a API devolve ou não dados preliminares é decidido pelo parâmetro dataState (documentação do método query):
Valor de dataState | O que a API devolve |
|---|---|
final (ou sem parâmetro) | Apenas dados finalizados |
all | Também dados recentes, que ainda podem mudar |
hourly_all | Dados divididos por hora, parciais, para usar com a dimensão hour |
Com dataState: "all" e agrupamento por data, a resposta inclui nos metadados o campo first_incomplete_date. Todos os valores posteriores a essa data ainda podem mudar de forma significativa, pelo que um script de arquivo deve guardá-los à parte ou substituí-los na execução seguinte. As horas nos metadados são indicadas no fuso America/Los_Angeles, o que conta quando se cruzam os dados com os registos de um servidor em Portugal.
A Google acrescentou a dimensão hour a 9 de abril de 2025. O painel mostra dados por hora apenas para as últimas 24 horas, enquanto a API os devolve para um máximo de 10 dias (anúncio no Google Search Central Blog). Isto permite comparar o último dia com o mesmo dia da semana anterior, por exemplo depois de publicar um artigo ou de implementar um novo tema.
{
"startDate": "2026-10-15",
"endDate": "2026-10-15",
"dataState": "hourly_all",
"dimensions": ["hour"]
}As chaves na resposta têm o formato 2026-10-15T00:00:00-07:00, ou seja, a hora no fuso de Los Angeles com o respetivo desvio. Antes de gravar na base de dados, convém convertê-las para UTC.
Como filtrar páginas e consultas na Search Analytics API
Um pedido sem filtros devolve todo o tráfego da propriedade. Num site multilingue, normalmente é preciso apenas uma fatia, por exemplo só os endereços com /pt-pt/. Para isso serve a matriz dimensionFilterGroups. É possível filtrar pelas dimensões country, device, page, query e searchAppearance, e os operadores disponíveis são (documentação do método query):
equals(predefinido) enotEquals: correspondência exata, com distinção entre maiúsculas e minúsculas parapageequery,containsenotContains: correspondência parcial sem distinção entre maiúsculas e minúsculas,includingRegexeexcludingRegex: expressão regular na sintaxe RE2.
{
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": ["page", "query"],
"type": "web",
"dimensionFilterGroups": [{
"filters": [
{ "dimension": "page", "operator": "includingRegex", "expression": "^https://wppoland\\.com/pt-pt/" },
{ "dimension": "query", "operator": "notContains", "expression": "wppoland" }
]
}],
"rowLimit": 25000
}O segundo filtro exclui as consultas de marca, um passo típico antes de analisar a cauda longa. O parâmetro type substituiu o obsoleto searchType e aceita os valores web (predefinido, separador Tudo sem Discover nem Google News), image, video, news (separador Notícias), discover e googleNews. Cada tipo tem de ser obtido num pedido separado.
O parâmetro aggregationType decide se as métricas são calculadas por propriedade (byProperty) ou por URL (byPage); o valor predefinido auto deixa a escolha à Google. O valor byProperty não funciona com os tipos discover e googleNews.
Porque é que o total da API não coincide com o gráfico do Search Console
Depois da primeira exportação, quase todas as equipas reparam que a soma dos cliques nas linhas é inferior ao número apresentado no gráfico do painel. Não é um erro do script. A Google descreve três causas (guia para obter todos os dados):
- A API disponibiliza no máximo 50 000 linhas por dia e por tipo de pesquisa, ordenadas por cliques. Em sites grandes, a cauda de consultas abaixo desse limiar não chega à resposta.
- Ao agrupar pelas dimensões
pageouquery, o sistema pode omitir parte dos dados para que os cálculos continuem exequíveis. - Impressões, cliques, posição e CTR são calculados de forma diferente na agregação por página e na agregação por propriedade.
A isto juntam-se as consultas anonimizadas, que não existem em nenhuma fonte. Conclusão prática: obtenha os totais de tráfego com um pedido sem as dimensões page e query (apenas date) e trate a divisão por expressões como uma amostra ordenada por cliques. A Google recomenda também obter os dados dia a dia e paginar de 25 000 em 25 000 linhas até surgir uma página vazia, separadamente para cada tipo de pesquisa.
Como guardar dados do Google Search Console no Google BigQuery
A solução mais resistente a falhas para sites grandes é a exportação automática dos dados do Google Search Console para o armazém de dados Google BigQuery. A Google lançou o Bulk Data Export a 21 de fevereiro de 2023: o mecanismo envia diariamente uma cópia dos dados de desempenho para o projeto BigQuery indicado (anúncio no Google Search Central Blog).
Vantagens desta abordagem:
- Sem o limite de 16 meses: os dados guardados no BigQuery ficam lá durante o tempo de que precisar.
- A exportação não está sujeita ao limite diário de linhas da API. A Google filtra apenas as consultas anonimizadas, por motivos de privacidade.
- Possibilidade de cruzar os dados do Search Console com tabelas da base de dados do WordPress (encomendas WooCommerce, carrinhos, conversões).
A exportação configura-se em dois passos. Na Google Cloud Console, ative a BigQuery API no projeto e conceda permissões à conta de serviço do Search Console. Nas definições do Search Console, indique o identificador do projeto e a localização do conjunto de dados. Só o proprietário da propriedade pode configurar a exportação. O Search Console começa por simular a exportação; se a simulação correr bem, todos os proprietários recebem um email e as exportações regulares arrancam no prazo de 48 horas. Em caso de erro, o alerta surge de imediato.
A exportação cria três tabelas:
searchdata_site_impression: dados agregados por propriedade, com consulta, país, tipo de pesquisa e dispositivo.searchdata_url_impression: dados agregados por URL, com detalhes das consultas e dos resultados com elementos avançados.ExportLog: registo dos dados que chegaram à base em cada dia. As exportações falhadas não ficam aqui registadas.
Exemplo de consulta SQL no BigQuery que identifica páginas com muitas impressões e CTR baixo no mercado português (candidatas à otimização das meta tags e dos títulos):
SELECT
url,
query,
SUM(impressions) AS total_impressions,
SUM(clicks) AS total_clicks,
ROUND(SAFE_DIVIDE(SUM(clicks), SUM(impressions)) * 100, 2) AS calculated_ctr,
ROUND(SUM(sum_position) / SUM(impressions) + 1, 1) AS avg_position
FROM
`projeto-analytics.searchconsole.searchdata_url_impression`
WHERE
data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND country = 'prt'
GROUP BY
url,
query
HAVING
total_impressions > 500
AND calculated_ctr < 2.0
AND avg_position BETWEEN 3.0 AND 10.0
ORDER BY
total_impressions DESC
LIMIT 100;A tabela searchdata_url_impression inclui, entre outras, as colunas data_date, url, query, country, search_type, impressions, clicks e sum_position, bem como indicadores dos tipos de resultados avançados (o exemplo da Google filtra search_type = 'WEB'). A coluna sum_position conta a posição a partir de zero, por isso a posição média calcula-se como SUM(sum_position) / SUM(impressions) + 1, tal como no exemplo da Google. A consulta seleciona expressões da primeira página de resultados que obtêm menos de 2 por cento de cliques. São candidatas a um teste de novo título, cabeçalho H1 e descrição; o efeito da alteração tem de ser medido nas semanas seguintes, não presumido à partida.
Pipeline Search Console para o BigQuery
[Google Search Console] ──(Bulk Export)──> [Google BigQuery]
│
├──> [Painel Looker Studio]
├──> [Auditoria de perda de posicoes]
└──> [Cruzamento com encomendas WooCommerce]Quando escolher a Search Analytics API e quando o Bulk Data Export
As duas fontes dão as mesmas métricas, mas resolvem problemas diferentes. No anúncio da exportação, a Google escreve que ela é especialmente útil para sites grandes, com dezenas de milhares de páginas ou com tráfego de dezenas de milhares de consultas por dia, e que os sites pequenos e médios têm acesso a todos os seus dados através da interface, do conector do Looker Studio ou da Search Analytics API (Google Search Central Blog).
| Critério | Search Analytics API | Bulk Data Export |
|---|---|---|
| Limite de linhas | 25 000 por pedido, 50 000 por dia e por tipo de pesquisa | Sem limite diário de linhas |
| Consultas anonimizadas | Omitidas | Omitidas |
| Atualidade | Dados preliminares (all) e por hora (hourly_all) | Cópia diária |
| Quem pode configurar | Qualquer utilizador com acesso à propriedade | Apenas o proprietário da propriedade |
| Para onde vão os dados | Para onde o seu script os gravar | Para o seu projeto BigQuery |
Na prática, as duas abordagens complementam-se. A exportação para o BigQuery funciona como arquivo e fonte para análises de cauda longa, enquanto um script leve sobre a API com dataState: "hourly_all" vigia as últimas horas depois de uma publicação ou implementação. Para um blog pequeno em WordPress, o script das secções anteriores, executado uma vez por dia por cron, costuma bastar.
Como associar dados do Search Console à base de dados do WordPress
Os dados brutos do Search Console ganham o maior valor de negócio quando são associados à estrutura interna de conteúdos do CMS. A exportação tradicional do Search Console contém apenas URLs completos sob a forma de cadeias de texto. Não informa sobre o tipo de conteúdo (artigo, produto WooCommerce, página de destino), a data da última alteração no editor Gutenberg nem o autor do texto.
A integração dos dados consiste em traduzir o caminho do URL para o identificador do objeto no WordPress (post_id). No WordPress, para isso serve a função url_to_postid($url) ou a correspondência direta do slug na tabela wp_posts.
O excerto de código PHP abaixo ilustra como atribuir os dados de consultas importados da API ao produto ou artigo correto na base de dados:
<?php
/**
* Integracao de metricas do Search Console com o WordPress
*/
declare(strict_types=1);
namespace WPPoland\Analytics;
class SearchConsoleIngestor {
public function atualizarMetricasArtigo(string $url, int $cliques, int $impressoes, float $posicao): void {
$caminho = wp_parse_url($url, PHP_URL_PATH);
if (!$caminho) {
return;
}
$postId = url_to_postid($url);
if ($postId === 0) {
$postId = $this->obterIdTermoTaxonomia($caminho);
}
if ($postId > 0) {
update_post_meta($postId, '_gsc_cliques_30d', $cliques);
update_post_meta($postId, '_gsc_impressoes_30d', $impressoes);
update_post_meta($postId, '_gsc_posicao_media', $posicao);
update_post_meta($postId, '_gsc_data_atualizacao', current_time('mysql', true));
}
}
private function obterIdTermoTaxonomia(string $caminho): int {
global $wpdb;
$slugLimpo = trim($caminho, '/');
$segmentos = explode('/', $slugLimpo);
$slugFinal = end($segmentos);
$termId = $wpdb->get_var($wpdb->prepare(
"SELECT t.term_id FROM {$wpdb->terms} t
JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
WHERE t.slug = %s LIMIT 1",
$slugFinal
));
return $termId ? (int)$termId : 0;
}
}Com esta associação, a equipa editorial vê no painel do WordPress o número exato de impressões e a posição de cada página. Se, depois de uma atualização do tema ou da implementação de novo código, a posição média de um artigo cair do lugar 4 para o 18, o sistema pode gerar automaticamente uma notificação num canal do Slack ou criar uma tarefa de auditoria para o programador.
Em arquiteturas baseadas em WordPress headless ou em geradores de sites estáticos, os dados da Search Console API podem ser obtidos durante a compilação do site (build time) e usados para gerar secções dinâmicas com os temas mais populares e os artigos relacionados.
Como verificar o estado de indexação dos endereços exportados com a URL Inspection API
Uma queda de cliques de um artigo nos dados da Search Analytics não diz se a página continua no índice. A resposta vem da URL Inspection API, que usa a mesma conta de serviço e os mesmos âmbitos (webmasters ou webmasters.readonly). O pedido é POST https://searchconsole.googleapis.com/v1/urlInspection/index:inspect com os campos inspectionUrl (endereço completo, que tem de pertencer à propriedade), siteUrl (no mesmo formato de antes: sc-domain: ou prefixo com barra final) e o campo opcional languageCode (documentação do método inspect).
O resultado indexStatusResult contém, entre outros (descrição de UrlInspectionResult):
| Campo | O que significa |
|---|---|
verdict | Resultado geral: se o endereço está indexado |
coverageState | Se a Google encontrou e indexou a página |
indexingState | Se uma regra noindex bloqueia a indexação |
robotsTxtState | Se o robots.txt bloqueia o rastreio |
lastCrawlTime | Quando o rastreador principal obteve o endereço pela última vez |
googleCanonical e userCanonical | Canónico escolhido pela Google e canónico declarado na página |
No WordPress, isto combina bem com a classe SearchConsoleIngestor da secção anterior: quando a posição de um artigo cai, o script verifica o seu endereço e grava coverageState e googleCanonical nos metadados do artigo. Uma discrepância entre o canónico da Google e o do plugin de SEO costuma explicar a queda mais depressa do que a análise de expressões. O limite, porém, é baixo: 2000 pedidos por dia e 600 por minuto por site (limites da Search Console API), por isso verifique apenas os endereços com sinal de queda, não o sitemap inteiro. O método mostra a versão do índice da Google e não testa o endereço em tempo real. A diferença entre esta ferramenta e a Indexing API é explicada no nosso guia do GSC e da Indexing API.
Como lidar com limites de quota e erros da API
A Search Analytics API tem limites descritos na documentação da Google. Ultrapassá-los resulta num erro de quota, e o script tem de esperar:
| Âmbito do limite | Valor |
|---|---|
| Por site (pedidos sobre a mesma propriedade) | 1 200 pedidos por minuto |
| Por utilizador (pedidos da mesma conta) | 1 200 pedidos por minuto |
| Por projeto Google Cloud | 40 000 pedidos por minuto e 30 000 000 por dia |
À parte funciona o limite de carga (load quota), calculado em janelas de 10 minutos e diárias. Segundo a Google, os pedidos são dispendiosos quando agrupa ou filtra por página ou por consulta, e a carga aumenta com a extensão do intervalo de datas. A Google desaconselha expressamente obter os mesmos dados várias vezes, por exemplo o mês anterior inteiro a cada execução. Um script de arquivo deve, por isso, ir buscar apenas os dias em falta.
Para garantir a fiabilidade de scripts que obtêm milhões de registos em grandes lojas de comércio eletrónico, convém implementar três mecanismos de defesa:
- Atraso exponencial nas novas tentativas (Exponential Backoff): ao receber um código de erro 429 ou 503, o script não interrompe o trabalho e espera um número crescente de segundos (2 elevado a n mais um jitter aleatório) antes de repetir o pedido.
- Divisão dos pedidos por dias individuais (Daily Date Chunking): em vez de obter os dados de um ano inteiro numa só chamada com paginação enorme, divide-se a tarefa em ciclos de um dia. Se um dos pedidos falhar por um erro de rede momentâneo, repete-se apenas o dia em falta.
- Reutilização dos tokens de autorização: as bibliotecas cliente da Google renovam automaticamente o token OAuth2 com base na chave da conta de serviço, mas em ambientes serverless (AWS Lambda, Cloudflare Workers) é preciso garantir que o token não é gerado de novo para cada pedido HTTP.
O excerto de código abaixo ilustra um mecanismo de consulta resiliente com novas tentativas:
async function executarComRepeticao(acaoApi, limiteTentativas = 5) {
let tentativa = 0;
while (tentativa < limiteTentativas) {
try {
return await acaoApi();
} catch (erro) {
tentativa++;
const eLimiteQuota = erro.code === 429 || erro.status === 429;
const eErroServidor = erro.code >= 500 && erro.code < 600;
if ((eLimiteQuota || eErroServidor) && tentativa < limiteTentativas) {
const esperaMs = Math.pow(2, tentativa) * 1000 + Math.random() * 500;
console.warn(`Erro ${erro.code}. Nova tentativa em ${Math.round(esperaMs)} ms (Tentativa ${tentativa}/${limiteTentativas})...`);
await new Promise((resolve) => setTimeout(resolve, esperaMs));
} else {
throw erro;
}
}
}
}Estes três mecanismos fazem com que o arquivo de dados de SEO funcione independentemente das falhas no painel do Google Search Console no navegador.
Como gerir dados de SEO com a Search Console API
Erros da interface gráfica, como a falha do seletor de datas e o recuo do calendário para 1969, lembram que os dados analíticos de um site devem estar no sistema do seu proprietário e não apenas num painel externo.
Um pipeline independente assente na Search Console API traz benefícios concretos:
- Até 50 000 linhas por dia e por tipo de pesquisa em vez das 1000 linhas da tabela do painel e, com o Bulk Data Export, o conjunto completo sem as consultas anonimizadas.
- Relatórios independentes dos erros de frontend da Google.
- Proteção contra a perda de dados históricos ao fim de 16 meses, graças ao arquivo no BigQuery ou numa base de dados relacional.
- Possibilidade de correlacionar diretamente o tráfego com as receitas da loja WooCommerce e com as alterações no código do tema WordPress.
Para implementar análises avançadas, automatizar relatórios ou fazer uma auditoria técnica do site, vale a pena consultar um programador WordPress que ligue o pipeline de dados à arquitetura da sua loja e o proteja contra falhas. Para consultoria técnica, entre em contacto com a nossa equipa.







