Uma chave autentica em todas as APIs do pacote: preencha PDFs com o Fillout e capture screenshots e PDFs da web com o Snapk. REST puro, chave no header, sem SDK obrigatório.
O Fillout é o caminho mais curto para ver a API funcionando: envie um PDF com campos de formulário e um JSON, receba o PDF preenchido de volta.
Troque hk_live_… pela sua chave e o arquivo pelo seu PDF. Pronto.
# Preenche um PDF e salva o resultado em preenchido.pdf
curl -X POST https://fillout.haruo.dev/api/v1/fill \
-H "x-api-key: hk_live_..." \
-F "[email protected]" \
-F 'data={"nome":"Ana Souza","cpf":"123.456.789-00","cidade":"São Paulo"}' \
-o preenchido.pdfA resposta é o PDF preenchido (application/pdf), entregue como anexo — por isso o -o preenchido.pdf. Não tem um PDF com campos à mão? Comece pelo detect-fields para ver quais campos o seu PDF expõe.
Toda chamada às rotas /api/ exige a sua chave. Envie-a no header x-api-key ou como Authorization: Bearer — as duas formas são aceitas.
curl https://fillout.haruo.dev/api/v1/detect-fields \
-H "x-api-key: hk_live_..." \
-F "[email protected]"curl https://fillout.haruo.dev/api/v1/detect-fields \
-H "Authorization: Bearer hk_live_..." \
-F "[email protected]"Crie a conta em /auth/signup e a chave aparece no seu painel como hk_live_…. Ao regenerar a chave, a anterior é revogada na hora e passa a responder 403 — atualize seus serviços com a nova. Guarde a chave em variável de ambiente e nunca a exponha em código de cliente (navegador ou app).
Fillout e Snapk não são assinaturas separadas. A mesma chave funciona nos dois, e as chamadas somam numa única cota mensal combinada.
hk_live_… autentica no Fillout e no Snapk. Sem contas ou credenciais por serviço.
Um limite mensal compartilhado entre os produtos. Só requisições bem-sucedidas (status < 400) contam.
Quando uma nova API entra no pacote, sua chave já funciona nela — sem migração.
| Plano | Preço | Cota mensal combinada | Rate limit |
|---|---|---|---|
| Free | $0 | 100 req / mês | padrão |
| Pro popular | $9 / mês | 10.000 req / mês | 60 req / min |
| Enterprise | sob medida | ilimitada | dedicado |
Contas novas começam com 14 dias em limites de nível Pro. Depois, sem assinatura, a chave passa a valer os limites do Free.
Base URL https://fillout.haruo.dev. Preenche campos de formulário AcroForm — PDFs achatados ou escaneados (sem campos) retornam 422.
/api/v1/fillEnvia um PDF com campos AcroForm mais um JSON de valores e retorna o PDF preenchido. Requisição multipart/form-data.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
fileobrigatório | arquivo (PDF) | O PDF a preencher, enviado como parte de arquivo do multipart. |
dataobrigatório | string (JSON) | Objeto JSON campo→valor, ex.: {"nome":"Ana"}. Enviado como campo de texto do multipart. |
Requisição
curl -X POST https://fillout.haruo.dev/api/v1/fill \
-H "x-api-key: hk_live_..." \
-F "[email protected]" \
-F 'data={"nome":"Ana Souza","cpf":"123.456.789-00"}' \
-o preenchido.pdfResposta
Content-Type: application/pdf
Content-Disposition: attachment; filename="filled-contrato.pdf"
<bytes do PDF preenchido>Retorna o binário do PDF, não JSON. PDF sem campos AcroForm → 422 UnprocessableEntity.
/api/v1/detect-fieldsEnvia um PDF e recebe a lista de campos AcroForm que ele expõe — útil para descobrir os nomes que você vai passar em /fill.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
fileobrigatório | arquivo (PDF) | O PDF a inspecionar, como parte de arquivo do multipart. |
Requisição
curl -X POST https://fillout.haruo.dev/api/v1/detect-fields \
-H "x-api-key: hk_live_..." \
-F "[email protected]"Resposta
{
"fields": [
{ "name": "nome", "type": "text", "rect": [72, 700, 320, 720], "pageNumber": 1 },
{ "name": "cpf", "type": "text", "rect": [72, 660, 320, 680], "pageNumber": 1 },
{ "name": "aceito", "type": "checkbox", "rect": [72, 620, 90, 638], "pageNumber": 1 }
],
"fieldCount": 3
}type é um de: "text", "checkbox", "radio", "dropdown", "signature". rect e pageNumber podem ser null.
/api/v1/fill-with-templatePreenche um template de PDF já armazenado (por ID) com valores em JSON — sem reenviar o arquivo a cada chamada.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
templateIdobrigatório | string | ID do template armazenado. |
dataobrigatório | object | Objeto JSON campo→valor. |
flattenopcional | boolean | Achatar o formulário no resultado. Padrão: true. |
Requisição
curl -X POST https://fillout.haruo.dev/api/v1/fill-with-template \
-H "x-api-key: hk_live_..." \
-H "Content-Type: application/json" \
-d '{"templateId":"tpl_123","data":{"nome":"Ana Souza"},"flatten":true}' \
-o preenchido.pdfResposta
Content-Type: application/pdf
Content-Disposition: attachment; filename="filled-<template>.pdf"
<bytes do PDF preenchido>Existe também /api/v1/fill-with-template/hbs, que aplica o field mapping do template via Handlebars antes de preencher. Mesmo corpo, mesma resposta.
/api/v1/templatesCria um template: envia o PDF e os metadados (multipart). Reuse o ID retornado em /fill-with-template.
Requisição
curl -X POST https://fillout.haruo.dev/api/v1/templates \
-H "x-api-key: hk_live_..." \
-F "[email protected]" \
-F "name=Contrato padrão" \
-F "description=Modelo de contrato de prestação de serviços"Resposta
{
"id": "tpl_123",
"name": "Contrato padrão",
"description": "Modelo de contrato de prestação de serviços",
"field_mapping": "{}",
"created_at": "2026-07-24T12:00:00.000Z"
}Status 201 Created. GET /api/v1/templates lista todos os templates.
/api/v1/templates/{id}Gerencia um template individual: GET retorna seus metadados (id, name, description, pdf_path, field_mapping, created_at, updated_at); PUT atualiza name, description e fieldMapping (JSON); DELETE remove e responde { "success": true }.
Base URL https://snapk.haruo.dev. Renderização via navegador headless. Sob carga alta de Chromium, as rotas podem responder 503 com Retry-After.
/api/v1/screenshotCaptura o screenshot de uma URL e retorna a imagem PNG.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
urlobrigatório | string | A URL a capturar. |
widthopcional | integer | Largura da viewport (1–7680). Padrão: 1280. |
heightopcional | integer | Altura da viewport (1–4320). Padrão: 720. |
fullPageopcional | boolean | Capturar a página inteira. Padrão: false. |
Requisição
curl -X POST https://snapk.haruo.dev/api/v1/screenshot \
-H "x-api-key: hk_live_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","width":1280,"fullPage":true}' \
-o captura.pngResposta
Content-Type: image/png
<bytes do PNG>Retorna o binário do PNG. 400 se a URL for inválida; 503 (com Retry-After e retryAfterMs) se a fila do Chromium estiver cheia.
/api/v1/pdfRenderiza HTML como um PDF. Informe html direto ou o nome de um template .hbs; data preenche placeholders {{chave}}.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
htmlopcional | string | HTML a renderizar. Obrigatório se template não for informado. |
templateopcional | string | Nome de um template .hbs no servidor. Alternativa a html. |
dataopcional | object | Valores para substituir placeholders {{chave}}. |
optionsopcional | object | Opções de página: format (A4), landscape, margin, printBackground, scale. |
Requisição
curl -X POST https://snapk.haruo.dev/api/v1/pdf \
-H "x-api-key: hk_live_..." \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Fatura</h1><p>Total: R$ 100</p>","options":{"format":"A4"}}' \
-o documento.pdfResposta
Content-Type: application/pdf
Content-Disposition: attachment; filename="<timestamp>.pdf"
<bytes do PDF>Informe html ou template — sem nenhum dos dois, retorna 400. Por segurança, o HTML é renderizado sem acesso à rede (apenas conteúdo inline/data:).
/api/v1/url-to-pdfAbre uma URL num navegador headless e retorna a página como PDF.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
urlobrigatório | string | A URL a converter em PDF. |
optionsopcional | object | Opções de página (format, landscape, margin, printBackground, scale, waitUntil). |
Requisição
curl -X POST https://snapk.haruo.dev/api/v1/url-to-pdf \
-H "x-api-key: hk_live_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","options":{"format":"A4","landscape":false}}' \
-o pagina.pdfResposta
Content-Type: application/pdf
<bytes do PDF>URLs internas/privadas são bloqueadas (proteção contra SSRF) e retornam 400.
Erros vêm como JSON no formato { statusCode, error, message }. Os campos de autenticação e cota são resolvidos de forma central, iguais para todas as APIs do pacote.
| Status | Significado | Quando acontece |
|---|---|---|
400 | Bad Request | Corpo/arquivo inválido: JSON malformado no campo data, PDF inválido, URL faltando ou bloqueada. |
401 | Unauthorized | Chave ausente ("Missing API key") ou desconhecida ("Invalid API key"). |
403 | Forbidden | A chave foi revogada ("API key has been revoked") — regenere e atualize seus serviços. |
422 | Unprocessable Entity | Só no Fillout: o PDF não tem campos AcroForm preenchíveis (fieldCount: 0). |
429 | Too Many Requests | Cota mensal combinada esgotada. Traz plan, limit, used, resetAt e os headers abaixo. |
502 | Bad Gateway | Só no Snapk: a renderização (screenshot/PDF) falhou. |
503 | Service Unavailable | Só no Snapk: fila do Chromium cheia. Traz Retry-After e retryAfterMs; repita depois. |
Requisições autenticadas retornam quanto da cota combinada resta. No 429 vem também Retry-After (em segundos).
X-Quota-Limit: 10000
X-Quota-Used: 42
X-Quota-Reset: 2026-08-01T00:00:00.000ZAo estourar a cota, além dos headers, o corpo detalha o plano e o momento do reset.
{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Monthly quota exceeded for the 'free' plan (100 requests). Upgrade your plan or wait for the quota to reset.",
"plan": "free",
"limit": 100,
"used": 100,
"resetAt": "2026-08-01T00:00:00.000Z"
}A cota é combinada e mensal: chamadas ao Fillout e ao Snapk somam no mesmo limite, e só requisições bem-sucedidas são contabilizadas. Precisa de mais volume? O Pro sobe para 10.000 req/mês.
Conta grátis, sem cartão de crédito. 100 requisições por mês para começar — uma chave para os dois produtos.
Criar conta grátis