Referência da API

API REST
Construa sobre seus links.

Autentique com um token de API, liste e crie links, leia visitas e assine webhooks.

URL base https://letl.ink/api/v1 Somente HTTPS

Planos Growth Ultimate
Nesta página

Primeiros passos

A API REST está disponível no Growth e no Ultimate. As requisições vão para https://letl.ink/api/v1 via HTTPS. Um espaço de trabalho no Free ou no Solo recebe 403 plan_required de /api/v1; o endpoint MCP é liberado separadamente no Ultimate.

  1. Abra Integrações → API REST (apenas proprietários e administradores do espaço de trabalho).
  2. Gere um token. Ele não expira.
  3. Guarde-o como uma senha. Um único token atende a API REST, o servidor MCP e todas as plataformas de automação conectadas a este espaço de trabalho, então gerá-lo novamente invalida todos de uma vez.

Os membros podem copiar um token existente, se já tiver sido gerado; apenas proprietários e administradores podem trocá-lo.

Autenticação

Envie o token como X-API-KEY ou como Authorization: Bearer. Os dois são aceitos em /api/v1 e em /mcp. Uma requisição sem nenhum token retorna 401 unauthenticated.

GET Quem sou eu?
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me

Cabeçalho equivalente: Authorization: Bearer YOUR_TOKEN

Convenções

Isto vale para todos os endpoints abaixo.

Envelope de resposta

Toda resposta bem-sucedida é um objeto JSON com uma chave data e uma chave meta quando há algo a dizer sobre o resultado — paginação, o período de datas resolvido, se a geografia foi incluída. Toda falha é um objeto com uma chave error contendo um code estável, uma message legível e, às vezes, details. Use o code para decidir o fluxo, nunca o texto da mensagem.

Formas
// success
{ "data": { … }, "meta": { … } }

// failure
{ "error": { "code": "invalid_parameter",
             "message": "sort must be one of: created_at, clicks.",
             "details": { "parameter": "sort" } } }

Paginação

Os endpoints de listagem aceitam limit (1–200, padrão 50) e offset. O bloco meta informa total, has_more e next_offset, para você paginar sem fazer contas: continue chamando com offset = meta.next_offset até has_more ser false.

Paginando todos os links
offset=0
while [ "$offset" != "null" ]; do
  page=$(curl -s -H "X-API-KEY: $TOKEN" \
    "https://letl.ink/api/v1/links?limit=200&offset=$offset")
  echo "$page" | jq -c '.data[]'
  offset=$(echo "$page" | jq -r '.meta.next_offset')
done

Ordenação, filtros e busca

GET /links aceita sort (created_at, clicks, short_code, title, updated_at), direction (asc/desc), search (busca no código curto, título e destino), type, archived, favorite e domain. Valores desconhecidos são rejeitados com invalid_parameter e a lista do que é permitido, em vez de serem ignorados silenciosamente.

Períodos de datas

start_date e end_date usam YYYY-MM-DD e incluem os dois dias. O período é limitado à retenção do seu plano — pedir um ano de histórico em um plano com 30 dias de retenção retorna os 30 dias disponíveis, e meta.date_range informa o período realmente usado.

Limites de taxa

600 requisições por minuto por conta, em janela deslizante. Toda resposta traz X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (um timestamp Unix). Exceder o limite retorna 429 rate_limited com um cabeçalho Retry-After em segundos. Aguarde o tempo indicado em Retry-After em vez de tentar de novo imediatamente.

Cabeçalhos em todas as respostas
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 586
X-RateLimit-Reset: 1789000816

Versionamento

O caminho inclui a versão. Adicionar um campo a uma resposta ou um novo parâmetro opcional não é uma mudança incompatível e pode acontecer a qualquer momento, então interprete de forma defensiva e ignore chaves que você não reconhece. Qualquer coisa que altere ou remova um campo existente é lançada com um novo prefixo de versão.

Endpoints

Conta

GET /me Teste de conexão

Identidade, espaço de trabalho, plano e todos os direitos que esse plano inclui. A forma mais barata de verificar um token e descobrir o que a conta pode fazer antes de tentar.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/me
Resposta 200
{
  "data": {
    "user": { "id": 1, "email": "[email protected]", "name": "You" },
    "workspace": { "id": 12, "name": "My Workspace", "block_bots": true },
    "plan": {
      "id": "growth",
      "name": "Growth",
      "limits": {
        "links": 200,
        "monthly_clicks": 100000,
        "custom_domains": 3,
        "qr_codes": 30,
        "retention_days": 365
      },
      "features": {
        "api": true,
        "webhooks": true,
        "integrations": true,
        "ai_access": false,
        "geo_analytics": false,
        "smart_link_types": true
      }
    },
    "base_domain": "letl.ink"
  }
}
GET /usage

Quanto de cada cota foi consumido neste mês de cobrança. Use para avisar um cliente antes que ele atinja o teto de cliques rastreados — depois disso o redirecionamento continua funcionando, mas os cliques deixam de ser registrados.

Resposta 200
{
  "data": {
    "links": { "used": 42, "limit": 200, "remaining": 158, "percent_used": 21.0 },
    "clicks_this_month": { "used": 8140, "limit": 100000, "remaining": 91860, "percent_used": 8.1 },
    "custom_domains": { "used": 1, "limit": 3, "remaining": 2, "percent_used": 33.3 },
    "qr_codes": { "used": 4, "limit": 30, "remaining": 26, "percent_used": 13.3 }
  },
  "meta": { "plan": "growth", "period": "2026-09" }
}
GET /domains

Domínios personalizados do espaço de trabalho e se cada um já concluiu a verificação de DNS. Só domínios verificados podem ser usados no campo domain ao criar um link.

GET /integrations

Todas as integrações que o produto oferece, se o seu plano as inclui, se este espaço de trabalho as conectou e como está a entrega — último status, último erro e número de falhas consecutivas. Útil para monitorar vários espaços de trabalho.

Links

Análises

GET /analytics/summary

Os números principais de todo o espaço de trabalho: cliques desde o início e no período, visitantes únicos, leituras de QR, dia de pico e distribuições por hora do dia e dia da semana. Aceita days (padrão 30, limitado à sua retenção).

Resposta 200 (resumido)
{
  "data": {
    "window_days": 30,
    "clicks": {
      "total": 24592, "window": 8140, "last_7_days": 2103,
      "daily_average": 271,
      "peak_day": { "date": "Sep 02", "clicks": 612 }
    },
    "visitors": { "unique_total": 18204, "unique_window": 6021 },
    "qr": { "scans_total": 940, "scans_window": 310 },
    "timing": {
      "by_hour": [12, 4, 2, …],
      "by_weekday": [1420, 1610, 1585, 1490, 1310, 480, 245],
      "peak_hour": { "peakHour": 14, "peakValue": 812, "label": "14:00–14:59" },
      "peak_weekday": { "peakDay": 1, "peakValue": 1610, "label": "Tuesday" }
    },
    "links": { "active": 42, "limit": 200 }
  },
  "meta": { "plan": "growth", "geo_included": true, "retention_days": 365 }
}
GET /analytics/timeseries

Contagem diária de cliques, pronta para gráficos. Passe link_id para limitar a um link e days para definir o período. Dias sem tráfego são retornados como zero em vez de omitidos, então a série é sempre contínua.

Resposta 200
{
  "data": [
    { "date": "Sep 04", "clicks": 22 },
    { "date": "Sep 05", "clicks": 11 }
  ],
  "meta": { "window_days": 7, "total": 33, "link_id": null }
}
GET /analytics/breakdown

Cliques agrupados por uma dimensão, com a participação de cada linha no total.

Parâmetros de consulta

  • dimension string

    channel, referrer, device, browser, os, brand, variation ou country. browser, os, brand e variation exigem um link_id.

    Obrigatório
  • link_id integer

    Limita a um link. Omita para todo o espaço de trabalho.

    Opcional
  • limit integer

    1–100. O padrão é 10.

    Opcional
Resposta 200
{
  "data": [
    { "label": "Search", "value": 215, "share": 33.1 },
    { "label": "Social", "value": 201, "share": 30.9 },
    { "label": "Direct / none", "value": 142, "share": 21.8 }
  ],
  "meta": { "dimension": "channel", "link_id": 49, "total": 650 }
}
GET /events Paginado

O feed bruto de visitas de todos os links do espaço de trabalho, das mais recentes para as mais antigas. É o substituto moderno de GET /visits: os mesmos dados, envelope padrão e suporte a limit e offset.

Resposta 200
{
  "data": [
    {
      "id": 2691,
      "link_id": 49,
      "short_code": "spring",
      "occurred_at": "2026-09-10T22:45:33+00:00",
      "ip_address": "203.0.113.7",
      "referrer": "https://twitter.com/",
      "browser": "Chrome",
      "os": "iOS",
      "device_type": "mobile",
      "device_brand": "Apple",
      "country": "US",
      "variation": "control"
    }
  ],
  "meta": { "limit": 50, "returned": 50, "geo_included": true }
}

Webhooks

GET /hooks

Endpoints de webhook registrados nesta conta.

POST /hooks

Registre um endpoint HTTPS para receber um payload JSON a cada clique registrado. Até 25 por conta. Entregas que falham são repetidas automaticamente.

Corpo JSON

  • target_url

    Deve ser https://.

    Obrigatório
  • event

    O padrão é link_visited, que atualmente é o único evento.

    Opcional
POST cURL
curl -X POST https://letl.ink/api/v1/hooks \
  -H "X-API-KEY: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_url": "https://example.com/hooks/clicks"}'
DELETE /hooks/{id}

Remove um endpoint de webhook. POST /hooks/subscribe e DELETE /hooks/unsubscribe/{id} continuam disponíveis para o fluxo de REST Hooks do Zapier.

Legado

GET /auth/me Legado

O teste de conexão original: retorna seu id de usuário, e-mail e id/nome do espaço de trabalho como um objeto plano. GET /me retorna a mesma identidade mais os limites do seu plano e os recursos disponíveis, e é a melhor escolha para código novo.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me
Resposta 200
{
  "id": 1,
  "email": "[email protected]",
  "workspace_id": 12,
  "workspace_name": "My Workspace"
}
GET /visits Legado

As 50 visitas mais recentes em todo o espaço de trabalho, como um array JSON puro — sem envelope —, usando os nomes de campo visit_id, visit_time, link_shortcode e visitor_*. É isso que a etapa "perform list" do REST Hook do Zapier lê, então o formato está congelado. Use GET /events para novos desenvolvimentos.

POST /hooks/subscribe Zapier

Assinatura de REST Hook. Recebe target_url e um event opcional (padrão link_visited) e retorna a assinatura criada de forma plana, sem envelope. POST /hooks é a mesma operação no formato padrão.

DELETE /hooks/unsubscribe/{id} Zapier

Cancelamento de REST Hook, retornando {"success": true}. DELETE /hooks/{id} é o equivalente no formato padrão.

Erros

  • 400 invalid_request

    O corpo não era um objeto JSON ou faltava um campo obrigatório.

  • 400 invalid_parameter

    Um parâmetro de consulta estava malformado ou fora do conjunto permitido. details.parameter indica qual.

  • 401 unauthenticated

    Nenhum token foi enviado. Adicione X-API-KEY ou Authorization: Bearer.

  • 401 invalid_token

    O token não corresponde a nenhuma conta. Ele pode ter sido gerado novamente.

  • 403 plan_required

    O plano da conta não inclui este endpoint ou recurso. details.upgrade_url aponta para a cobrança.

  • 403 forbidden

    Autenticado, mas sem permissão — por exemplo, um membro tentando uma ação exclusiva do proprietário.

  • 404 not_found

    Esse recurso não existe neste espaço de trabalho. IDs de outro espaço de trabalho são informados como não encontrados, e não como proibidos.

  • 409 conflict

    A requisição conflita com o estado atual — o limite de webhooks foi atingido ou a conta ainda não tem espaço de trabalho.

  • 422

    Retornado por POST /links/bulk quando todas as linhas do lote falharam. Consulte meta.errors.

  • 429 rate_limited

    Mais de 600 requisições no último minuto. Aguarde Retry-After segundos.

Os endpoints legados GET /visits e GET /links/{id}/visits retornam um array JSON puro com nomes de campo próprios e estão congelados para as integrações do Zapier já construídas sobre eles. Código novo deve usar GET /events e GET /links/{id}/events, que usam o envelope padrão e suportam paginação.

Ainda tem dúvidas?

Estamos aqui para ajudar. Entre em contato e responderemos assim que possível.