Veezee

Datos de LinkedIn, Reddit y X para agentes de IA

Documentación

@veezee/sdk

Cliente REST tipado y CLI para la API de Veezee: datos de LinkedIn, Reddit y X (antes Twitter) para agentes de IA, medidos en créditos con recibos por llamada. Cero dependencias en tiempo de ejecución, construido sobre el fetch global de Node 20. Versión actual: 0.6.1.

La clave se gasta; iniciar sesión añade un token de gestión para la administración de claves. vz init crea una clave sin registro: esa clave por sí sola es suficiente para llamar a todos los endpoints de datos. vz login es un paso opcional y separado que verifica un correo electrónico y almacena un token de gestión vzm_ para listar, crear, revocar y rotar claves, y para establecer una alerta de saldo bajo. El primer inicio de sesión que confirma un correo reclamado en una clave de prueba también añade una concesión única de 10,000 créditos (una vez por correo, una vez por clave), que se gasta después del presupuesto diario gratuito.

Instalación

npm install @veezee/sdk

Se requiere Node 20+.

Inicio rápido

Comienza con un solo comando:

npx @veezee/sdk init

vz init (el mismo comando, más corto una vez instalado) hace POST a /v1/keys/mint sin registro y sin tarjeta, y escribe la clave devuelta en ~/.veezee/config (modo 0600). Nunca imprime la clave en bruto, solo una pista enmascarada: la clave vive en un archivo y en una variable de entorno, no en tu historial de terminal ni en la transcripción del chat de un agente. Después de eso, cada comando vz y cada llamada al SDK en el mismo entorno simplemente funciona:

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() es idempotente: reutiliza VEEZEE_API_KEY o ~/.veezee/config si cualquiera de ellos ya tiene una clave, y solo crea una nueva cuando ninguno la tiene. La clave de prueba en sí no lleva saldo; el nivel gratuito es una asignación por IP y por día (200 créditos/día, datos recientes, solo primera página) verificada contra la IP que llama, no contra la clave. Reclamar la clave añade un saldo gratuito adicional: adjunta un correo, confírmalo iniciando sesión en veezee.io/login, y la cuenta recibe una concesión única de 10,000 créditos (una vez por correo, una vez por clave) que se gasta con los mismos límites de prueba después de usar la asignación gratuita diaria. Si superas ambos, una llamada falla con TRIAL_CAP_EXCEEDED que lleva upgrade_url: compra créditos en veezee.io/upgrade (sin necesidad de iniciar sesión) y se aplican a la misma clave que ya tienes. Nada que reconfigurar: misma clave, mismo archivo de configuración, mismo código, ahora con un saldo real.

Puedes omitir vz init y pasar una clave directamente:

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

Autenticación

Cada llamada envía Authorization: Bearer <apiKey>. Una llamada sin clave resoluble lanza VeezeeError con code: "KEY_REQUIRED" y un mint_url: haz POST allí (sin autenticación, sin cuerpo) para crear una clave, o simplemente ejecuta vz init o llama a client.mint(), y luego reintenta. get_usage también necesita una clave siempre.

La superficie de la API

Las herramientas de plataforma viven bajo client.linkedin, client.reddit y client.x; las herramientas de cuenta/facturación (independientes de la plataforma) están en el nivel 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.

Cada respuesta es un sobre: { entity, platform, canonical_url, data_as_of, schema_version, common, platform_fields, freshness, usage }. usage.credits_charged y usage.receipt_id están en cada llamada; freshness te dice qué tan antiguos son los datos. Las llamadas con clave de prueba del nivel gratuito también llevan usage.free_tier_hint, que explica de qué presupuesto se tomó la llamada y que pagar mejora esta misma clave.

Manejo de errores

Las respuestas que no son 2xx lanzan un VeezeeError tipado (extiende Error) que lleva el cuerpo problem+json de la 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
    }
  }
}

El cliente reintenta RATE_LIMITED, CONCURRENCY_LIMIT, UPSTREAM_UNAVAILABLE, INTERNAL y fallos de red/tiempo de espera hasta 3 intentos con retroceso exponencial, respetando retry_after_seconds cuando está presente. Cada llamada medida recibe un Idempotency-Key (UUID), reutilizado en sus propios reintentos: los reintentos nunca cobran dos veces. Los errores terminales (entrada incorrecta, créditos insuficientes, autenticación) lanzan inmediatamente sin reintentar.

Los errores que un pago puede resolver también llevan upgrade_url (dáselo a tu humano), credits_required (en INSUFFICIENT_CREDITS: los créditos que la llamada necesitaba) y offer (tipado OfferV1): precios de paquetes, checkout_url (el mismo enlace que upgrade_url) y resume, que dice exactamente cómo reintentar después del pago.

Una llamada hecha sin clave resoluble lanza code: "KEY_REQUIRED" que lleva mint_url: ejecuta vz init o llama a client.mint(), y luego reintenta. Una llamada de gestión (listKeys, createKey, revokeKey, rotateKey, setBalanceAlert) hecha sin token de gestión resoluble lanza code: "AUTH_REQUIRED": ejecuta vz login, y luego reintenta.

CLI

El paquete incluye un binario veezee, con vz como alias corto para el mismo binario (sustantivo-verbo, refleja los métodos anteriores):

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>]

Ejecuta vz init primero (una vez): crea una clave de prueba gratuita y la escribe en ~/.veezee/config, solo se imprime una pista enmascarada en stdout, nunca la clave en bruto. vz init --json imprime el mismo resultado como JSON (aún sin clave en bruto) para scripting. vz init --force vuelve a crear y sobrescribe la clave almacenada.

Por ejemplo, vz reddit search "notion alternative" --type comments o vz x profile nasa. Cada espacio de nombres de plataforma también tiene su propio resolve-url: vz reddit resolve-url <url>, vz x resolve-url <url>.

Añade --json en cualquier comando para salida legible por máquina (el sobre en bruto; los errores imprimen la forma completa del error como JSON también). Cada comando de datos acepta --max-credits <n> para limitar el gasto en esa llamada: si la cotización excede el límite, la API devuelve un error tipado y no cobra nada. Las banderas desconocidas se rechazan con la lista de banderas válidas (salida 2), nunca se ignoran silenciosamente. La autenticación se resuelve en orden: --key <apiKey>, luego la variable de entorno VEEZEE_API_KEY, luego ~/.veezee/config de vz init. --base-url anula el host de la API. Códigos de salida: 0 éxito, 1 fallo, 2 error de uso o entrada inválida, 4 autenticación requerida. vz --version imprime la versión del SDK. Ejecuta vz --help (o sin argumentos) para ejemplos.

vz login es un paso separado y opcional: verifica un correo electrónico (devolución de llamada del navegador por defecto, o --device para un flujo de entrada de código sin interfaz) y almacena un token de gestión vzm_ en ~/.veezee/config, solo se imprime una pista enmascarada en stdout. vz keys list|create|revoke|rotate y vz alert <threshold|off> usan ese token, resuelto en orden: --key <vzm_...>, luego VEEZEE_MGMT_TOKEN, luego el token almacenado. vz keys create y vz keys rotate imprimen el api_key en bruto en stdout una vez (la advertencia de "mostrado una vez" va a stderr): guárdalo inmediatamente, no se puede recuperar de nuevo. vz logout borra el token almacenado localmente; no lo revoca en el servidor.

Habilidades del agente

El paquete incluye ocho paquetes SKILL.md instalables bajo skills/: prospect-enrich, candidate-sourcing, company-enrichment, account-research, post-voice-research, reddit-monitoring, launch-sentiment-sweep y outreach-list-builder. Después de npm install @veezee/sdk, agrégalos a tu agente con la sintaxis de ruta local de la CLI de habilidades:

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

Los mismos paquetes también se instalan directamente desde GitHub (veezeehq/veezee-skills): npx skills add veezeehq/veezee-skills. Índice de paquetes y documentación: veezee.io/docs/skills.

Desarrollo

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

CI debería ejecutar exactamente esta secuencia (npm ci && npm run build && npm test) en Node 20+; este paquete no tiene paso de lint separado ni llamadas de red en su suite de pruebas.

Documentación

Referencia completa de la API: https://veezee.io/docs. Página del SDK: https://veezee.io/docs/sdk. Página de la CLI: https://veezee.io/docs/cli. Servidores MCP alojados (mismas herramientas, sin instalación): https://veezee.io/docs/clients, listados en el Registro MCP como io.veezee/linkedin, io.veezee/reddit y io.veezee/x-twitter.