Veezee

Dados do LinkedIn, Reddit e X para agentes de IA

Documentação

@veezee/sdk

Cliente REST tipado e CLI para a API Veezee: dados de LinkedIn, Reddit e X (antigo Twitter) para agentes de IA, medidos em créditos com recibos por chamada. Zero dependências em tempo de execução, construído sobre o fetch global do Node 20. Versão atual: 0.6.1.

A chave é o que gasta; fazer login adiciona um token de gerenciamento para administração de chaves. vz init cria uma chave sem cadastro: essa chave sozinha é suficiente para chamar todos os endpoints de dados. vz login é uma etapa separada e opcional que comprova um e-mail e armazena um token de gerenciamento vzm_ para listar, criar, revogar e rotacionar chaves, e para definir um alerta de saldo baixo. O primeiro login que confirma um e-mail reivindicado em uma chave de teste também adiciona uma concessão única de 10.000 créditos (uma vez por e-mail, uma vez por chave), gasta após o orçamento diário gratuito.

Instalação

npm install @veezee/sdk

Node 20+ é necessário.

Início rápido

Comece com um único comando:

npx @veezee/sdk init

vz init (mesmo comando, mais curto após a instalação) faz POST para /v1/keys/mint sem cadastro e sem cartão, e grava a chave retornada em ~/.veezee/config (modo 0600). Ele nunca imprime a chave bruta, apenas uma dica mascarada: a chave fica em um arquivo e em uma variável de ambiente, não no seu histórico de terminal ou na transcrição de chat de um agente. Depois disso, todo comando vz e toda chamada do SDK no mesmo ambiente simplesmente funcionam:

import { VeezeeClient } from "@veezee/sdk";

const client = new VeezeeClient();
await client.mint(); // reuses the key from `vz init`, or mints one the first time
const profile = await client.linkedin.getProfile({ identifier: "williamhgates" });
console.log(profile.common.full_name, profile.common.headline);

mint() é idempotente: ele reutiliza VEEZEE_API_KEY ou ~/.veezee/config se algum deles já tiver uma chave, e só cria uma nova quando nenhum dos dois tem. A chave de teste em si não carrega saldo; o nível gratuito é uma cota por IP, por dia (200 créditos/dia, dados recentes, apenas primeira página) verificada contra o IP de chamada, não contra a chave. Reivindicar a chave adiciona um saldo gratuito por cima: anexe um e-mail, confirme-o fazendo login em veezee.io/login, e a conta recebe uma concessão única de 10.000 créditos (uma vez por e-mail, uma vez por chave) que gasta nos mesmos limites de teste após a cota gratuita diária ser usada. Supere ambos e uma chamada falha com TRIAL_CAP_EXCEEDED carregando upgrade_url: compre créditos em veezee.io/upgrade (sem necessidade de login) e eles caem na mesma chave que você já tem. Nada para reconfigurar: mesma chave, mesmo arquivo de configuração, mesmo código, agora com saldo real.

Você pode pular o vz init e passar uma chave diretamente:

const client = new VeezeeClient({ apiKey: process.env.VEEZEE_API_KEY });

Autenticação

Toda chamada envia Authorization: Bearer <apiKey>. Uma chamada sem chave resolvível lança VeezeeError com code: "KEY_REQUIRED" e um mint_url: faça POST lá (sem autenticação, sem corpo) para criar uma chave, ou apenas execute vz init ou chame client.mint(), e então tente novamente. get_usage também sempre precisa de uma chave.

A superfície da API

As ferramentas de plataforma ficam em client.linkedin, client.reddit e client.x; as ferramentas de conta/cobrança (independentes de plataforma) são de nível superior:

await client.linkedin.getProfile({ identifier: "williamhgates", sections: ["experience", "education"] });
// -> PersonEnvelope { common: { full_name, headline, experience, ... } }

await client.linkedin.searchPeople({ keywords: "CTO", current_company: "anthropic", limit: 20 });
// -> PeopleSearchEnvelope { common: { results, cursor, total_matches } }

await client.linkedin.getCompany({ identifier: "microsoft" });
// -> CompanyEnvelope { common: { name, industry, employee_count, ... } }

await client.linkedin.getPosts({ identifier: "microsoft" });
// -> PostsEnvelope { common: { results, cursor, author_type } }

await client.reddit.search({ query: "notion alternative", type: "comments" });
// -> RedditSearchEnvelope { common: { type: "comments", comments, cursor, returned_count } }

await client.reddit.getSubredditPosts({ subreddit_name: "selfhosted", sort: "new" });
// -> RedditPostsEnvelope { common: { results, cursor, returned_count } }

await client.x.getProfile({ identifier: "nasa" });
// -> XProfileEnvelope { common: { screen_name, name, followers, description, ... } }

await client.x.search({ query: "veezee api", type: "recent" });
// -> XSearchEnvelope { common: { type: "recent", tweets, cursor, returned_count } }

await client.resolveUrl({ url: "https://www.linkedin.com/in/williamhgates" });
// -> UrlResolutionEnvelope { common: { type: "person", id, handle, canonical_url } }
// client.reddit.resolveUrl and client.x.resolveUrl work the same way for their own URLs.

await client.getUsage();
// -> UsageEnvelope { common: { plan, platforms, balance_remaining, recent_receipts, upgrade_url, manage_url } }

await client.checkout({ pack: "flex" | "production" });
// -> { checkout_url }. Needs no key: paying with none mints a fresh account and its key.

Toda resposta é um envelope: { entity, platform, canonical_url, data_as_of, schema_version, common, platform_fields, freshness, usage }. usage.credits_charged e usage.receipt_id estão em toda chamada; freshness informa a idade dos dados. Chamadas de chave de teste no nível gratuito também carregam usage.free_tier_hint, explicando de qual orçamento a chamada usou e que pagar faz upgrade desta mesma chave.

Tratamento de erros

Respostas não-2xx lançam um VeezeeError tipado (estende Error) carregando o corpo problem+json da API:

import { VeezeeError } from "@veezee/sdk";

try {
  await client.linkedin.getProfile({ identifier: "someone" });
} catch (err) {
  if (err instanceof VeezeeError) {
    console.error(err.code, err.message); // message is a next-turn instruction
    if (err.is_retriable) {
      // safe to retry as-is; the client already retried transient errors internally
    }
  }
}

O cliente tenta novamente RATE_LIMITED, CONCURRENCY_LIMIT, UPSTREAM_UNAVAILABLE, INTERNAL e falhas de rede/timeout até 3 tentativas com backoff exponencial, respeitando retry_after_seconds quando presente. Toda chamada medida recebe um Idempotency-Key (UUID), reutilizado em suas próprias tentativas: tentativas nunca cobram em dobro. Erros terminais (entrada inválida, créditos insuficientes, autenticação) lançam imediatamente sem tentar novamente.

Erros que um pagamento pode resolver também carregam upgrade_url (entregue ao seu humano), credits_required (em INSUFFICIENT_CREDITS: os créditos que a chamada precisava) e offer (tipado OfferV1): preços de pacotes, checkout_url (o mesmo link que upgrade_url) e resume, que diz exatamente como tentar novamente após o pagamento.

Uma chamada feita sem chave resolvível lança code: "KEY_REQUIRED" carregando mint_url: execute vz init ou chame client.mint(), e então tente novamente. Uma chamada de gerenciamento (listKeys, createKey, revokeKey, rotateKey, setBalanceAlert) feita sem token de gerenciamento resolvível lança code: "AUTH_REQUIRED": execute vz login, e então tente novamente.

CLI

O pacote inclui um binário veezee, com vz como um alias curto para o mesmo binário (substantivo-verbo, espelha os métodos acima):

veezee init [--force]
veezee linkedin profile get <identifier> [--sections a,b] [--realtime]
veezee linkedin search [--keywords x] [--title x] [--company x] [--past-company x] [--school x] [--first-name x] [--last-name x] [--limit n] [--cursor x] [--freshness x]
veezee linkedin company get <identifier>
veezee linkedin posts get <identifier>
veezee reddit search <query> [--type posts|comments|subreddits|users] [--sort x] [--range x]
veezee reddit subreddit <name> [--include-settings]
veezee reddit subreddit-posts <name> [--sort x] [--range x] [--include-promoted]
veezee reddit user <username> [--sections comments,posts,subreddits]
veezee reddit post <id1,id2,...> [--detail concise|full] [--comment-id x]
veezee x search <query> [--type recent|popular|people]
veezee x profile <identifier> [--by screen_name|id]
veezee x tweets <identifier> [--mode posts|posts_and_replies|highlights] [--no-retweets]
veezee x tweet <tweet_id>
veezee resolve-url <url>
veezee usage
veezee login [--device] [--no-browser]
veezee logout
veezee keys list [--account <id>]
veezee keys create [--label <name>] [--account <id>]
veezee keys revoke <key_id> [--account <id>]
veezee keys rotate <key_id> [--account <id>]
veezee alert <threshold|off> [--account <id>]

Execute vz init primeiro (uma vez): ele cria uma chave de teste gratuita e a grava em ~/.veezee/config, apenas dica mascarada impressa no stdout, nunca a chave bruta. vz init --json imprime o mesmo resultado como JSON (ainda sem chave bruta) para scripts. vz init --force recria e sobrescreve a chave armazenada.

Por exemplo, vz reddit search "notion alternative" --type comments ou vz x profile nasa. Cada namespace de plataforma também tem seu próprio resolve-url: vz reddit resolve-url <url>, vz x resolve-url <url>.

Adicione --json em qualquer comando para saída legível por máquina (o envelope bruto; erros imprimem a forma completa do erro como JSON também). Todo comando de dados aceita --max-credits <n> para limitar o gasto nessa chamada: se a cotação exceder o limite, a API retorna um erro tipado e não cobra nada. Flags desconhecidas são rejeitadas com a lista de flags válidas (exit 2), nunca ignoradas silenciosamente. A autenticação resolve nesta ordem: --key <apiKey>, depois a variável de ambiente VEEZEE_API_KEY, depois ~/.veezee/config de vz init. --base-url substitui o host da API. Códigos de saída: 0 sucesso, 1 falha, 2 erro de uso ou entrada inválida, 4 autenticação necessária. vz --version imprime a versão do SDK. Execute vz --help (ou sem argumentos) para exemplos.

vz login é uma etapa separada e opcional: ela comprova um e-mail (callback de navegador por padrão, ou --device para um fluxo de entrada de código sem interface gráfica) e armazena um token de gerenciamento vzm_ em ~/.veezee/config, apenas dica mascarada impressa no stdout. vz keys list|create|revoke|rotate e vz alert <threshold|off> usam esse token, resolvido nesta ordem: --key <vzm_...>, depois VEEZEE_MGMT_TOKEN, depois o token armazenado. vz keys create e vz keys rotate imprimem o api_key bruto no stdout uma vez (o aviso "mostrado uma vez" vai para o stderr): armazene-o imediatamente, ele não pode ser recuperado novamente. vz logout limpa o token armazenado localmente; não o revoga no servidor.

Habilidades do agente

O pacote inclui oito pacotes instaláveis de SKILL.md em skills/: prospect-enrich, candidate-sourcing, company-enrichment, account-research, post-voice-research, reddit-monitoring, launch-sentiment-sweep e outreach-list-builder. Após npm install @veezee/sdk, adicione-os ao seu agente com a sintaxe de caminho local da CLI de skills:

npx skills add ./node_modules/@veezee/sdk/skills

Os mesmos pacotes também instalam diretamente do GitHub (veezeehq/veezee-skills): npx skills add veezeehq/veezee-skills. Índice de pacotes e documentação: veezee.io/docs/skills.

Desenvolvimento

npm install
npm run build   # tsc -> dist/
npm test        # compiles test/ and runs node --test
npm pack --dry-run

O CI deve executar exatamente esta sequência (npm ci && npm run build && npm test) no Node 20+; este pacote não tem etapa separada de lint e não faz chamadas de rede em sua suíte de testes.

Documentação

Referência completa da API: https://veezee.io/docs. Página do SDK: https://veezee.io/docs/sdk. Página da CLI: https://veezee.io/docs/cli. Servidores MCP hospedados (mesmas ferramentas, sem instalação): https://veezee.io/docs/clients, listados no Registro MCP como io.veezee/linkedin, io.veezee/reddit e io.veezee/x-twitter.