Lista de verificación previa
Repásala antes de empezar para evitar retrocesos.
- Un correo electrónico activo para el registro.
- Requisito: tener un entorno de ejecución disponible: Python 3, JDK 15 o superior (para usar bloques de texto), Go, o PHP con la extensión curl.
- Ser mayor de 18 años, ya que el servicio es exclusivo para adultos.
- Saber que buscas conversación de texto puro: hay un solo modelo, sin embeddings, imágenes, audio ni fine-tuning.
- Usar variables de entorno para la clave, no guardarla en el código ni subirla al repositorio.
El contrato de la API es mínimo: la URL es https://api.wushenchaapi.com/v1 y es compatible con el formato de finalización de chat de OpenAI, por lo que puedes reutilizar casi todo tu cuerpo de petición anterior.
Estimación de tiempo: el registro toma un minuto, la instalación depende de tu entorno, y la primera petición en sí misma no supera los 30 segundos. Si tarda más, suele ser un problema de red o de clave; salta a la lista de solución de problemas al final. Sigue este orden: primero models, luego chat, primero síncrono, luego streaming.
Registro y obtención de la clave
Visita /get-api-key/ y regístrate con correo y contraseña. La clave se muestra al instante; cópiala. Detalles importantes:
- Cada cuenta tiene una sola clave de API.
- Puedes regenerarla, pero la anterior caducará inmediatamente. Despliega el nuevo valor antes de cambiarlo en producción.
- La cuenta nueva incluye $0.50 de crédito de prueba válido por 7 días, sin necesidad de datos de pago.
- Si el crédito se agota o vence, recibirás un 402 con el código
no_credit. Recarga tu crédito prepago para continuar; no es suscripción y el saldo no caduca.
Guarda la clave en variables de entorno: en macOS/Linux ejecuta export API_KEY=tu_clave, en Windows PowerShell usa $env:API_KEY="tu_clave". Todos los ejemplos leen desde API_KEY.
Paso 1: verificar conectividad con /v1/models
No envíes chats aún. Haz primero una petición GET sin costo para confirmar que la URL, la red y la clave funcionan:
curl https://api.wushenchaapi.com/v1/models \
-H "Authorization: Bearer $API_KEY"Si recibes 200 y la lista de modelos (solo uncensored), estás conectado. Un 401 indica clave incorrecta o ausente; un timeout indica problemas de red o proxy. Separar los problemas de conectividad de los del cuerpo de la petición es el hábito que más tiempo te ahorra.
Python: requests
Sin SDK, un requests.post basta. Tres puntos: timeout obligatorio; verifica r.ok antes de choices; error: {"error":{"code":...,"message":...}}.
import os
import requests
url = "https://api.wushenchaapi.com/v1/chat/completions"
headers = {
"Authorization": "Bearer " + os.environ["API_KEY"],
"Content-Type": "application/json",
}
payload = {
"model": "uncensored",
"messages": [{"role": "user", "content": "用两句话描述一场雨夜里的追逐戏。"}],
"max_tokens": 300,
}
r = requests.post(url, headers=headers, json=payload, timeout=120)
print(r.status_code)
data = r.json()
if r.ok:
print(data["choices"][0]["message"]["content"])
print(data["usage"])
else:
print(data["error"]["code"], data["error"]["message"])En caso de éxito, usage contiene prompt_tokens y completion_tokens. Se recomienda imprimir estos valores en cada ejecución para tener control sobre los costos.
Análisis campo por campo del cuerpo de la petición
Los cuatro lenguajes envían el mismo JSON. Entiéndelo primero; luego es solo traducir la sintaxis.
| Campo | Obligatorio | Descripción |
|---|---|---|
| model | Sí | Fijo en uncensored. |
| messages | Sí | Array de objetos con role y content. Roles válidos: system, user, assistant. |
| max_tokens | No | Por defecto 2048, máximo 16,000. Ajusta este valor para textos largos. |
| temperature / top_p / stop | No | Parámetros de muestreo estándar que se reenvían tal cual. |
| stream | No | Cuando es true, se usa streaming SSE. |
Lo que más sueles leer en la respuesta son tres campos: choices[0].message.content es el contenido, choices[0].finish_reason indica si finalizó normalmente o por truncamiento, y usage muestra el consumo de tokens. Leer los tres asegura una integración completa.
Ten en cuenta estas limitaciones estrictas: el cuerpo de la petición no debe superar los 8 MB y tienes un límite de 300 peticiones por minuto para cada clave. No lances todas las tareas en paralelo de golpe; establece primero un límite de concurrencia.
Java: java.net.http
JDK 11 incluye HttpClient sin dependencias externas. Los ejemplos usan bloques de texto de JDK 15 para JSON; en versiones inferiores puedes usar concatenación de cadenas o tu librería JSON familiar para serializar.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Demo {
public static void main(String[] args) throws Exception {
String key = System.getenv("API_KEY");
String body = """
{"model":"uncensored",
"messages":[{"role":"user","content":"写一段两百字以内的悬疑小说开头。"}],
"max_tokens":400}
""";
HttpRequest req = HttpRequest.newBuilder(
URI.create("https://api.wushenchaapi.com/v1/chat/completions"))
.timeout(Duration.ofSeconds(120))
.header("Authorization", "Bearer " + key)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> resp = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.statusCode());
System.out.println(resp.body());
}
}Consejo: cuando el contenido tiene chino, BodyPublishers.ofString usa por defecto codificación UTF-8, no hace falta gestionar nada extra. En producción, convierte HttpClient en un singleton para reutilizarlo; no crees uno nuevo en cada petición.
Go: net/http
La librería estándar de Go también es suficiente. Lo importante es no usar http.DefaultClient sin configurar un tiempo de espera; de lo contrario, las goroutines se quedarán colgadas si el servidor remoto se bloquea.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"time"
)
func main() {
body := []byte(`{"model":"uncensored","messages":[{"role":"user","content":"给一个反派角色写三句独白。"}],"max_tokens":300}`)
req, err := http.NewRequest("POST", "https://api.wushenchaapi.com/v1/chat/completions", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 120 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(out))
}Lee el cuerpo de la respuesta con io.ReadAll antes de analizarlo. Para procesarlo de forma estructurada, define la struct correspondiente y deserializa con encoding/json; los campos son choices, message, content y usage.
PHP: curl
PHP usa la extensión curl. Recuerda añadir JSON_UNESCAPED_UNICODE a json_encode; de lo contrario, el chino se convertirá en \uXXXX. Funcionará, pero será difícil de leer al depurar.
<?php
$payload = [
"model" => "uncensored",
"messages" => [["role" => "user", "content" => "写一首八行的现代诗,主题是末班地铁。"]],
"max_tokens" => 300,
];
$ch = curl_init("https://api.wushenchaapi.com/v1/chat/completions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$res = curl_exec($ch);
if ($res === false) {
die("curl 错误: " . curl_error($ch) . "\n");
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $status, "\n";
$data = json_decode($res, true);
echo $data["choices"][0]["message"]["content"] ?? $res, "\n";Distingue dos tipos de fallo: si curl_exec devuelve false, es un problema de capa de red; si devuelve contenido pero el código de estado no es 200, es un problema de capa de interfaz y debes leer error.code.
Trampas comunes en la primera llamada
- 401: Escribiste el header
Authorization: clavey omitiste el prefijoBearer; o la variable de entorno no se aplicó en la nueva terminal. - 404: Faltaba
/v1en la ruta, o se escribió malchat/completions. - 400: JSON inválido, o el prompt con max_tokens supera el límite de 100,000 tokens.
- 402: Saldo agotado o crédito de prueba gratis expirado.
- Salida truncada:
max_tokenstiene un valor por defecto de 2048, con un máximo de 16,000 por llamada; para textos largos debes aumentarlo explícitamente. - Quieres streaming: añade
"stream": trueal cuerpo de la petición. La respuesta es SSE y al final se añade automáticamente un bloque de datos con usage.
Una vez que funcione, el siguiente paso es ver Cómo escribir prompts para mejorar la calidad de la salida. Si encuentras errores, consulta el manual de códigos de error y resolución de problemas. La descripción completa de parámetros está en la documentación y los precios en la página de precios.
Una vez que funcione, añade tres cosas antes de ir a producción
Los ejemplos del tutorial son la versión mínima viable; si vas a integrarlo en un servicio real, al menos añade estos tres elementos.
- Tiempo de espera. El ejemplo establece 120 segundos porque la generación de texto largo es lenta. Si tu negocio espera de forma síncrona, reduce el tiempo según la tolerancia de la página y combina con streaming para que el usuario vea el texto primero.
- Desvío por error.401, 402 y 403 no se resuelven con reintentos; requieren alerta o aviso al usuario. Solo 429 y 503 justifican reintentos con retroceso. La implementación está en el manual de solución de problemas.
- Registro de uso. Registra una línea de log con el usage de cada respuesta. Guarda solo los números, no escribas el contenido del usuario en los logs. Esto es vital para conciliar cuentas a fin de mes y detectar consumos anómalos.
Un último comentario sobre la gestión de claves: solo hay una clave; si se filtra, solo puedes regenerarla, y al regenerarla todos los servicios en uso se desconectan simultáneamente. No la disperses en múltiples scripts o configuraciones de varias máquinas; centralízala en un único lugar de configuración de claves para que el cambio sea limpio y sin omisiones.
Por último, los límites de contenido: el contenido adulto legal, la ficción novelada y los temas controvertidos no se rechazan, pero el contenido sexual que involucre a menores se bloquea siempre, devolviendo 403, incluso en ficción o roleplay. Si tu aplicación está dirigida a adultos, se recomienda añadir una verificación de edad en tu propio flujo de registro.
Te dejo un orden de autoverificación, que puedes pegar al lado del monitor: ¿está la clave en las variables de entorno? ¿Funciona el endpoint de models? ¿Es el cuerpo de la petición un JSON válido? ¿El modelo es uncensored? ¿max_tokens es suficiente? ¿En la respuesta, finish_reason es stop o length? ¿Se ha registrado usage? Si las siete casillas están en verde, la integración está completa.