Fragmentos de código.
Ejemplos para conectarte a la API desde distintos lenguajes y herramientas. Usa https://api.nan.builders/v1 como base URL y tu API key personal.
model: deepseek-v4-flash
generación de texto, chat y visión
curl
curl https://api.nan.builders/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Hello, how are you?"}],
"max_tokens": 500
}'
python (openai)
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Write a hello world in Rust"}],
max_tokens=500,
stream=True
)
for chunk in response:
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
Instalación: pip install openai
node.js (openai)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
const stream = await client.chat.completions.create({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "Write a hello world in Zig" }],
max_tokens: 500,
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) process.stdout.write(content);
}
Instalación: npm install openai
salida estructurada en deepseek-v4-flash
deepseek-v4-flash acepta response_format json_schema en peticiones a /chat/completions sin streaming y sin tools. En api.nan.builders lo rechaza con un 400 cuando la petición usa streaming, lleva tools o va a /responses (también con n mayor que 1, un esquema de más de 16 KB, un esquema no válido o un esquema que el gateway no valida (un $ref fuera del esquema, $dynamicRef o $anchor, un $id anidado o demasiado grande al resolver sus $ref); en api.helmcode.com se acepta en todos esos casos), y json_object solo garantiza JSON válido, no su forma. Para obtener una salida que siga un esquema en esas peticiones, define una única herramienta de tipo función cuyo parameters sea tu JSON Schema (raíz "type": "object", "strict": true) y fuérzala con tool_choice. El modelo devuelve argumentos que siguen el esquema en choices[0].message.tool_calls[0].function.arguments, como una cadena JSON. strict pide que se ajusten exactamente; se aplica donde el modelo admite decodificación estricta, así que valida los argumentos si tu código depende de ellos.
curl https://api.nan.builders/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Ana García is 34 and lives in Valencia."}],
"tools": [{
"type": "function",
"function": {
"name": "save_person",
"description": "Save the person mentioned in the text.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"],
"additionalProperties": false
}
}
}],
"tool_choice": {"type": "function", "function": {"name": "save_person"}}
}'
# → choices[0].message.tool_calls[0].function.arguments:
# {"name": "Ana García", "age": 34, "city": "Valencia"}
import json
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"],
"additionalProperties": False
}
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Ana García is 34 and lives in Valencia."}],
tools=[{
"type": "function",
"function": {
"name": "save_person",
"description": "Save the person mentioned in the text.",
"strict": True,
"parameters": schema
}
}],
tool_choice={"type": "function", "function": {"name": "save_person"}}
)
person = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
print(person) # {'name': 'Ana García', 'age': 34, 'city': 'Valencia'}
model: qwen3-embedding
embeddings vectoriales
curl
curl https://api.nan.builders/v1/embeddings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "qwen3-embedding",
"input": ["Hello world", "Hola mundo"],
"encoding_format": "float"
}'
# → 4096-dimensional vectors per input
python
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
response = client.embeddings.create(
model="qwen3-embedding",
input=["Kubernetes pod scheduling", "Pod scheduling in Kubernetes"],
encoding_format="float"
)
embeddings = [d.embedding for d in response.data]
print(len(embeddings[0])) # 4096
node.js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
const response = await client.embeddings.create({
model: "qwen3-embedding",
input: ["Hello world", "Hola mundo"],
encoding_format: "float",
});
const embeddings = response.data.map((d) => d.embedding);
console.log(embeddings[0].length); // 4096
model: rerank
reordenado semántico, completa el stack de RAG
curl
curl https://api.nan.builders/v1/rerank \
-H "Authorization: Bearer $NAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "rerank",
"query": "What is the capital of France?",
"documents": [
"Paris is the capital of France and home to the Eiffel Tower.",
"Berlin is the capital of Germany.",
"Madrid is the capital of Spain."
]
}'
# → results[] ordered by relevance_score desc, with original index
python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["NAN_API_KEY"],
base_url="https://api.nan.builders/v1"
)
# The /rerank endpoint is not part of the standard OpenAI client,
# but we can invoke it with client.post().
response = client.post(
path="/rerank",
cast_to=object,
body={
"model": "rerank",
"query": "What is the capital of France?",
"documents": [
"Paris is the capital of France and home to the Eiffel Tower.",
"Berlin is the capital of Germany.",
"Madrid is the capital of Spain.",
],
},
)
for r in response["results"]:
print(f"{r['index']}: {r['relevance_score']:.3f}")
También funciona con requests a pelo o con cualquier cliente HTTP: el endpoint es compatible con OpenAI tanto en la autenticación como en el formato del cuerpo.
model: kokoro
texto a voz
curl
curl https://api.nan.builders/v1/audio/speech \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "kokoro",
"input": "Welcome to NaN builders.",
"voice": "af_heart"
}' \
-o speech.mp3
# English female voice (af_heart), Spanish (ef_dora), etc.
# See all voices: https://github.com/hexgrad/Kokoro-82M
python
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
response = client.audio.speech.create(
model="kokoro",
voice="af_heart",
input="Hello, welcome to NaN builders.",
speed=1.0,
response_format="mp3"
)
response.stream_to_file("output.mp3")
# Spanish voice
response = client.audio.speech.create(
model="kokoro",
voice="ef_dora",
input="Hola, bienvenido a NaN builders.",
response_format="mp3"
)
node.js
import OpenAI from "openai";
import fs from "fs";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
const response = await client.audio.speech.create({
model: "kokoro",
voice: "af_heart",
input: "Hello, welcome to NaN builders.",
speed: 1.0,
response_format: "mp3",
});
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("output.mp3", buffer);
model: whisper
voz a texto
curl
# Transcribe audio file
curl https://api.nan.builders/v1/audio/transcriptions \
-H "Authorization: Bearer sk-your-key-here" \
-F "model=whisper" \
-F "file=@recording.mp3" \
-F "language=en"
# → {"text":"Transcribed text","language":"en","duration":5.2}
# Translate to English
curl https://api.nan.builders/v1/audio/translations \
-H "Authorization: Bearer sk-your-key-here" \
-F "model=whisper" \
-F "file=@recording.mp3"
python
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
# Transcribe English audio
with open("recording.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper",
file=f,
language="en",
response_format="verbose_json"
)
print(result.text) # Transcribed text
print(result.language) # "en"
print(result.duration) # 5.2 (seconds)
# Translate to English
with open("recording.mp3", "rb") as f:
translation = client.audio.translations.create(
model="whisper",
file=f
)
print(translation.text) # English translation
node.js
import OpenAI from "openai";
import fs from "fs";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
// Transcribe audio
const file = fs.createReadStream("recording.mp3");
const result = await client.audio.transcriptions.create({
model: "whisper",
file,
language: "en",
response_format: "verbose_json",
});
console.log(result.text); // Transcribed text
console.log(result.language); // "en"
console.log(result.duration); // 5.2
model: mimo-v2.6-flash
omnimodal: chat, visión y audio
curl
curl https://api.nan.builders/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "mimo-v2.6-flash",
"messages": [{"role": "user", "content": "Hello, how are you?"}],
"max_tokens": 500
}'
Los tokens de razonamiento cuentan dentro de max_tokens, que se usa tal cual lo envías. Con el razonamiento activado, un presupuesto pequeño puede gastarse entero en razonar y llegar con content vacío y finish_reason: "length". Si solo necesitas la respuesta, pon "reasoning_effort": "none"; si no, da un max_tokens generoso (los prompts densos pueden razonar miles de tokens) y usa stream: true. Mira Controlar el razonamiento.
visión (curl)
curl https://api.nan.builders/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "mimo-v2.6-flash",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
]
}],
"max_tokens": 500
}'
python (openai)
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
response = client.chat.completions.create(
model="mimo-v2.6-flash",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image."},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
]
}],
max_tokens=500
)
print(response.choices[0].message.content)
model: flux-2-klein
generación y edición de imágenes
curl
curl https://api.nan.builders/v1/images/generations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "flux-2-klein",
"prompt": "Un faro al atardecer sobre acantilados, fotográfico",
"size": "1024x1024",
"n": 1
}'
# → {"created":...,"data":[{"url":"https://..."}]}
Cada lado de size tiene que ser divisible entre 16 y estar entre 256 y 1536, con una relación de aspecto entre 1:3 y 3:1. n llega hasta 4.
python
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
image = client.images.generate(
model="flux-2-klein",
prompt="Un faro al atardecer sobre acantilados, fotográfico",
size="1024x1024",
extra_body={"seed": 42}
)
print(image.data[0].url)
El enlace es temporal, alrededor de 60 minutos. Pide response_format="b64_json" y los bytes llegan en data[0].b64_json, en base64, en lugar de detrás de un enlace. seed y guidance son extensiones de NaN, así que el SDK de OpenAI las manda por extra_body.
node.js (imagen a imagen)
import OpenAI from "openai";
import fs from "fs";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
const image = await client.images.edit({
model: "flux-2-klein",
image: fs.createReadStream("referencia.png"),
prompt: "Convierte la escena en invierno, con nieve",
size: "1024x1024",
});
console.log(image.data[0].url);
/images/edits acepta hasta cuatro imágenes de referencia (PNG, JPEG o WebP, de menos de 25 MB cada una) y no admite mask: mandar una devuelve 400.
Las imágenes necesitan membresía de inferencia, 403 si no la tienes, y van por su propio presupuesto: 20 peticiones por minuto y 100 al mes, que no toca tu cuota de tokens.
model: qwen-image-2.1
generación de texto a imagen
curl
curl https://api.nan.builders/v1/images/generations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"model": "qwen-image-2.1",
"prompt": "Una pizarra con la palabra faro escrita en ella, rotulación limpia",
"size": "1024x1024",
"n": 1
}'
# → {"created":...,"data":[{"url":"https://..."}]}
Cada lado de size tiene que ser divisible entre 16 y estar entre 512 y 1280, con una relación de aspecto entre 1:3 y 3:1. n llega hasta 4. Sin imágenes de referencia: este modelo es solo texto a imagen. seed es un entero en 0..2147483647.
python
from openai import OpenAI
client = OpenAI(
api_key="sk-your-key-here",
base_url="https://api.nan.builders/v1"
)
image = client.images.generate(
model="flux-2-klein",
prompt="Una pizarra con la palabra faro escrita en ella, rotulación limpia",
size="1024x1024",
extra_body={"seed": 42}
)
print(image.data[0].url)
El enlace es temporal, alrededor de 60 minutos. Pide response_format="b64_json" y los bytes llegan en data[0].b64_json, en base64, en lugar de detrás de un enlace. seed y guidance son extensiones de NaN, así que el SDK de OpenAI las manda por extra_body.
node.js (imagen a imagen)
import OpenAI from "openai";
import fs from "fs";
const client = new OpenAI({
apiKey: "sk-your-key-here",
baseURL: "https://api.nan.builders/v1",
});
const image = await client.images.edit({
model: "flux-2-klein",
image: fs.createReadStream("referencia.png"),
prompt: "Convierte la escena en invierno, con nieve",
size: "1024x1024",
});
console.log(image.data[0].url);
/images/edits acepta hasta cuatro imágenes de referencia (PNG, JPEG o WebP, de menos de 25 MB cada una) y no admite mask: mandar una devuelve 400.
Las imágenes necesitan membresía de inferencia, 403 si no la tienes, y van por su propio presupuesto: 20 peticiones por minuto y 100 al mes, que no toca tu cuota de tokens.
Conectar tu editor o tu agente
Las configuraciones de Cursor, Claude Code, Codex, Cline, OpenCode, Zed y el resto están en Configurar tu agente, con una página por herramienta.