Sofya
Una API que proporciona a los agentes de IA herramientas de búsqueda web, obtención de páginas como markdown, extracción de IA e investigación profunda.
Documentación
Documentación de la API
Todo lo que necesitas para integrar Sofya en tu agente de IA.
Copiar como Markdown
Copiar como Texto
Inicio Rápido
2. Obtén créditos
Las cuentas creadas al iniciar sesión con una cuenta de GitHub de al menos 1 año de antigüedad obtienen el nivel gratuito: 2,000 créditos, que se restablecen a 2,000 cada 30 días. Los registros por correo electrónico comienzan con 0 créditos en modalidad de pago por uso. ¿Necesitas más? Compra créditos desde el panel a $0.0025 cada uno (mínimo $10 = 4,000 créditos). Consulta Créditos y Facturación.
3. Busca en la web
curl -X POST https://sofya.co/v1/search \
-H "Authorization: Bearer ay_live_..." \
-H "Content-Type: application/json" \
-d '{"query": "latest AI news"}'
Autenticación
Cada solicitud a la API necesita tu clave de API, enviada en el encabezado Authorization o en el encabezado X-API-Key.
Authorization: Bearer ay_live_your_key_here
# or
X-API-Key: ay_live_your_key_here
Excepciones: GET /v1/status y GET /v1/updates no necesitan clave. El servidor MCP también admite inicio de sesión OAuth para clientes que lo ofrezcan. Si configuras una lista de IPs permitidas en una clave, las solicitudes desde cualquier otra IP recibirán un 401.
MCP Protocolo de Contexto de Modelo
Conecta Sofya directamente a Claude Code, Cursor o cualquier cliente compatible con MCP. Tu agente de IA obtiene herramientas de búsqueda, obtención, extracción e investigación, sin necesidad de llamadas REST.
Ve a tu panel y haz clic en el botón de copiar para tu cliente. El comando viene prellenado con tu clave de API.
Claude Code
claude mcp add --transport http sofya https://mcp.sofya.co/mcp \
--header "Authorization: Bearer ay_live_..."
Cursor · ~/.cursor/mcp.json
{
"mcpServers": {
"sofya": {
"url": "https://mcp.sofya.co/mcp",
"headers": { "Authorization": "Bearer ay_live_..." }
}
}
}
Codex · ~/.codex/config.toml
[mcp_servers.sofya]
url = "https://mcp.sofya.co/mcp"
http_headers = { "Authorization" = "Bearer ay_live_..." }
Windsurf · ~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"sofya": {
"serverUrl": "https://mcp.sofya.co/mcp",
"headers": { "Authorization": "Bearer ay_live_..." }
}
}
}
VS Code Copilot ·.vscode/mcp.json
{
"servers": {
"sofya": {
"type": "http",
"url": "https://mcp.sofya.co/mcp",
"headers": { "Authorization": "Bearer ay_live_..." }
}
}
}
Tu clave de API se envía a través del encabezado HTTP. El modelo de IA nunca la ve. Los clientes que admiten MCP OAuth pueden conectarse a https://mcp.sofya.co/mcp sin encabezado e iniciar sesión en su lugar.
Herramientas Disponibles
search
Búsqueda web con extracción de contenido de páginas y respuestas de IA opcionales. 1-2 créditos (+10 con respuesta de IA).
fetch
Obtiene URLs y documentos como markdown limpio. 2 créditos por URL; las URLs fallidas son gratuitas.
extract
Haz una pregunta sobre una página y obtén una respuesta escrita por IA. 10 créditos.
research
Investigación profunda con múltiples consultas y síntesis de IA. 50 créditos.
Las herramientas MCP aceptan los mismos parámetros y devuelven el mismo JSON que los endpoints REST. Los errores llegan como resultado de herramienta con isError: true y un mensaje en lugar de un código de estado HTTP, y no se cobran.
Definiciones de Herramientas para Claude y GPT
Copia y pega estos esquemas de herramientas en tus llamadas a la API de Anthropic u OpenAI. Tu modelo obtiene las herramientas de Sofya sin que tengas que escribir definiciones tú mismo.
Anthropic (Claude)
Pasa este arreglo como parámetro tools en tu solicitud /v1/messages. Cuando Claude devuelva un bloque tool_use, llama al endpoint REST correspondiente de Sofya y devuelve el resultado como tool_result.
[
{
"name": "sofya_search",
"description": "Search the web for current information. Returns extracted page content, not just snippets. Set topic='news' for current events. Set include_answer=true for an AI-synthesized answer (+10 credits). Returns: query, answer, results [{title, url, content, description, fetched, published_date}], search_depth, topic, elapsed_ms, credits_used, credits_remaining, altered_query, relaxed_query (set when the query matched nothing and was retried once with its site: operator, else its quotes, removed - the results answer that looser query).",
"input_schema": {
"type": "object",
"required": ["query"],
"properties": {
"query": {"type": "string", "description": "The search query"},
"search_depth": {"type": "string", "description": "\"snippets\" (1 credit) or \"basic\" (2, default)"},
"max_results": {"type": "integer", "description": "Maximum number of results, 1-20 (default 10). Out-of-range values are clamped"},
"include_answer": {"type": "boolean", "description": "Add AI answer synthesized from results (+10 credits)"},
"topic": {"type": "string", "description": "\"general\" (default) or \"news\""},
"freshness": {"type": "string", "description": "\"day\", \"week\", \"month\", \"year\", or \"YYYY-MM-DD:YYYY-MM-DD\""},
"include_domains": {"type": "array", "items": {"type": "string"}, "description": "Only these domains, bare hostnames like github.com (max 10)"},
"exclude_domains": {"type": "array", "items": {"type": "string"}, "description": "Exclude these domains, bare hostnames like pinterest.com (max 10)"}
}
}
},
{
"name": "sofya_fetch",
"description": "Fetch one or more URLs in parallel and return their content as clean markdown. Supports web pages, PDF, Word, Excel, PowerPoint, OpenDocument, EPUB, RTF, CSV, XML and plain text. 2 credits per URL, max 10 URLs. Failed URLs (success=false) are not charged. Returns: results [{title, url, content, raw_html, published_time, success, error}], credits_used, credits_remaining.",
"input_schema": {
"type": "object",
"required": ["urls"],
"properties": {
"urls": {"type": "array", "items": {"type": "string"}, "description": "URLs to fetch (max 10)"},
"include_raw_html": {"type": "boolean", "description": "Include raw HTML source in response (default false)"}
}
}
},
{
"name": "sofya_extract",
"description": "Fetch a URL and answer a question about it using AI. Use when you need specific facts from a page (pricing, specs, contact info) rather than its full content. Returns a free-text answer, not structured JSON. 10 credits. If the page has under 200 chars of text it returns empty content with usage.low_content=true instead of a fabricated answer (still charged). Returns: content, url, credits_used, credits_remaining, usage (input_tokens, output_tokens, content_chars, low_content).",
"input_schema": {
"type": "object",
"required": ["url", "prompt"],
"properties": {
"url": {"type": "string", "description": "The URL to extract from"},
"prompt": {"type": "string", "description": "What to extract, e.g. \"list all pricing tiers with features\""}
}
}
},
{
"name": "sofya_research",
"description": "Deep research on a topic. Decomposes query into sub-queries, searches and reads multiple sources in parallel, synthesizes a structured report with citations. 50 credits. Returns: query, report, sources [{title, url, fetched}], sub_queries, credits_used, credits_remaining, usage.",
"input_schema": {
"type": "object",
"required": ["query"],
"properties": {
"query": {"type": "string", "description": "The research question or topic"},
"topic": {"type": "string", "description": "\"general\" (default) or \"news\""},
"freshness": {"type": "string", "description": "\"day\", \"week\", \"month\", \"year\", or \"YYYY-MM-DD:YYYY-MM-DD\""},
"max_sources": {"type": "integer", "description": "Max sources to use, 5-30 (default 20). Out-of-range values are clamped"}
}
}
}
]
OpenAI (GPT)
Pasa este arreglo como parámetro tools en tu solicitud /chat/completions. Cuando el modelo devuelva tool_calls, llama al endpoint REST correspondiente de Sofya y devuelve el resultado como mensaje role: "tool".
[
{
"type": "function",
"function": {
"name": "sofya_search",
"description": "Search the web for current information. Returns extracted page content, not just snippets. Set topic='news' for current events. Set include_answer=true for an AI-synthesized answer (+10 credits). Returns: query, answer, results [{title, url, content, description, fetched, published_date}], search_depth, topic, elapsed_ms, credits_used, credits_remaining, altered_query, relaxed_query (set when the query matched nothing and was retried once with its site: operator, else its quotes, removed - the results answer that looser query).",
"parameters": {
"type": "object",
"required": ["query"],
"properties": {
"query": {"type": "string", "description": "The search query"},
"search_depth": {"type": "string", "description": "\"snippets\" (1 credit) or \"basic\" (2, default)"},
"max_results": {"type": "integer", "description": "Maximum number of results, 1-20 (default 10). Out-of-range values are clamped"},
"include_answer": {"type": "boolean", "description": "Add AI answer synthesized from results (+10 credits)"},
"topic": {"type": "string", "description": "\"general\" (default) or \"news\""},
"freshness": {"type": "string", "description": "\"day\", \"week\", \"month\", \"year\", or \"YYYY-MM-DD:YYYY-MM-DD\""},
"include_domains": {"type": "array", "items": {"type": "string"}, "description": "Only these domains, bare hostnames like github.com (max 10)"},
"exclude_domains": {"type": "array", "items": {"type": "string"}, "description": "Exclude these domains, bare hostnames like pinterest.com (max 10)"}
},
"additionalProperties": false
}
}
},
{
"type": "function",
"function": {
"name": "sofya_fetch",
"description": "Fetch one or more URLs in parallel and return their content as clean markdown. Supports web pages, PDF, Word, Excel, PowerPoint, OpenDocument, EPUB, RTF, CSV, XML and plain text. 2 credits per URL, max 10 URLs. Failed URLs (success=false) are not charged. Returns: results [{title, url, content, raw_html, published_time, success, error}], credits_used, credits_remaining.",
"parameters": {
"type": "object",
"required": ["urls"],
"properties": {
"urls": {"type": "array", "items": {"type": "string"}, "description": "URLs to fetch (max 10)"},
"include_raw_html": {"type": "boolean", "description": "Include raw HTML source in response (default false)"}
},
"additionalProperties": false
}
}
},
{
"type": "function",
"function": {
"name": "sofya_extract",
"description": "Fetch a URL and answer a question about it using AI. Use when you need specific facts from a page (pricing, specs, contact info) rather than its full content. Returns a free-text answer, not structured JSON. 10 credits. If the page has under 200 chars of text it returns empty content with usage.low_content=true instead of a fabricated answer (still charged). Returns: content, url, credits_used, credits_remaining, usage (input_tokens, output_tokens, content_chars, low_content).",
"parameters": {
"type": "object",
"required": ["url", "prompt"],
"properties": {
"url": {"type": "string", "description": "The URL to extract from"},
"prompt": {"type": "string", "description": "What to extract, e.g. \"list all pricing tiers with features\""}
},
"additionalProperties": false
}
}
},
{
"type": "function",
"function": {
"name": "sofya_research",
"description": "Deep research on a topic. Decomposes query into sub-queries, searches and reads multiple sources in parallel, synthesizes a structured report with citations. 50 credits. Returns: query, report, sources [{title, url, fetched}], sub_queries, credits_used, credits_remaining, usage.",
"parameters": {
"type": "object",
"required": ["query"],
"properties": {
"query": {"type": "string", "description": "The research question or topic"},
"topic": {"type": "string", "description": "\"general\" (default) or \"news\""},
"freshness": {"type": "string", "description": "\"day\", \"week\", \"month\", \"year\", or \"YYYY-MM-DD:YYYY-MM-DD\""},
"max_sources": {"type": "integer", "description": "Max sources to use, 5-30 (default 20). Out-of-range values are clamped"}
},
"additionalProperties": false
}
}
}
]
Ejemplo: conexión
Cuando el modelo llame a una herramienta, asigna el nombre de la herramienta al endpoint de Sofya y reenvía los argumentos:
# Python - handle tool calls from Claude or GPT
TOOL_TO_ENDPOINT = {
"sofya_search": "/v1/search",
"sofya_fetch": "/v1/fetch",
"sofya_extract": "/v1/extract",
"sofya_research": "/v1/research",
}
def call_sofya(tool_name: str, args: dict) -> dict:
import httpx
resp = httpx.post(
f"https://sofya.co{TOOL_TO_ENDPOINT[tool_name]}",
headers={"Authorization": "Bearer ay_live_..."},
json=args,
timeout=120,
)
return resp.json()
Herramientas Principales
POST /v1/search 1-2 créditos (+10 con respuesta)
Busca en la web. Devuelve el contenido de la página, no solo fragmentos. Elige una profundidad de búsqueda para controlar la relación calidad/costo. Agrega include_answer a cualquier profundidad para obtener una respuesta sintetizada por IA (+10 créditos). Esta es una alternativa ligera al endpoint de investigación de 50 créditos.
snippets 1 crédito
Solo fragmentos de SERP. Más rápido.
basic 2 créditos (predeterminado)
Obtiene páginas y devuelve contenido extraído (~12,000 caracteres por resultado).
Cuerpo de la Solicitud
{
"query": "latest AI news",
"search_depth": "basic",
"max_results": 10,
"include_answer": false,
"include_domains": [],
"exclude_domains": [],
"topic": "general",
"freshness": null
}
| Parámetro | Tipo | Descripción |
|---|---|---|
| query | string | Obligatorio. Máximo 2,048 caracteres. |
| search_depth | string | "basic" (predeterminado, 2 créditos) o "snippets" (1 crédito). |
| max_results | integer | Número máximo de resultados, 1-20, predeterminado 10. Los valores fuera de rango se ajustan, no se rechazan. Puedes obtener menos. |
| include_answer | boolean | Predeterminado false. Agrega una respuesta de IA sintetizada a partir de los resultados (+10 créditos, por lo que basic + respuesta = 12). Si no se puede producir una respuesta, answer es null y los 10 créditos se reembolsan. |
| include_domains | string[] | Solo devuelve resultados de estos dominios. Nombres de host simples como github.com (sin esquema ni ruta), máximo 10. |
| exclude_domains | string[] | Excluye estos dominios. Nombres de host simples, máximo 10. |
| topic | string | "general" (predeterminado) o "news". |
| freshness | string | Predeterminado null. "day", "week", "month", "year" o un rango "YYYY-MM-DD:YYYY-MM-DD". |
Respuesta
{
"query": "latest AI news",
"answer": null,
"results": [
{
"title": "...",
"url": "...",
"content": "Extracted page content...",
"description": "SERP snippet",
"fetched": true,
"published_date": "2026-03-08",
"sublinks": [],
"table": {}
}
],
"search_depth": "basic",
"topic": "general",
"elapsed_ms": 4200,
"credits_used": 2,
"credits_remaining": 1998,
"altered_query": null,
"relaxed_query": null
}
query: la consulta realmente ejecutada. Puede diferir de la que enviaste: incluye cualquier operador site: construido a partir de include/exclude_domains, o la consulta más flexible después de un reintento sin resultados.
answer: null a menos que include_answer sea true (y sigue siendo null, con reembolso, si no se pudo producir una respuesta).
content: en profundidad basic, hasta ~12,000 caracteres de texto de página por resultado. Una página que no carga a tiempo se degrada a su fragmento, con fetched: false. En profundidad snippets, fetched siempre es false.
published_date: YYYY-MM-DD de los metadatos de la página o del SERP, o null.
sublinks: enlaces adicionales mostrados bajo un resultado, cada uno {"title", "description", "url"}. table: un objeto, a menudo vacío.
altered_query: se establece si el motor de búsqueda corrigió automáticamente tu consulta; de lo contrario, null.
relaxed_query: se establece cuando la consulta no coincidió con nada y se reintentó una vez con su operador site: (o sus comillas) eliminado. Los resultados responden entonces a esa consulta más flexible. Null en caso contrario.
topic: "general" (predeterminado) para búsqueda web, o "news" para búsqueda específica de noticias. Usa "news" para eventos actuales, noticias de última hora, política o cualquier consulta sensible al tiempo. Devuelve artículos con fechas de publicación.
freshness: "day", "week", "month", "year" o rango personalizado "YYYY-MM-DD:YYYY-MM-DD"
curl -X POST https://sofya.co/v1/search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ay_live_..." \
-d '{"query": "latest AI news", "search_depth": "basic"}'
import httpx
resp = httpx.post("https://sofya.co/v1/search",
headers={"Authorization": "Bearer ay_live_..."},
json={"query": "latest AI news", "search_depth": "basic"})
print(resp.json())
const resp = await fetch("https://sofya.co/v1/search", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": "Bearer ay_live_..." },
body: JSON.stringify({ query: "latest AI news", search_depth: "basic" })
});
console.log(await resp.json());
POST /v1/fetch 2 créditos por URL
Obtiene hasta 10 URLs en paralelo y devuelve su contenido como markdown limpio. Funciona en páginas web y documentos: PDF, Word (DOCX, DOC), Excel (XLSX, XLS), PowerPoint (PPTX, PPT), OpenDocument, EPUB, RTF, CSV, XML y texto plano. Cuesta 2 créditos por URL que tenga éxito; una URL que falle regresa con success: false y no se cobra.
Cuerpo de la Solicitud
{
"urls": ["https://example.com"],
"include_raw_html": false
}
urls: obligatorio, 1-10 URLs. Una lista vacía es un 400; más de 10 es un 422.
include_raw_html: predeterminado false. También devuelve el HTML crudo de la página.
Respuesta
{
"results": [
{
"title": "Example Page",
"url": "https://example.com",
"content": "# Markdown content...",
"raw_html": null,
"published_time": null,
"success": true,
"error": null
}
],
"credits_used": 2,
"credits_remaining": 1998
}
success / error: cada URL tiene éxito o falla de forma independiente. Una URL que no termine después de 50 segundos se rinde y devuelve success: false con un mensaje error; las demás URLs no se ven afectadas.
Límites de tamaño: 5 MB para páginas HTML, 50 MB para documentos.
raw_html: null a menos que include_raw_html sea true, y puede seguir siendo null para contenido no HTML (como documentos) y para algunos sitios.
published_time: YYYY-MM-DD de los metadatos de la página cuando esté disponible; de lo contrario, null.
curl -X POST https://sofya.co/v1/fetch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ay_live_..." \
-d '{"urls": ["https://example.com"]}'
import httpx
resp = httpx.post("https://sofya.co/v1/fetch",
headers={"Authorization": "Bearer ay_live_..."},
json={"urls": ["https://example.com"]})
print(resp.json())
const resp = await fetch("https://sofya.co/v1/fetch", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": "Bearer ay_live_..." },
body: JSON.stringify({ urls: ["https://example.com"] })
});
console.log(await resp.json());
POST /v1/extract 10 créditos
Obtiene una página web y responde una pregunta sobre ella usando IA. Cuesta 10 créditos. content es una respuesta de texto libre (no es JSON ni salida estructurada; pide un formato en tu prompt si lo necesitas). Solo los primeros 50,000 caracteres del texto de la página se envían al modelo. Si la página tiene menos de 200 caracteres de texto (cuerpo vacío o renderizado por JavaScript), el modelo no se llama y content se devuelve vacío con usage.low_content: true en lugar de una respuesta inventada. Eso sigue siendo un 200 y se cobran 10 créditos. Si la página en sí no se puede obtener (404, bloqueada, sin DNS, tiempo agotado, URL rechazada), recibes un 422 y no se te cobra.
Cuerpo de la Solicitud
{
"url": "https://example.com",
"prompt": "List all pricing tiers with their monthly prices"
}
url: obligatorio. La página a leer.
prompt: obligatorio, máximo 4,096 caracteres. Qué encontrar o responder.
Respuesta
{
"content": "Three tiers: Free ($0), Pro ($20/month), Team ($50/month).",
"url": "https://example.com",
"credits_used": 10,
"credits_remaining": 1990,
"usage": { "input_tokens": 1420, "output_tokens": 24, "content_chars": 4820, "low_content": false }
}
usage.content_chars es el número de caracteres de texto de página que recibió el modelo; usage.low_content es true cuando la página tenía menos de 200 caracteres de texto (el modelo se omitió, content está vacío y la llamada aún se cobra). Usa estos para detectar páginas vacías o no renderizables.
curl -X POST https://sofya.co/v1/extract \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ay_live_..." \
-d '{"url": "https://example.com", "prompt": "Summarize this page"}'
import httpx
resp = httpx.post("https://sofya.co/v1/extract",
headers={"Authorization": "Bearer ay_live_..."},
json={"url": "https://example.com", "prompt": "Summarize this page"})
print(resp.json())
const resp = await fetch("https://sofya.co/v1/extract", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": "Bearer ay_live_..." },
body: JSON.stringify({ url: "https://example.com", prompt: "Summarize this page" })
});
console.log(await resp.json());
POST /v1/research 50 créditos
Investigación profunda sobre cualquier tema. Descompone tu consulta en subconsultas, busca y lee múltiples fuentes en paralelo, y luego sintetiza un informe estructurado con citas. Cuesta 50 créditos. La investigación tiene un límite de tiempo de 55 segundos (504, no se cobra); si no se pueden encontrar fuentes, la solicitud falla y no se cobra.
Cuerpo de la Solicitud
{
"query": "How do modern LLMs handle long context?",
"topic": "general",
"freshness": null,
"max_sources": 20
}
query: obligatorio, máximo 4,096 caracteres.
topic: "general" (predeterminado) o "news".
freshness: predeterminado null. "day", "week", "month", "year" o "YYYY-MM-DD:YYYY-MM-DD".
max_sources: 5-30, predeterminado 20. Los valores fuera de rango se ajustan.
Respuesta
{
"query": "How do modern LLMs handle long context?",
"report": "## Key Findings\n\n- ...",
"sources": [
{
"title": "Scaling Transformer Context Windows",
"url": "https://arxiv.org/abs/...",
"fetched": true
}
],
"sub_queries": [
"transformer context window scaling techniques",
"RoPE positional encoding extensions"
],
"credits_used": 50,
"credits_remaining": 1950,
"usage": { "input_tokens": 12400, "output_tokens": 1850 }
}
curl -X POST https://sofya.co/v1/research \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ay_live_..." \
-d '{"query": "How do modern LLMs handle long context?"}'
import httpx
resp = httpx.post("https://sofya.co/v1/research",
headers={"Authorization": "Bearer ay_live_..."},
json={"query": "How do modern LLMs handle long context?"},
timeout=120)
print(resp.json())
const resp = await fetch("https://sofya.co/v1/research", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": "Bearer ay_live_..." },
body: JSON.stringify({ query: "How do modern LLMs handle long context?" })
});
console.log(await resp.json());
Cuenta
GET /v1/auth/me
Obtén información de tu cuenta: saldo de créditos, nivel y solicitudes totales. Gratis.
Respuesta
{
"credits": 1994,
"plan_credits": 1994,
"purchased_credits": 0,
"is_free_tier": true,
"credits_reset_at": "2026-11-04T12:00:00Z",
"total_requests": 3,
"api_key": null,
"last_login_method": "github",
"email": "user@example.com",
"email_verified": true,
"github_username": "octocat",
"has_billing_portal": false,
"new_ip_alerts_enabled": true
}
credits: saldo total disponible para gastar, plan_credits (créditos del nivel gratuito) más purchased_credits.
credits_reset_at: cuándo se restablecen los créditos del nivel gratuito a 2,000 (cada 30 días). Los créditos comprados nunca se restablecen.
api_key: siempre null cuando llamas con una clave de API. Solo se completa para el panel con sesión iniciada.
GET /v1/auth/transactions
Obtén tus últimos 50 recargas y ajustes de cuenta, del más reciente al más antiguo. Los cargos y reembolsos por solicitud no se listan aquí; usa uso para esos.
Respuesta
[
{
"id": "uuid",
"type": "credit",
"amount": 4000,
"endpoint": "top-up",
"balance_after": 5994,
"created_at": "2026-10-08T12:00:00Z"
}
]
GET /v1/auth/usage
Obtén tu desglose de uso diario. Las filas son por día, endpoint y clave de API, con el día más reciente primero, por lo que un día puede tener varias filas para el mismo endpoint si usas más de una clave.
Parámetros de Consulta
days: 1-90, predeterminado 7. Los valores fuera de rango se ajustan.
offset_days: predeterminado 0. Omite esta cantidad de días recientes, para paginar hacia atrás en el tiempo.
api_key_id: opcional. Solo cuenta las solicitudes hechas con esta clave (su id del panel).
curl "https://sofya.co/v1/auth/usage?days=30" \
-H "Authorization: Bearer ay_live_..."
Respuesta
[
{
"date": "2026-10-08",
"endpoint": "/v1/search",
"request_count": 3,
"total_credits": 6
}
]
Créditos y Facturación
Cada llamada se paga en créditos. Un crédito es $0.0025.
| Llamada | Créditos | USD |
|---|---|---|
| search (snippets) | 1 | $0.0025 |
| search (basic) | 2 | $0.005 |
| include_answer | +10 | +$0.025 |
| fetch | 2 por URL | $0.005 por URL |
| extract | 10 | $0.025 |
| research | 50 | $0.125 |
Compra de créditos: recarga desde el panel en dólares completos, desde $10 (4,000 créditos) hasta $25,000. Los créditos comprados no se ven afectados por el restablecimiento del nivel gratuito. No existe un endpoint de API para comprar créditos.
Nivel gratuito: las cuentas creadas al iniciar sesión con una cuenta de GitHub de al menos 1 año de antigüedad obtienen 2,000 créditos, que se restablecen a 2,000 cada 30 días. Los créditos gratuitos no utilizados no se acumulan. Vincular GitHub a una cuenta de correo existente no agrega el nivel gratuito, y los registros por correo (enlace mágico) comienzan con 0 créditos en modalidad de pago por uso.
Qué se cobra: solo una respuesta 2xx, exactamente en su credits_used. Consulta Facturación en errores.
Límites de Tasa
Las solicitudes están limitadas a 30 solicitudes por segundo por clave de API. REST (/v1/*) y MCP (/mcp) se cuentan por separado, por lo que cada uno tiene sus propios 30 por segundo.
Si superas el límite, la API devuelve 429 Too Many Requests con un encabezado Retry-After que indica el número entero de segundos a esperar antes de reintentar.
Respuesta 429
HTTP/1.1 429 Too Many Requests
Retry-After: 1
{
"detail": "Rate limit exceeded. 30 requests per second."
}
Las solicitudes limitadas por tasa no consumen créditos. Implementa retroceso exponencial o respeta el encabezado Retry-After para obtener mejores resultados.
Códigos de Error
| Código | Estado | Descripción |
|---|---|---|
| 400 | Solicitud incorrecta | Un problema a nivel de solicitud que el esquema no puede detectar, p. ej., una lista urls vacía en fetch |
| 401 | No autorizado | Clave API inválida o faltante, o la IP de la solicitud no está en la lista de permitidos de IP de la clave (aplica a cada endpoint /v1, incluyendo /v1/auth/*) |
| 402 | Pago requerido | Créditos insuficientes |
| 422 | Entidad no procesable | Parámetros inválidos: un campo faltante o de tipo incorrecto, search_depth o topic desconocidos, formato freshness incorrecto (o de más de 25 caracteres), dominios que no son nombres de host simples, más de 10 urls o dominios, una consulta de búsqueda de más de 2,048 caracteres, o una consulta de investigación o prompt de extracción de más de 4,096. También extract cuando la página objetivo no pudo obtenerse (404, bloqueada, sin DNS, tiempo agotado, URL rechazada); verifica la URL, reintentar rara vez ayuda. |
| 429 | Demasiadas solicitudes | Límite de velocidad alcanzado. Revisa el encabezado Retry-After. |
| 502 | Puerta de enlace incorrecta | Error interno. Reintenta la solicitud. |
| 503 | Servicio no disponible | La capacidad de búsqueda está temporalmente no disponible. Reintenta en breve. |
| 504 | Tiempo de espera de la puerta de enlace | La solicitud alcanzó su límite de tiempo (60s; 55s para investigación). Reintenta, o para investigación prueba una consulta más estrecha o menos fuentes. |
Facturación en errores: solo una respuesta 2xx se cobra, exactamente a su credits_used. Ninguna respuesta de error se cobra: los créditos se reservan cuando la solicitud comienza y se reembolsan automáticamente si falla o expira. Dentro de un 200, algunas partes también se reembolsan: en fetch, cada URL que regresa success: false; en búsqueda, los 10 créditos include_answer cuando no se produjo una respuesta (answer: null). La investigación que no encuentra fuentes falla y se reembolsa. Extract con low_content: true es un 200 y se cobra. Así que en cualquier no-2xx, es seguro reintentar.
A través de MCP, los errores llegan como un resultado de herramienta con isError: true y un mensaje en lugar de un código de estado HTTP. Se aplican las mismas reglas de facturación.
Estado y Monitoreo
Salud por endpoint: GET /v1/status (sin clave API) reporta la salud de búsqueda, fetch, extract e investigación medida por Sofya mismo.
{
"overall": "operational",
"endpoints": {
"search": { "status": "operational", "latency_ms": 1905.9, "last_check": "2026-10-09T14:00:27+00:00" },
"fetch": { "status": "operational", "latency_ms": 4116.3, "last_check": "2026-10-09T13:59:47+00:00" },
"extract": { "status": "operational", "latency_ms": 6437.9, "last_check": "2026-10-09T13:59:18+00:00" },
"research": { "status": "operational", "latency_ms": 16049.8, "last_check": "2026-10-09T13:59:18+00:00" }
},
"incidents": []
}
overall: operational, degraded, partial_outage, major_outage, o checking (mientras las comprobaciones aún no han informado).
endpoints.*.status: operational, degraded, down, o unknown. latency_ms es una latencia típica reciente y puede ser null. Un endpoint también puede llevar latency_variants (p. ej., snippets de búsqueda vs básico).
incidents: incidentes recientes.
Registro de cambios: GET /v1/updates (sin clave API) devuelve la lista de cambios visibles para el usuario como JSON.
Estado del sitio web: status.sofya.co se ejecuta en un servidor separado y verifica el sitio web (sofya.co) cada 60 segundos, por lo que sigue informando incluso si Sofya mismo está caído. No sondea los endpoints individuales de la API; usa /v1/status para esos. Sus feeds son públicos, no se necesita clave API.
Feeds JSON de status.sofya.co
- /api/v2/summary.json - Compatible con Atlassian Statuspage, para herramientas que ya leen feeds de Statuspage.
- /api/v2/status.json - Solo metadatos de página e indicador general. Sondeo más barato si solo necesitas arriba/abajo.
- /api/status.json - Feed más rico: última latencia, tiempo de actividad de 24h y 90d, último error.
$ curl https://status.sofya.co/api/v2/status.json
{
"page": { "id": "sofya", "name": "Sofya Status", "url": "https://status.sofya.co", "time_zone": "Etc/UTC", "updated_at": "2026-10-09T14:00:30+00:00" },
"status": {
"indicator": "none",
"description": "All Systems Operational"
}
}
El campo indicator sigue la convención de Statuspage: none (operacional), minor (degradado), o major (caído).
# Documentación de la API de Sofya
URL base: https://sofya.co\
Inicio rápido
1. Regístrate
Visita el panel de control (https://sofya.co/dashboard) e inicia sesión con GitHub o con un enlace de correo electrónico. Tu clave de API está en el panel de control.
2. Obtén créditos
Las cuentas creadas al iniciar sesión con una cuenta de GitHub que tenga al menos 1 año de antigüedad obtienen el nivel gratuito: 2,000 créditos, que se restablecen a 2,000 cada 30 días. Los registros con correo electrónico comienzan con 0 créditos en el modelo de pago por uso. ¿Necesitas más? Compra créditos desde el panel de control a $0.0025 cada uno (mínimo $10 = 4,000 créditos). Consulta Créditos y facturación a continuación.
3. Busca en la web
\``bash curl -X POST https://sofya.co/v1/search \\ -H "Authorization: Bearer ay\_live\_..." \\ -H "Content-Type: application/json" \\ -d '{"query": "latest AI news"}' \``\
Autenticación
Cada solicitud a la API necesita tu clave de API, enviada en el encabezado Authorization\ o en el encabezado X-API-Key\.
\`` Authorization: Bearer ay\_live\_your\_key\_here # or X-API-Key: ay\_live\_your\_key\_here \``\
Excepciones: GET /v1/status\ y GET /v1/updates\ no necesitan clave. El servidor MCP también admite el inicio de sesión OAuth para clientes que lo ofrezcan. Si configuras una lista de IP permitidas en una clave, las solicitudes desde cualquier otra IP reciben un 401.
MCP (Model Context Protocol)
Conecta Sofya directamente a Claude Code, Cursor o cualquier cliente compatible con MCP. Tu agente de IA obtiene herramientas de búsqueda, recuperación, extracción e investigación, sin necesidad de llamadas REST. Ve a tu panel de control y haz clic en el botón de copiar para tu cliente. El comando viene prellenado con tu clave de API.
Claude Code
\``bash claude mcp add --transport http sofya https://mcp.sofya.co/mcp \\ --header "Authorization: Bearer ay\_live\_..." \``\
Cursor (~/.cursor/mcp.json\)
\``json { "mcpServers": { "sofya": { "url": "https://mcp.sofya.co/mcp", "headers": { "Authorization": "Bearer ay\_live\_..." } } } } \``\
Codex (~/.codex/config.toml\)
\``toml \[mcp\_servers.sofya\] url = "https://mcp.sofya.co/mcp" http\_headers = { "Authorization" = "Bearer ay\_live\_..." } \``\
Windsurf (~/.codeium/windsurf/mcp\_config.json\)
\``json { "mcpServers": { "sofya": { "serverUrl": "https://mcp.sofya.co/mcp", "headers": { "Authorization": "Bearer ay\_live\_..." } } } } \``\
VS Code Copilot (.vscode/mcp.json\)
\``json { "servers": { "sofya": { "type": "http", "url": "https://mcp.sofya.co/mcp", "headers": { "Authorization": "Bearer ay\_live\_..." } } } } \``\
Tu clave de API se envía mediante un encabezado HTTP. El modelo de IA nunca la ve. Los clientes que admiten OAuth de MCP pueden conectarse a https://mcp.sofya.co/mcp\ sin encabezado e iniciar sesión en su lugar.
Herramientas disponibles:
search\· Búsqueda web con extracción de contenido de páginas. 1-2 créditos, +10 coninclude\_answer: true\para una respuesta sintetizada por IA. Una alternativa ligera al endpoint completo de investigación.fetch\· Recupera URLs y documentos como markdown limpio. 2 créditos por URL; las URLs fallidas son gratuitas.extract\· Haz una pregunta sobre una página y obtén una respuesta escrita por IA. 10 créditos.research\· Investigación profunda con múltiples consultas y síntesis por IA. 50 créditos.
Las herramientas MCP aceptan los mismos parámetros y devuelven el mismo JSON que los endpoints REST. Los errores llegan como un resultado de herramienta con isError: true\ y un mensaje en lugar de un código de estado HTTP, y no se cobran.
Definiciones de herramientas (para Claude y GPT)
Copia y pega estos esquemas de herramientas en tus llamadas a la API de Anthropic u OpenAI. Cuando el modelo llame a una herramienta, envía sus argumentos mediante POST al endpoint de Sofya correspondiente (sofya\_search\ -> /v1/search\, sofya\_fetch\ -> /v1/fetch\, sofya\_extract\ -> /v1/extract\, sofya\_research\ -> /v1/research\) y devuelve el JSON como resultado de la herramienta.
Anthropic (Claude)
Pasa este array como el parámetro tools\ en tu solicitud /v1/messages\:
\``json \[ { "name": "sofya\_search", "description": "Search the web for current information. Returns extracted page content, not just snippets. Set topic='news' for current events. Set include\_answer=true for an AI-synthesized answer (+10 credits). Returns: query, answer, results \[{title, url, content, description, fetched, published\_date}\], search\_depth, topic, elapsed\_ms, credits\_used, credits\_remaining, altered\_query, relaxed\_query (set when the query matched nothing and was retried once with its site: operator, else its quotes, removed - the results answer that looser query).", "input\_schema": { "type": "object", "required": \["query"\], "properties": { "query": {"type": "string", "description": "The search query"}, "search\_depth": {"type": "string", "description": "\\"snippets\\" (1 credit) or \\"basic\\" (2, default)"}, "max\_results": {"type": "integer", "description": "Maximum number of results, 1-20 (default 10). Out-of-range values are clamped"}, "include\_answer": {"type": "boolean", "description": "Add AI answer synthesized from results (+10 credits)"}, "topic": {"type": "string", "description": "\\"general\\" (default) or \\"news\\""}, "freshness": {"type": "string", "description": "\\"day\\", \\"week\\", \\"month\\", \\"year\\", or \\"YYYY-MM-DD:YYYY-MM-DD\\""}, "include\_domains": {"type": "array", "items": {"type": "string"}, "description": "Only these domains, bare hostnames like github.com (max 10)"}, "exclude\_domains": {"type": "array", "items": {"type": "string"}, "description": "Exclude these domains, bare hostnames like pinterest.com (max 10)"} } } }, { "name": "sofya\_fetch", "description": "Fetch one or more URLs in parallel and return their content as clean markdown. Supports web pages, PDF, Word, Excel, PowerPoint, OpenDocument, EPUB, RTF, CSV, XML and plain text. 2 credits per URL, max 10 URLs. Failed URLs (success=false) are not charged. Returns: results \[{title, url, content, raw\_html, published\_time, success, error}\], credits\_used, credits\_remaining.", "input\_schema": { "type": "object", "required": \["urls"\], "properties": { "urls": {"type": "array", "items": {"type": "string"}, "description": "URLs to fetch (max 10)"}, "include\_raw\_html": {"type": "boolean", "description": "Include raw HTML source in response (default false)"} } } }, { "name": "sofya\_extract", "description": "Fetch a URL and answer a question about it using AI. Use when you need specific facts from a page (pricing, specs, contact info) rather than its full content. Returns a free-text answer, not structured JSON. 10 credits. If the page has under 200 chars of text it returns empty content with usage.low\_content=true instead of a fabricated answer (still charged). Returns: content, url, credits\_used, credits\_remaining, usage (input\_tokens, output\_tokens, content\_chars, low\_content).", "input\_schema": { "type": "object", "required": \["url", "prompt"\], "properties": { "url": {"type": "string", "description": "The URL to extract from"}, "prompt": {"type": "string", "description": "What to extract, e.g. \\"list all pricing tiers with features\\""} } } }, { "name": "sofya\_research", "description": "Deep research on a topic. Decomposes query into sub-queries, searches and reads multiple sources in parallel, synthesizes a structured report with citations. 50 credits. Returns: query, report, sources \[{title, url, fetched}\], sub\_queries, credits\_used, credits\_remaining, usage.", "input\_schema": { "type": "object", "required": \["query"\], "properties": { "query": {"type": "string", "description": "The research question or topic"}, "topic": {"type": "string", "description": "\\"general\\" (default) or \\"news\\""}, "freshness": {"type": "string", "description": "\\"day\\", \\"week\\", \\"month\\", \\"year\\", or \\"YYYY-MM-DD:YYYY-MM-DD\\""}, "max\_sources": {"type": "integer", "description": "Max sources to use, 5-30 (default 20). Out-of-range values are clamped"} } } } \] \``\
OpenAI (GPT)
Usa las mismas cuatro herramientas en tu solicitud /chat/completions\, envolviendo cada una como { "type": "function", "function": { "name":..., "description":..., "parameters":... } }\, donde parameters\ es el input\_schema\ anterior más "additionalProperties": false\. Devuelve los resultados como mensajes role: "tool"\.
Ejemplo: conexión
\``python # Python - handle tool calls from Claude or GPT TOOL\_TO\_ENDPOINT = { "sofya\_search": "/v1/search", "sofya\_fetch": "/v1/fetch", "sofya\_extract": "/v1/extract", "sofya\_research": "/v1/research", } def call\_sofya(tool\_name: str, args: dict) -\> dict: import httpx resp = httpx.post( f"https://sofya.co{TOOL\_TO\_ENDPOINT\[tool\_name\]}", headers={"Authorization": "Bearer ay\_live\_..."}, json=args, timeout=120, ) return resp.json() \``\
Herramientas principales
POST /v1/search · 1-2 créditos (+10 con respuesta de IA)
Busca en la web. Devuelve el contenido de la página, no solo fragmentos. Elige una profundidad de búsqueda para controlar la relación calidad/costo.
| Profundidad | Créditos | Descripción |
|---|---|---|
| snippets | 1 | Solo fragmentos del SERP. Más rápido. |
| basic | 2 | Recupera páginas, devuelve contenido extraído (hasta ~12,000 caracteres por resultado). Predeterminado. |
Consejo: Agrega include\_answer: true\ a cualquier profundidad para obtener una respuesta sintetizada por IA (+10 créditos). Por ejemplo, basic + answer = 12 créditos. Esta es una alternativa ligera al endpoint de investigación de 50 créditos cuando necesitas una síntesis rápida sin descomposición en múltiples consultas.
Solicitud:
\``json { "query": "latest AI news", "search\_depth": "basic", "max\_results": 10, "include\_answer": false, "include\_domains": \[\], "exclude\_domains": \[\], "topic": "general", "freshness": null } \``\
| Parámetro | Tipo | Descripción |
|---|---|---|
| query | string | Obligatorio. Máximo 2,048 caracteres. |
| search_depth | string | "basic"\ (predeterminado, 2 créditos) o "snippets"\ (1 crédito). |
| max_results | integer | Número máximo de resultados, 1-20, predeterminado 10. Los valores fuera de rango se ajustan, no se rechazan. Puedes obtener menos. |
| include_answer | boolean | Predeterminado false. Agrega una respuesta de IA sintetizada a partir de los resultados (+10 créditos, por lo que basic + answer = 12). Si no se puede producir una respuesta, answer\ es null y los 10 créditos se reembolsan. |
| include_domains | string[] | Solo devuelve resultados de estos dominios. Nombres de host simples como github.com\ (sin esquema ni ruta), máximo 10. |
| exclude_domains | string[] | Excluye estos dominios. Nombres de host simples, máximo 10. |
| topic | string | "general"\ (predeterminado) o "news"\. Usa "news"\ para eventos actuales y otras consultas sensibles al tiempo. |
| freshness | string | Predeterminado null. "day"\, "week"\, "month"\, "year"\ o un rango "YYYY-MM-DD:YYYY-MM-DD"\. |
Respuesta:
\``json { "query": "latest AI news", "answer": null, "results": \[ { "title": "...", "url": "...", "content": "Extracted page content...", "description": "SERP snippet", "fetched": true, "published\_date": "2026-03-08", "sublinks": \[\], "table": {} } \], "search\_depth": "basic", "topic": "general", "elapsed\_ms": 4200, "credits\_used": 2, "credits\_remaining": 1998, "altered\_query": null, "relaxed\_query": null } \``\
query\: la consulta realmente ejecutada. Puede diferir de la que enviaste: incluye cualquier operadorsite:\construido a partir de include/exclude_domains, o la consulta más flexible después de un reintento con cero resultados.answer\: null a menos queinclude\_answer\sea true (y sigue siendo null, con reembolso, si no se pudo producir una respuesta).content\: en profundidad basic, hasta ~12,000 caracteres de texto de página por resultado. Una página que no carga a tiempo vuelve a su fragmento, confetched: false\. En profundidad snippets,fetched\siempre es false.published\_date\:YYYY-MM-DD\de los metadatos de la página o del SERP, o null.sublinks\: enlaces adicionales mostrados bajo un resultado, cada uno{"title", "description", "url"}\.table\: un objeto, a menudo vacío.altered\_query\: se establece si el motor de búsqueda corrigió automáticamente tu consulta; de lo contrario, null.relaxed\_query\: se establece cuando la consulta no coincidió con nada y se reintentó una vez sin su operadorsite:\(o sin sus comillas). Los resultados responden entonces a esa consulta más flexible. Null en caso contrario.
curl:
\``bash curl -X POST https://sofya.co/v1/search \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer ay\_live\_..." \\ -d '{"query": "latest AI news", "search\_depth": "basic"}' \``\
Python:
\``python import httpx resp = httpx.post("https://sofya.co/v1/search", headers={"Authorization": "Bearer ay\_live\_..."}, json={"query": "latest AI news", "search\_depth": "basic"}) print(resp.json()) \``\
JavaScript:
\``javascript const resp = await fetch("https://sofya.co/v1/search", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer ay\_live\_..." }, body: JSON.stringify({ query: "latest AI news", search\_depth: "basic" }) }); console.log(await resp.json()); \``\
POST /v1/fetch · 2 créditos por URL
Recupera hasta 10 URLs en paralelo y devuelve su contenido como markdown limpio. Funciona con páginas web y documentos: PDF, Word (DOCX, DOC), Excel (XLSX, XLS), PowerPoint (PPTX, PPT), OpenDocument, EPUB, RTF, CSV, XML y texto plano. Cuesta 2 créditos por URL que tenga éxito; una URL que falle regresa con success: false\ y no se cobra.
Solicitud:
\``json { "urls": \["https://example.com"\], "include\_raw\_html": false } \``\
urls\: obligatorio, 1-10 URLs. Una lista vacía es un 400; más de 10 es un 422.include\_raw\_html\: predeterminado false. También devuelve el HTML crudo de la página.
Respuesta:
\``json { "results": \[ { "title": "Example Page", "url": "https://example.com", "content": "# Markdown content...", "raw\_html": null, "published\_time": null, "success": true, "error": null } \], "credits\_used": 2, "credits\_remaining": 1998 } \``\
success\/error\: cada URL tiene éxito o falla de forma independiente. Una URL que no termina después de 50 segundos se rinde y devuelvesuccess: false\con un mensajeerror\; las demás URLs no se ven afectadas.- Límites de tamaño: 5 MB para páginas HTML, 50 MB para documentos.
raw\_html\: null a menos queinclude\_raw\_html\sea true, y puede seguir siendo null para contenido no HTML (como documentos) y para algunos sitios.published\_time\:YYYY-MM-DD\de los metadatos de la página cuando esté disponible; de lo contrario, null.
curl:
\``bash curl -X POST https://sofya.co/v1/fetch \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer ay\_live\_..." \\ -d '{"urls": \["https://example.com"\]}' \``\
Python:
\``python import httpx resp = httpx.post("https://sofya.co/v1/fetch", headers={"Authorization": "Bearer ay\_live\_..."}, json={"urls": \["https://example.com"\]}) print(resp.json()) \``\
JavaScript:
\``javascript const resp = await fetch("https://sofya.co/v1/fetch", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer ay\_live\_..." }, body: JSON.stringify({ urls: \["https://example.com"\] }) }); console.log(await resp.json()); \``\
POST /v1/extract · 10 créditos
Recupera una página web y responde una pregunta sobre ella usando IA. Cuesta 10 créditos. content\ es una respuesta de texto libre (no es JSON ni salida estructurada; pide un formato en tu mensaje si lo necesitas). Solo los primeros 50,000 caracteres del texto de la página se envían al modelo. Si la página tiene menos de 200 caracteres de texto (cuerpo vacío o renderizado con JavaScript), el modelo no se llama y content\ se devuelve vacío con usage.low\_content: true\ en lugar de una respuesta inventada. Eso sigue siendo un 200 y se cobran 10 créditos. Si la página no se puede recuperar (404, bloqueada, sin DNS, tiempo de espera agotado, URL rechazada), obtienes un 422 y no se te cobra.
Solicitud:
\``json { "url": "https://example.com", "prompt": "List all pricing tiers with their monthly prices" } \``\
url\: obligatorio. La página a leer.prompt\: obligatorio, máximo 4,096 caracteres. Qué encontrar o responder.
Respuesta:
\``json { "content": "Three tiers: Free ($0), Pro ($20/month), Team ($50/month).", "url": "https://example.com", "credits\_used": 10, "credits\_remaining": 1990, "usage": { "input\_tokens": 1420, "output\_tokens": 24, "content\_chars": 4820, "low\_content": false } } \``\
usage.content\_chars\ es el número de caracteres de texto de página que recibió el modelo; usage.low\_content\ es true cuando la página tenía menos de 200 caracteres de texto (el modelo se omitió, content\ está vacío y la llamada aún se cobra). Usa estos campos para detectar páginas vacías o no renderizables.
curl:
\``bash curl -X POST https://sofya.co/v1/extract \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer ay\_live\_..." \\ -d '{"url": "https://example.com", "prompt": "Summarize this page"}' \``\
Python:
\``python import httpx resp = httpx.post("https://sofya.co/v1/extract", headers={"Authorization": "Bearer ay\_live\_..."}, json={"url": "https://example.com", "prompt": "Summarize this page"}) print(resp.json()) \``\
JavaScript:
\``javascript const resp = await fetch("https://sofya.co/v1/extract", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer ay\_live\_..." }, body: JSON.stringify({ url: "https://example.com", prompt: "Summarize this page" }) }); console.log(await resp.json()); \``\
POST /v1/research · 50 créditos
Investigación profunda sobre cualquier tema. Descompone tu consulta en subconsultas, busca y lee múltiples fuentes en paralelo, y luego sintetiza un informe estructurado con citas. Cuesta 50 créditos. La investigación tiene un límite de tiempo de 55 segundos (504, no se cobra); si no se pueden encontrar fuentes, la solicitud falla y no se cobra.
Solicitud:
\``json { "query": "How do modern LLMs handle long context?", "topic": "general", "freshness": null, "max\_sources": 20 } \``\
query\: obligatorio, máximo 4,096 caracteres.topic\:"general"\(predeterminado) o"news"\.freshness\: predeterminado null."day"\,"week"\,"month"\,"year"\o"YYYY-MM-DD:YYYY-MM-DD"\.max\_sources\: 5-30, predeterminado 20. Los valores fuera de rango se ajustan.
Respuesta:
\``json { "query": "How do modern LLMs handle long context?", "report": "## Key Findings\\n\\n-...", "sources": \[ { "title": "Scaling Transformer Context Windows", "url": "https://arxiv.org/abs/...", "fetched": true } \], "sub\_queries": \[ "transformer context window scaling techniques", "RoPE positional encoding extensions" \], "credits\_used": 50, "credits\_remaining": 1950, "usage": { "input\_tokens": 12400, "output\_tokens": 1850 } } \``\
curl:
\``bash curl -X POST https://sofya.co/v1/research \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer ay\_live\_..." \\ -d '{"query": "How do modern LLMs handle long context?"}' \``\
Python:
\``python import httpx resp = httpx.post("https://sofya.co/v1/research", headers={"Authorization": "Bearer ay\_live\_..."}, json={"query": "How do modern LLMs handle long context?"}, timeout=120) print(resp.json()) \``\
JavaScript:
\``javascript const resp = await fetch("https://sofya.co/v1/research", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer ay\_live\_..." }, body: JSON.stringify({ query: "How do modern LLMs handle long context?" }) }); console.log(await resp.json()); \``\
Cuenta
GET /v1/auth/me
Obtén la información de tu cuenta: saldo de créditos, nivel y solicitudes totales. Gratis.
Respuesta:
\``json { "credits": 1994, "plan\_credits": 1994, "purchased\_credits": 0, "is\_free\_tier": true, "credits\_reset\_at": "2026-11-04T12:00:00Z", "total\_requests": 3, "api\_key": null, "last\_login\_method": "github", "email": "user@example.com", "email\_verified": true, "github\_username": "octocat", "has\_billing\_portal": false, "new\_ip\_alerts\_enabled": true } \``\
credits\: saldo total disponible para gastar,plan\_credits\(créditos del nivel gratuito) máspurchased\_credits\.credits\_reset\_at\: cuándo se restablecen los créditos del nivel gratuito a 2,000 (cada 30 días). Los créditos comprados nunca se restablecen.api\_key\: siempre null cuando llamas con una clave de API. Solo se completa para el panel de control con sesión iniciada.
GET /v1/auth/transactions
Obtén tus últimas 50 recargas y ajustes de cuenta, de más reciente a más antiguo. Los cargos y reembolsos por solicitud no se listan aquí; usa /v1/auth/usage\ para esos.
Respuesta:
\``json \[ { "id": "uuid", "type": "credit", "amount": 4000, "endpoint": "top-up", "balance\_after": 5994, "created\_at": "2026-10-08T12:00:00Z" } \] \``\
GET /v1/auth/usage
Obtén el desglose de tu uso diario. Las filas son por día, endpoint y clave de API, con el día más reciente primero, por lo que un día puede tener varias filas para el mismo endpoint si usas más de una clave.
Parámetros de consulta:
days\: 1-90, predeterminado 7. Los valores fuera de rango se ajustan.offset\_days\: predeterminado 0. Omite esta cantidad de días recientes, para paginar hacia atrás en el tiempo.api\_key\_id\: opcional. Solo cuenta las solicitudes hechas con esta clave (su id del panel de control).
\``bash curl "https://sofya.co/v1/auth/usage?days=30" \\ -H "Authorization: Bearer ay\_live\_..." \``\
Respuesta:
\``json \[ { "date": "2026-10-08", "endpoint": "/v1/search", "request\_count": 3, "total\_credits": 6 } \] \``\ --- ## Créditos y Facturación Cada llamada se paga en créditos. Un crédito equivale a $0.0025. | Llamada | Créditos | USD | |------|---------|-----| | búsqueda (fragmentos) | 1 | $0.0025 | | búsqueda (básica) | 2 | $0.005 | | include_answer | +10 | +$0.025 | | fetch | 2 por URL | $0.005 por URL | | extract | 10 | $0.025 | | research | 50 | $0.125 | **Compra de créditos:** recarga desde el panel de control en dólares completos, desde $10 (4,000 créditos) hasta $25,000. Los créditos comprados no se ven afectados por el reinicio del nivel gratuito. No existe un endpoint de API para comprar créditos. **Nivel gratuito:** las cuentas creadas al iniciar sesión con una cuenta de GitHub que tenga al menos 1 año de antigüedad reciben 2,000 créditos, que se restablecen a 2,000 cada 30 días. Los créditos gratuitos no utilizados no se acumulan. Vincular GitHub a una cuenta de correo electrónico existente no agrega el nivel gratuito, y los registros por correo electrónico (enlace mágico) comienzan con 0 créditos en el modelo de pago por uso. **Qué se cobra:** solo una respuesta 2xx, exactamente en su credits\_used\. Consulta Facturación en errores a continuación. --- ## Límites de Velocidad Las solicitudes están limitadas a **30 solicitudes por segundo por clave de API**. REST (/v1/\*\) y MCP (/mcp\) se cuentan por separado, por lo que cada uno tiene sus propios 30 por segundo. Si superas el límite, la API devuelve 429 Too Many Requests\ con un encabezado Retry-After\ que indica el número entero de segundos que debes esperar antes de reintentar. **Respuesta 429:** \`` HTTP/1.1 429 Too Many Requests Retry-After: 1 { "detail": "Rate limit exceeded. 30 requests per second." } \``\ Las solicitudes limitadas por velocidad no consumen créditos. Implementa retroceso exponencial o respeta el encabezado Retry-After\ para obtener los mejores resultados. --- ## Códigos de Error | Código | Estado | Descripción | |------|--------|-------------| | 400 | Solicitud Incorrecta | Un problema a nivel de solicitud que el esquema no puede detectar, por ejemplo, una lista urls\ vacía en fetch | | 401 | No Autorizado | Clave de API inválida o faltante, o la IP de la solicitud no está en la lista de permitidos de la clave (aplica a cada endpoint /v1\, incluido /v1/auth/\*\) | | 402 | Pago Requerido | Créditos insuficientes | | 422 | Entidad No Procesable | Parámetros inválidos: un campo faltante o de tipo incorrecto, search\_depth\ o topic\ desconocido, formato freshness\ incorrecto (o de más de 25 caracteres), dominios que no son nombres de host simples, más de 10 urls o dominios, una consulta de búsqueda de más de 2,048 caracteres, o una consulta de research o un prompt de extract de más de 4,096. También extract cuando la página de destino no se pudo obtener (404, bloqueada, sin DNS, tiempo agotado, URL rechazada); verifica la URL, reintentar rara vez ayuda. | | 429 | Demasiadas Solicitudes | Limitado por velocidad. Consulta el encabezado Retry-After\. | | 502 | Puerta de Enlace Incorrecta | Error interno. Reintenta la solicitud. | | 503 | Servicio No Disponible | La capacidad de búsqueda no está disponible temporalmente. Reintenta en breve. | | 504 | Tiempo de Puerta de Enlace Agotado | La solicitud alcanzó su límite de tiempo (60s; 55s para research). Reintenta, o para research prueba una consulta más específica o menos fuentes. | **Facturación en errores:** solo se cobra una respuesta 2xx, exactamente en su credits\_used\. Ninguna respuesta de error se cobra: los créditos se reservan cuando comienza la solicitud y se reembolsan automáticamente si falla o se agota el tiempo. Dentro de un 200, algunas partes también se reembolsan: en fetch, cada URL que devuelve success: false\; en búsqueda, los 10 créditos include\_answer\ cuando no se produjo una respuesta (answer: null\). Research que no encuentra fuentes falla y se reembolsa. Extract con low\_content: true\ es un 200 y se cobra. Por lo tanto, en cualquier no-2xx, es seguro reintentar. **A través de MCP**, los errores llegan como un resultado de herramienta con isError: true\ y un mensaje en lugar de un código de estado HTTP. Se aplican las mismas reglas de facturación. --- ## Estado y Monitoreo **Salud por endpoint:** GET /v1/status\ (sin clave de API) informa la salud de búsqueda, fetch, extract y research medida por la propia Sofya. \``json { "overall": "operational", "endpoints": { "search": { "status": "operational", "latency\_ms": 1905.9, "last\_check": "2026-10-09T14:00:27+00:00" }, "fetch": { "status": "operational", "latency\_ms": 4116.3, "last\_check": "2026-10-09T13:59:47+00:00" }, "extract": { "status": "operational", "latency\_ms": 6437.9, "last\_check": "2026-10-09T13:59:18+00:00" }, "research": { "status": "operational", "latency\_ms": 16049.8, "last\_check": "2026-10-09T13:59:18+00:00" } }, "incidents": \[\] } \``\ - overall\: operational\, degraded\, partial\_outage\, major\_outage\, o checking\ (mientras las verificaciones aún no han informado). - endpoints.\*.status\: operational\, degraded\, down\, o unknown\. latency\_ms\ es una latencia típica reciente y puede ser nula. Un endpoint también puede llevar latency\_variants\ (por ejemplo, fragmentos de búsqueda vs básica). - incidents\: incidentes recientes. **Registro de cambios:** GET /v1/updates\ (sin clave de API) devuelve la lista de cambios visibles para el usuario como JSON. **Estado del sitio web:** status.sofya.co se ejecuta en un servidor separado y verifica el sitio web (sofya.co) cada 60 segundos, por lo que sigue informando incluso si Sofya está caída. No sondea los endpoints individuales de la API; usa /v1/status\ para esos. Sus feeds son públicos, no se necesita clave de API: - https://status.sofya.co/api/v2/summary.json\ - Compatible con Atlassian Statuspage, para herramientas que ya leen feeds de Statuspage. - https://status.sofya.co/api/v2/status.json\ - Solo metadatos de página e indicador general. Sondeo más económico si solo necesitas saber si está activo o caído. - https://status.sofya.co/api/status.json\ - Feed más rico: última latencia, tiempo de actividad de 24h y 90d, último error. El campo indicator\ sigue la convención de Statuspage: none\ (operativo), minor\ (degradado), o major\ (caído).