Glanceshot
Documentação para programadores

API Documentação

URL base: https://glanceshot.com/v1. Todos os endpoints usam HTTP padrão. Os parâmetros podem ser enviados na cadeia de consulta (GET) ou no corpo JSON (POST).

Início rápido

  1. Registe-se gratuitamente e crie uma API Key na página «API Key» da consola.
  2. Inclua a API Key no cabeçalho X-Access-Key e faça um pedido ao endpoint de captura:
curl "https://glanceshot.com/v1/screenshot?url=https://example.com&full_page=true" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -o screenshot.png

Autenticação

Todos os pedidos exigem uma API Key, que pode ser enviada de uma das seguintes formas:

  • Cabeçalho X-Access-Key: gls_… (recomendado para pedidos a partir do servidor)
  • Parâmetro de consulta access_key=gls_… (apenas para chamadas do lado do servidor; não o inclua em páginas web)
  • URL assinado (para incluir o endereço numa página web, sem expor a API Key nem o segredo)

A API Key só é apresentada em texto simples uma vez, no momento da criação. Guarde-a em segurança. Se for exposta, pode desativá-la na consola.

Capturas de ecrã

GET/v1/screenshot
POST/v1/screenshot

Parâmetros exclusivos das capturas de ecrã:

ParâmetroTipoValor predefinidoDescrição
formatenumpngpng, jpeg ou webp.
device_scale_factornumber1Fator de escala de 1–3. O valor 2 oferece nitidez Retina, com um ficheiro cerca de 4 vezes maior.
qualityinteger80Qualidade JPEG / WebP, 1–100.
full_pagebooleanfalseCaptura a página inteira. Percorre a página ecrã a ecrã para ativar o carregamento diferido, até uma altura máxima de 20,000 píxeis.
selectorstring—Captura apenas o primeiro elemento correspondente a este seletor CSS. Não pode ser usado com full_page.

Parâmetros comuns às capturas de ecrã e aos PDF:

ParâmetroTipoValor predefinidoDescrição
urlstring—Endereço da página a capturar, com http ou https. Use este parâmetro ou html, não ambos. O fragmento # do URL é preservado, permitindo capturar diretamente a página correspondente em aplicações de página única com encaminhamento por hash (como https://app.com/#/report/1).
htmlstring—Envie HTML diretamente (máximo de 2 MB). Apenas POST.
viewport_widthinteger1280Largura da área de visualização, 320–3840. Abaixo de 768, a página é renderizada como num dispositivo móvel.
viewport_heightinteger800Altura da área de visualização, 200–3840.
wait_untilenumnetworkidleEstratégia de espera: domcontentloaded (a mais rápida, ideal para páginas estáticas), load ou networkidle (acompanha todos os pedidos desde a abertura da página e, após o carregamento, só captura quando não houver pedidos em curso durante 500 milissegundos consecutivos; espera até mais 8 segundos, ideal para aplicações de página única).
wait_for_selectorstring—Após carregar a página, espera que um elemento correspondente a este seletor CSS apareça e fique visível antes de capturar, até 10 segundos. Se não aparecer, devolve selector_not_found (sem cobrança). Ideal para aplicações de página única que renderizam conteúdo com atraso através de temporizadores ou animações, sem depender de pedidos de rede, como .chart-ready.
delayinteger0Atraso adicional em milissegundos após a espera, 0–10000. Útil para aguardar animações ou componentes com carregamento diferido.
block_adsbooleantrueBloqueia pedidos a domínios de publicidade e rastreamento e oculta os espaços publicitários na página.
block_cookie_bannersbooleantrueOculta as janelas de consentimento de cookies.
dark_modebooleanfalseSimula prefers-color-scheme: dark.
hide_selectorsstring—Lista de seletores CSS dos elementos a ocultar, por exemplo .ad,#popup.
cachebooleantrueDefine se deve procurar na cache. Com false, força uma nova renderização, mas o resultado continua a ser guardado na cache.
cache_ttlinteger86400Prazo de validade da cache em segundos, 60–2592000.
response_typeenumbinarybinary devolve o ficheiro diretamente; json devolve o endereço do ficheiro e os metadados.
proxystring—Opcional. Usa um proxy que adquiriste para aceder à página de destino (os custos do proxy ficam a teu cargo), por exemplo, quando precisas de aceder a partir de um país ou região específicos. Suporta socks5:// e http://, com nome de utilizador e palavra-passe opcionais, no formato socks5://user:pass@203.0.113.10:1080. Só pode ser enviado no corpo de um pedido POST; não está disponível para pedidos GET, URL assinados nem tarefas em lote. Os pedidos com proxy têm uma cache separada. Se não for possível ligar ao proxy, a autenticação falhar ou o proxy não conseguir aceder ao site de destino, é devolvido proxy_failed e o pedido não é cobrado.
cookiesstring | object[]—Cookies do site de destino, numa única linha como sid=abc; theme=dark ou num array como [{"name":"sid","value":"abc"}] (com domain e path opcionais). Só são enviados para o site de destino, não para terceiros presentes na página.
headersobject—Cabeçalhos a adicionar ao pedido, como Authorization: Bearer …. Só são enviados para o site de destino; não é possível definir cabeçalhos geridos pelo navegador (Host, Cookie, User-Agent, etc.).
local_storageobject—Dados a gravar no localStorage da origem do URL de destino. As chaves e os valores têm de ser strings.
session_storageobject—Igual a local_storage, mas grava em sessionStorage.
login_profilestring—Identificador do perfil de sessão guardado com a extensão do navegador (lp_8f2c…). É mais prático do que enviar cookies diretamente e também pode ser usado em tarefas em lote (não são guardadas credenciais nas tarefas).

Capturar aplicações de página única (React, Vue, etc.):A página é capturada num navegador real, após a execução do JavaScript. A opção predefinida networkidle espera que a página termine de obter os dados. Se o conteúdo só for renderizado depois, através de temporizadores ou animações, use wait_for_selector para indicar um elemento que sinalize que o conteúdo está pronto. Se não houver um elemento adequado, use delay para esperar mais um pouco.

PDF

GET/v1/pdf
POST/v1/pdf

Cada PDF consome 2 unidades da quota. Antes da impressão, a página é percorrida para ativar o carregamento diferido. Além dos parâmetros comuns, são suportados:

ParâmetroTipoValor predefinidoDescrição
paperenuma4Formato do papel: a4 ou letter. Se a página definir uma regra @page, essa configuração tem prioridade.
print_backgroundbooleantrueImprime cores e imagens de fundo.
pdf_mediaenumprintprint usa os estilos de impressão; screen usa os estilos de ecrã (o resultado corresponde ao que vê).
Converter HTML em PDF
curl -X POST "https://glanceshot.com/v1/pdf" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Relatório trimestral</h1><p>Corpo do texto…</p>", "paper": "a4"}' \
  -o report.pdf

Respostas

Por predefinição (response_type=binary), o ficheiro é devolvido diretamente, com Content-Type correspondente ao formato. Cabeçalhos de resposta:

  • X-Request-Id: ID do pedido, a indicar ao comunicar um problema
  • X-Glanceshot-Cache: HIT ou MISS
  • X-Glanceshot-Truncated: true: a página inteira foi cortada por exceder 20,000 píxeis
  • X-Glanceshot-Format-Changed: webp: PNG convertido automaticamente em WebP por exceder 5 MB (não há conversão se format=png for especificado explicitamente)

Com response_type=json, são devolvidos o endereço do ficheiro e os metadados:

Resposta JSON
curl -X POST "https://glanceshot.com/v1/screenshot" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "response_type": "json"}'

# Devolve
{
  "url": "https://glanceshot.com/i/3f/3f9c….png",
  "cache_hit": false,
  "duration_ms": 1284,
  "format": "png",
  "width": 1280,
  "height": 800,
  "request_id": "req_01j…"
}

URL assinados

Quer incluir o endereço da captura de ecrã diretamente em <img src> sem expor a API Key? O URL assinado contém apenas o identificador público key_id, nunca a API Key. A assinatura é calculada no seu servidor com o segredo associado à chave. Pode consultar o ID da chave e o segredo na página API Key da consola:

Regras de assinatura
canonical = Ordene todos os parâmetros, exceto signature, por ordem lexicográfica dos nomes,
            formate cada entrada como k=urlencode(v) (RFC 3986, com os espaços codificados como %20) e una-as com &
signature = hex(HMAC-SHA256(secret, canonical))

Uma assinatura válida autentica o pedido. Os pedidos sem assinatura ou com uma assinatura inválida são rejeitados. Qualquer alteração aos parâmetros (por exemplo, mudar url) invalida a assinatura. O parâmetro opcional expires (tempo Unix em segundos) é incluído no cálculo da assinatura; após a expiração, é devolvido signature_expired. Para atualizar a cache, basta alterar um parâmetro sem efeito (como v=2) e gerar uma nova assinatura. Os URL assinados só permitem chamadas GET aos endpoints de captura de ecrã e PDF e não aceitam parâmetros de proxy.

key_id é público e, por si só, não permite chamar a API. Não inclua a API Key (access_key) num URL assinado: se o fizer, será devolvido invalid_params. Se já incluiu a API Key numa página web, desative essa chave na consola e crie uma nova.

import crypto from "node:crypto";

// Codificação RFC 3986 (espaços como %20), de acordo com as regras do servidor
const enc = (v) =>
  encodeURIComponent(v).replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase());

function signedUrl(params, keyId, secret) {
  const all = { ...params, key_id: keyId };
  const canonical = Object.keys(all).sort().map((k) => `${k}=${enc(String(all[k]))}`).join("&");
  const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
  return `https://glanceshot.com/v1/screenshot?${canonical}&signature=${signature}`;
}

const src = signedUrl({ url: "https://example.com", viewport_width: 1200 }, KEY_ID, SECRET);
// <img src={src} alt="Pré-visualização da página web" />

Usar um proxy

Se precisar de aceder a uma página a partir de um país ou região específicos (por exemplo, para ver o idioma, o conteúdo e os preços locais), pode incluir no pedido um proxy contratado por si, cujos custos ficam a seu cargo. A página e todas as imagens, scripts e chamadas a API nela incluídas serão carregados através desse proxy. São suportados proxies socks5:// e http://, com nome de utilizador e palavra-passe opcionais.

curl -X POST "https://glanceshot.com/v1/screenshot" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "proxy": "socks5://user:pass@203.0.113.10:1080"}' \
  -o screenshot.png
  • proxy só pode ser incluído no corpo de um pedido POST: não é permitido em pedidos GET, URL assinados ou tarefas em lote, para evitar que as credenciais do proxy fiquem expostas no URL, no histórico do navegador ou em páginas web.
  • Se o nome de utilizador ou a palavra-passe contiverem caracteres especiais como @ ou :, aplique primeiro a codificação de URL (por exemplo, substitua @ por %40).
  • Os pedidos com proxy têm uma cache separada e não partilham resultados com pedidos sem proxy ou com outros proxies.
  • Se não for possível ligar ao proxy, se a autenticação falhar ou se o site de destino estiver inacessível, é devolvido proxy_failed e o pedido não é cobrado. O campo message indica o motivo específico.
  • O proxy tem de ter um endereço público. Aplicam-se ao URL de destino as mesmas restrições dos pedidos sem proxy: não é permitido aceder a endereços de redes privadas.
  • Ao aceder a sites através de um proxy, respeite os termos de utilização do site de destino e a legislação local.

Capturar páginas com sessão iniciada

Para capturar páginas que exigem autenticação, pode enviar o estado de sessão da sua própria conta. Há duas opções: enviar cookies, headers, local_storage e session_storage diretamente no pedido, ou usar a extensão Glanceshot para guardar o estado de sessão num «perfil de sessão» e enviar apenas login_profile no pedido.

curl -X POST "https://glanceshot.com/v1/screenshot"   -H "X-Access-Key: $GLANCESHOT_KEY"   -H "Content-Type: application/json"   -d '{
    "url": "https://app.example.com/dashboard",
    "cookies": "sid=YOUR_SESSION; theme=dark",
    "local_storage": {"token": "YOUR_TOKEN"}
  }' -o screenshot.png
  • A extensão consegue ler credenciais de sessão às quais os scripts da página não têm acesso (incluindo cookies HttpOnly), bem como localStorage e IndexedDB, e é adequada para a maioria dos sites. Instale e ligue a extensão na página «Perfis de sessão» da consola.
  • O estado de sessão só pode ser enviado no corpo de um pedido POST (GET e URLs assinados são rejeitados) e não é guardado nos logs nem no histórico de chamadas. Os pedidos com estado de sessão têm uma cache separada, específica desse estado, que não é partilhada com outros utilizadores.
  • Se enviar um estado de sessão, mas a página redirecionar para o início de sessão, é devolvido login_expired e o pedido não é cobrado (a sessão expirou; volte a sincronizá-la com a extensão).
  • Nos sites cuja sessão está associada ao dispositivo ou ao IP e não pode ser reproduzida na nuvem (como os de banca online), use a opção «Capturar página atual» da extensão para tirar a captura no seu próprio navegador e enviá-la.

Cache

Os pedidos com parâmetros idênticos usam a cache (omitir um parâmetro equivale a enviar explicitamente o valor predefinido; os parâmetros de autenticação e de cache não entram na comparação). Quando há um resultado em cache, o modo binário devolve um redirecionamento 302 para o endereço do ficheiro, com Cache-Control; o navegador segue o redirecionamento e guarda o ficheiro em cache automaticamente. O modo JSON devolve cache_hit: true.

Os resultados obtidos da cache não são faturados.Pedidos simultâneos com os mesmos parâmetros geram apenas uma renderização.

Limites e faturação

  • Só são faturados os pedidos bem-sucedidos (2xx) que não usem a cache: 1 unidade por captura de ecrã e 2 por PDF.
  • Os pedidos com tempo limite excedido, falhas de renderização, erros ou bloqueios de acesso no site de destino, parâmetros inválidos ou falhas de autenticação não consomem quota.
  • A quota é reposta no início de cada mês (UTC). O plano gratuito inclui 100 unidades por mês; após esgotá-las, é devolvido quota_exceeded.
  • Exceder o limite de pedidos por segundo ou de pedidos simultâneos devolve 429. O cabeçalho de resposta Retry-After indica o tempo de espera recomendado em segundos.
  • Tempo máximo de renderização: 30 segundos; volume máximo de transferência por página: 50 MB; tamanho máximo do ficheiro gerado: 50 MB.

Consulte os limites de cada plano: Preços.

Códigos de erro

Todas as respostas de erro usam JSON:

Formato dos erros
{
  "error": {
    "code": "render_failed",
    "message": "target responded with an error status",
    "upstream_status": 404,
    "request_id": "req_01j…"
  }
}
HTTPcodeSignificadoFaturado
400invalid_paramsParâmetro em falta ou fora do intervalo permitido; message indica qualNão
400html_too_largehtml excede 2 MBNão
401unauthorizedAPI Key em falta, inválida ou desativadaNão
401signature_invalidA assinatura não corresponde (parâmetros alterados ou segredo incorreto)Não
401signature_expiredO prazo expires do URL assinado já expirouNão
403url_blockedO acesso ao endereço de destino não é permitido (por exemplo, endereços de rede privada ou localhost)Não
408render_timeoutA renderização excedeu 30 segundosNão
422render_failedO site de destino devolveu 4xx/5xx, houve uma falha de DNS ou um erro de certificado; inclui upstream_statusNão
422selector_not_foundselector não corresponde a nenhum elemento ou a espera por wait_for_selector excedeu o tempo limiteNão
422too_many_redirectsMais de 5 redirecionamentosNão
422site_blockedO site de destino apresentou uma verificação de segurança, um CAPTCHA, uma página de início de sessão obrigatório ou de acesso recusado, em vez do conteúdo normal; o pedido não é cobradoNão
422login_expiredFoi enviado um estado de sessão, mas o site de destino continua a mostrar a página de início de sessão, o que indica que a sessão expirou.Não
422proxy_failedNão foi possível ligar ao proxy indicado, a autenticação falhou ou o proxy não conseguiu aceder ao site de destino. message indica o motivo específico. O pedido não é cobrado.Não
429rate_limitedLimite de pedidos por segundo excedido; tente novamente de acordo com Retry-AfterNão
429concurrency_exceededLimite de pedidos simultâneos excedidoNão
429quota_exceededQuota mensal esgotada (plano gratuito)Não
429too_many_batchesMais de 5 tarefas em lote por concluir em simultâneoNão
500internalErro do servidor; contacte-nos e indique request_idNão

Lotes assíncronos

POST/v1/batch
GET/v1/batch/{id}

Envie 1–100 URL de uma só vez. O endpoint devolve imediatamente um ID de tarefa (HTTP 202) e as capturas são processadas uma a uma em segundo plano. Ideal para capturas em lote sem manter a ligação aberta. Pode consultar periodicamente o estado ou definir webhook_url para receber uma notificação quando tudo estiver concluído. A autenticação é igual à do endpoint síncrono (apenas por cabeçalho ou pelo parâmetro access_key; não suporta URL assinados).

ParâmetroTipoValor predefinidoDescrição
typeenumscreenshotscreenshot (captura de ecrã) ou pdf.
urlsstring[]—URL a processar, de 1–100. Se algum for inválido, o pedido inteiro devolve 400 e message indica a posição do URL.
optionsobject{}Parâmetros comuns a todos os URL, iguais aos da API síncrona (por exemplo, full_page, format, viewport_width, cache_ttl). Não podem incluir url, html, response_type, proxy.
webhook_urlstring—Opcional. Endereço a notificar quando tudo estiver concluído. Consulte «Notificação de conclusão».
Submeter tarefa em lote
curl -X POST "https://glanceshot.com/v1/batch" \
  -H "X-Access-Key: $GLANCESHOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "screenshot",
    "urls": ["https://example.com", "https://example.org"],
    "options": { "full_page": true, "format": "webp" },
    "webhook_url": "https://your-app.com/hooks/glanceshot"
  }'

# Devolve 202
{
  "id": "bat_01j…",
  "type": "screenshot",
  "status": "queued",
  "total": 2, "succeeded": 0, "failed": 0,
  "created_at": "2026-10-18T09:30:00Z", "completed_at": null,
  "webhook": { "url": "https://your-app.com/hooks/glanceshot", "status": "none", "attempts": 0 },
  "items": [
    { "index": 0, "url": "https://example.com", "status": "pending", "result": null, "error": null },
    { "index": 1, "url": "https://example.org", "status": "pending", "result": null, "error": null }
  ]
}

Consultar o progresso: o estado da tarefa, status, passa por queued (em fila), running (em processamento) e completed (tudo concluído, com ou sem sucesso). O estado de cada URL é pending, running, succeeded ou failed. Em caso de sucesso, result contém o endereço do ficheiro; em caso de falha, error contém o código de erro (igual ao do endpoint síncrono).

Consultar tarefa em lote
curl "https://glanceshot.com/v1/batch/bat_01j…" -H "X-Access-Key: $GLANCESHOT_KEY"

{
  "id": "bat_01j…",
  "status": "completed",
  "total": 2, "succeeded": 1, "failed": 1,
  "completed_at": "2026-10-18T09:30:12Z",
  "items": [
    {
      "index": 0, "url": "https://example.com", "status": "succeeded", "duration_ms": 1830,
      "result": { "url": "https://glanceshot.com/i/3f/3f9c….webp", "mime": "image/webp", "width": 1280, "height": 2410, "cache_hit": false },
      "error": null
    },
    {
      "index": 1, "url": "https://example.org", "status": "failed", "duration_ms": 640,
      "result": null,
      "error": { "code": "render_failed", "message": "target responded with an error status" }
    }
  ]
}
  • Aplicam-se as mesmas regras de faturação do endpoint síncrono: cada URL processado com sucesso consome 1 unidade por captura de ecrã ou 2 por PDF. Os resultados da cache e as falhas não consomem quota.
  • Se a quota já estiver esgotada ao submeter o pedido, é devolvido quota_exceeded de imediato. Se se esgotar durante o processamento, os URL restantes falham com quota_exceeded.
  • Cada conta pode ter até 5 tarefas em lote por concluir em simultâneo. Acima desse limite, é devolvido too_many_batches.
  • A validade dos ficheiros gerados é definida por options.cache_ttl (por predefinição, 1 dia). Transfira-os e guarde-os antes de expirarem.
  • Nos resultados da consulta e nas notificações, webhook.url é apresentado apenas até ao caminho, sem a cadeia de consulta.

Notificação de conclusão

Se definir webhook_url, enviaremos um pedido POST para esse endereço quando a tarefa estiver totalmente concluída. O corpo do pedido é JSON: { "event": "batch.completed", "batch": { … } }, em que batch é exatamente igual à resposta do endpoint de consulta. Cabeçalhos do pedido:

  • X-Glanceshot-Signature: t=1760779812,v1=5d3f…: t é a hora de envio (segundos Unix) e v1 é a assinatura
  • X-Glanceshot-Event: batch.completed, X-Glanceshot-Delivery-Attempt: 1 (número da tentativa de entrega)
Regras de assinatura
v1 = hex(HMAC-SHA256(secret, t + "." + Corpo original do pedido))
secret é o segredo de assinatura associado à API Key usada para submeter a tarefa (disponível na página API Key da consola).
  • Devolva uma resposta 2xx no prazo de 10 segundos; caso contrário, a entrega será considerada falhada. Em caso de falha, serão feitas novas tentativas após 10 segundos, 1 minuto e 5 minutos (até 4 tentativas no total).
  • Em casos muito raros, pode receber notificações duplicadas para a mesma tarefa. Use batch.id para eliminar duplicados.
  • Para verificar a assinatura, faça o cálculo com os bytes originais recebidos e confirme que a diferença entre t e a hora atual não excede alguns minutos, para evitar ataques de repetição.
  • O URL de callback deve ser um endereço http(s) acessível publicamente e não pode apontar para uma rede interna.
import crypto from "node:crypto";

// rawBody tem de conter os bytes originais recebidos, sem aplicar JSON.parse e voltar a serializar
function verifyGlanceshotWebhook(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // Proteção contra ataques de repetição
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const got = Buffer.from(parts.v1 ?? "");
  return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), got);
}

// verifyGlanceshotWebhook(req.rawBody, req.headers["x-glanceshot-signature"], GLANCESHOT_SECRET)

Consultar utilização

GET/v1/usage
shell
curl "https://glanceshot.com/v1/usage" -H "X-Access-Key: $GLANCESHOT_KEY"

{ "month": "202610", "used": 37, "limit": 100, "remaining": 63, "overage": 0, "resets_at": "2026-11-01T00:00:00Z", ... }

Verificação de estado

GET/v1/health

Não requer autenticação. Devolve o estado do nó e do conjunto de navegadores (total, ocupados e disponíveis).