Referencia
Códigos de error
Todos los endpoints devuelven los mismos códigos, con la misma forma de cuerpo. Esta página dice qué hacer con cada uno.
Referencia completa
| Código | Significado | Causa frecuente | Solución |
|---|---|---|---|
400 | Bad Request | Cuerpo JSON ilegible, parámetro ausente o fuera de límites. | Comprueba el formato de los parámetros. El campo error nombra exactamente el que da problemas. |
401 | Unauthorized | Clave API ausente, inválida o revocada. | Comprueba la cabecera Authorization: Bearer sr_live_…. |
402 | Payment Required | Créditos insuficientes: saldo nulo o inferior al coste de la acción pedida. | Recarga en subreply.io/billing. |
422 | Unprocessable | Publicación de Reddit bloqueada, archivada o eliminada, o cuenta de publicación no disponible. | Elige una publicación reciente y comentable. Reintentar igual no cambiará nada. |
500 | Internal Server Error | Fallo de la cadena de IA o del scraping. | Reintenta en 30 s; contacta con soporte si el error persiste. |
503 | Service Unavailable | Servicio de publicación no disponible o ninguna cuenta con la que publicar. | Reintenta en 60 s. |
202 no es un error
POST /api/v1/publish puede responder 202: la petición ha salido, la publicación no está confirmada y no se descuenta nada. No es un fallo que haya que reintentar — ver publicar un comentario.Formato de los errores
Toda respuesta fallida lleva un objeto JSON con un único campo, error, en francés y legible por una persona:
Cuerpo de error
{
"error": "Message explicite en français"
}No hay código de error interno ni campo details: el estado HTTP lleva la categoría y el mensaje lleva el detalle. Nunca analices el texto del mensaje para decidir una acción — bifurca según el estado.
Gestión de errores en producción
Con tres reglas basta para sostener un flujo:
- Reintentar 500 y 503, con una espera creciente y dos intentos como máximo. Son los únicos códigos que se resuelven solos.
- Cortar en 402 y avisar: todas las llamadas siguientes fallarán igual mientras no se recargue el saldo.
- No reintentar nunca 400, 401 ni 422 — hay algo que corregir en la petición, en la clave o en la publicación elegida.
Cliente con reintentos y control de créditos
type ApiError = { error: string };
// Reintenta solo lo que merece la pena: 503 (servicio de publicación
// momentáneamente no disponible) y 500 (fallo de la cadena de IA o del scraping).
// Un 400, un 401 o un 422 no se arreglan solos.
const RETRYABLE = new Set([500, 503]);
async function callSubreply<T>(
endpoint: string,
body: unknown,
attempt = 0,
): Promise<T> {
const response = await fetch(`https://subreply.io/api/v1/${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SUBREPLY_API_KEY}`,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(180_000),
});
if (response.ok) return (await response.json()) as T;
const { error } = (await response.json()) as ApiError;
// 402: sin créditos. No sirve de nada insistir — cortamos la cadena y
// avisamos, en vez de encadenar llamadas que van a fallar todas.
if (response.status === 402) {
await notifyOutOfCredits(error);
throw new Error(`Créditos agotados: ${error}`);
}
if (RETRYABLE.has(response.status) && attempt < 2) {
// 30 s y después 60 s: el tiempo que tarda un servicio en volver.
await new Promise((resolve) => setTimeout(resolve, 30_000 * (attempt + 1)));
return callSubreply<T>(endpoint, body, attempt + 1);
}
throw new Error(`SubReply ${response.status} : ${error}`);
}Las repeticiones no cuestan nada
La generación y la publicación se facturan de forma idempotente por URL de publicación: un segundo intento sobre la misma publicación devuelve
credits_used: 0. Un reintento tras un timeout es por tanto gratuito — en /publish, comprueba de todos modos la publicación antes de repetir, para no dejar dos comentarios.