Sniffington
Servidor MCP protegido por OAuth para acceder a las menciones públicas de marca y acciones compatibles de un espacio de trabajo de Sniffington.
Servidor MCP alojado
npx add-mcp 'https://sniffington.com/mcp'Se instala en Claude Code, Codex, Cursor y más
Documentación
API de Sniffington
Lee y gestiona menciones de marca a través de HTTP o MCP.
Descripción general
Sniffington encuentra publicaciones públicas que mencionan tu marca o palabras clave en Reddit, X, Hacker News, YouTube, TikTok y otras plataformas. Un modelo de IA elimina el ruido y etiqueta cada publicación con un sentimiento y una categoría. Esta API lee y modifica los mismos datos que ves en la aplicación.
URL base: https://sniffington.com/api/v1. Las solicitudes y respuestas son JSON. La especificación OpenAPI 3.1 está en /openapi.json, y los agentes de IA pueden comenzar desde /llms.txt.
Autenticación
Crea una clave de API en la aplicación en Integraciones → Claves de API, y luego envíala como un token de portador con cada solicitud:
Authorization: Bearer ss_sk_…
- Las claves
ss_sk_…tienen acceso completo y pueden leer y escribir. - Las claves
ss_ro_…son de solo lectura. Un intento de escritura con una de ellas devuelve 403. - Los espacios de trabajo almacenados en la UE tienen claves que comienzan con
ss_sk_eu_…yss_ro_eu_…. Funcionan de la misma manera.
Una clave pertenece a un solo espacio de trabajo, por lo que las solicitudes nunca nombran un espacio de trabajo. La aplicación muestra una clave solo una vez. Si una se filtra, revócala allí y crea una nueva.
Los clientes MCP como ChatGPT y Claude pueden iniciar sesión con OAuth en lugar de usar una clave (ver MCP). Algunas operaciones, como crear claves de API, requieren que el propietario del espacio de trabajo haya iniciado sesión en la aplicación. La referencia las marca como Propietario.
Registro de agentes
Un agente de IA puede crear su propia cuenta. Una persona sigue siendo la propietaria: aprueba una vez, por correo electrónico, y el agente recoge una clave de API. No hay contraseña ni paso de navegador para el agente.
1. El agente solicita una cuenta para la dirección de correo electrónico de una persona:
curl -X POST https://sniffington.com/api/agent/signup -H "content-type: application/json" \
-d '{"email":"you@example.com","agent_name":"Claude","scope":"full"}'
scope es read o full (por defecto full). region es us o eu; déjalo fuera y seguirá el origen de la solicitud. La respuesta es 202 con un claim_id, un poll_secret y un poll_url.
2. La persona recibe un correo electrónico con un enlace. Inicia sesión con esa dirección y elige el espacio de trabajo, si el agente puede hacer cambios y un presupuesto diario. El enlace funciona durante una hora y solo para el propietario de esa dirección.
3. El agente consulta hasta que la persona haya respondido:
curl https://sniffington.com/api/agent/signup/<claim_id> -H "Authorization: Bearer <poll_secret>"
- 202
pending: sigue esperando, con unos segundos de diferencia. - 200
approved: el cuerpo tieneapi_key, mostrado una vez, más el espacio de trabajo,scopeydaily_budget. Guarda la clave ahora. - 403
denied: la persona dijo que no. 410: el enlace expiró o la clave ya fue recogida. Comienza de nuevo.
Las claves creadas de esta manera llevan un presupuesto diario de 10, 25 o 100 acciones costosas: create_project, create_keyword, scan_now, scan_project y sniff_brand. Las lecturas son ilimitadas. Al superar el presupuesto, una llamada devuelve 429 con el código key_budget, y la herramienta MCP whoami muestra cuántas quedan. Cualquiera puede establecer el mismo límite en una clave que cree pasando daily_budget a create_api_key. Los límites del propio plan también se aplican.
Para subir de plan, un agente con una clave de lectura y escritura llama a request_upgrade (POST /v1/billing/upgrade-link). Devuelve un enlace de pago para que el propietario de la cuenta lo abra. No se cobra nada hasta que paguen, y el plan cambia cuando Polar lo confirma.
Los registros están limitados a 3 por día por dirección de correo electrónico y 10 por día por dirección IP.
Inicio rápido
Mantén la clave en una variable de entorno:
export SCOUT_API_KEY=ss_sk_…
Lista tus proyectos. Cada uno tiene un id que otras llamadas usan:
curl https://sniffington.com/api/v1/projects \
-H "Authorization: Bearer $SCOUT_API_KEY"
Añade una palabra clave a un proyecto. Los alias cuentan como la misma palabra clave, y las publicaciones que contienen un término excluido se omiten:
curl -X POST https://sniffington.com/api/v1/projects/acme/keywords \
-H "Authorization: Bearer $SCOUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"term": "acme", "aliases": ["acme.com", "@acmehq"], "excluded": ["acme corp"]}'
Lista menciones negativas de Reddit y X que nadie ha manejado todavía, 50 a la vez:
curl "https://sniffington.com/api/v1/mentions?project=acme&sources=reddit,x&sentiment=negative&status=open&limit=50" \
-H "Authorization: Bearer $SCOUT_API_KEY"
Marca una como hecha, usando el id de esa lista:
curl -X PATCH "https://sniffington.com/api/v1/mentions/MENTION_ID" \
-H "Authorization: Bearer $SCOUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "done"}'
La referencia de API enumera cada operación con sus parámetros.
Paginación
Las operaciones de lista devuelven una página de resultados y un cursor para la siguiente:
{ "data": [ … ], "next_cursor": "WzE3NTg…", "total": 1234 }
Pasa next_cursor de vuelta como cursor para obtener la siguiente página. Es null en la última página. limit establece el tamaño de página: 25 por defecto, 100 como máximo. Un cursor marca una posición en la lista, por lo que las publicaciones que llegan mientras paginas no desplazan ni repiten resultados.
Errores
Cada error tiene la misma forma. code es estable, así que úsalo para ramificar. message está escrito para personas y puede cambiar.
{ "error": { "code": "not_found", "message": "no project acme" } }
| Estado | Código | Cuándo |
|---|---|---|
| 400 | bad_request, validation_error | Falta un parámetro o campo, o tiene el tipo incorrecto. El mensaje lo nombra. |
| 401 | unauthorized | Sin credenciales, o la clave es incorrecta, expiró o fue revocada. |
| 402 | plan_limit | Se alcanzó el límite del plan en proyectos, palabras clave o asientos. |
| 403 | forbidden | La credencial no tiene permiso para hacer esto, por ejemplo, una clave de solo lectura intentando escribir. |
| 404 | not_found | No hay nada con ese id en este espacio de trabajo. |
| 409 | conflict | La solicitud choca con el estado actual, por ejemplo, un nombre que ya está tomado. |
| 415 | unsupported_media_type | Una escritura desde un navegador con sesión iniciada que no es JSON. |
| 429 | rate_limited | Demasiadas solicitudes. Espera el número de segundos en Retry-After. |
Un estado 5xx significa que el servidor falló. Las lecturas son seguras de reintentar.
Límites de velocidad
Cada credencial puede hacer 240 lecturas (GET) y 60 escrituras por minuto. Las llamadas sin clave, como los endpoints públicos del panel, se cuentan por dirección IP.
Al superar el límite obtienes un 429 con el código rate_limited. Espera un minuto e inténtalo de nuevo.
Webhooks
Un webhook envía eventos, como una nueva mención, a tu URL como JSON en un POST. Añade endpoints en la aplicación o con las operaciones de webhook.
Cada entrega está firmada a la manera de Standard Webhooks, con estos encabezados:
webhook-id: el id del mensaje. Los reintentos lo reutilizan, así que úsalo para omitir duplicados.webhook-timestamp: cuándo se envió, en segundos Unix.webhook-signature: uno o más valoresv1,<signature>separados por espacios.
La firma es el HMAC-SHA256 en base64 de {webhook-id}.{webhook-timestamp}.{body}. La clave es tu secreto de endpoint: elimina el prefijo whsec_ y decodifica el resto en base64. Verifica la firma contra el cuerpo sin procesar antes de analizar el JSON, y rechaza marcas de tiempo de más de cinco minutos.
import { createHmac, timingSafeEqual } from "node:crypto";
// secret: the endpoint's "whsec_…" secret. headers: the request headers. body: the raw body as a string.
export function verifyWebhook(secret, headers, body) {
const id = headers["webhook-id"], ts = headers["webhook-timestamp"], sigs = headers["webhook-signature"];
if (!id || !ts || !sigs) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = Buffer.from(createHmac("sha256", key).update(\`${id}.${ts}.${body}\`).digest("base64"));
return sigs.split(" ").some((part) => {
const [version, sig] = part.split(",");
const given = Buffer.from(sig || "");
return version === "v1" && given.length === expected.length && timingSafeEqual(given, expected);
});
}
Responde con un estado 2xx rápidamente y haz el trabajo lento después. Cualquier otro estado, o ninguna respuesta, cuenta como un fallo, y la entrega se reintenta con intervalos crecientes durante unas 90 horas.
MCP
Sniffington también es un servidor MCP, en https://sniffington.com/mcp sobre HTTP Streamable.
- ChatGPT, Claude y otros clientes que admiten OAuth 2.1 con registro dinámico de clientes solo necesitan esa URL. Se registran ellos mismos y te piden que inicies sesión, así que no hay clave que copiar.
- Los clientes sin OAuth pueden enviar una clave de API como
Authorization: Bearer ss_…. Con una clave de solo lectura, solo funcionan las herramientas de lectura.
Cada operación marcada como herramienta MCP en la referencia es una herramienta con el mismo nombre, y toma los parámetros y campos del cuerpo de esa operación como argumentos.
Claude Code:
claude mcp add --transport http sniffington https://sniffington.com/mcp
Un cliente configurado con una clave:
{
"mcpServers": {
"sniffington": {
"url": "https://sniffington.com/mcp",
"headers": {
"Authorization": "Bearer ss_ro_…"
}
}
}
}