> For the complete documentation index, see [llms.txt](https://docs.clore.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.clore.ai/guides/guides_v2-es/avanzado/python-sdk.md).

# Guía del SDK de Python

Guía completa del SDK de Python — clientes síncronos/asíncronos, filtrado del marketplace, ciclo de vida de pedidos, mercado al contado, operaciones de monedero y manejo de errores

{% hint style="success" %}
**¿Nuevo en el SDK?** Empieza por la [guía rápida de 5 minutos](/guides/guides_v2-es/primeros-pasos/python-quickstart.md) primero.
{% endhint %}

Para un tutorial rápido con ejemplos reales, consulta [Clore.ai Python SDK — Automatiza tus flujos de trabajo de GPU en 5 minutos](https://blog.clore.ai/cloreai-python-sdk-automate-your-gpu-workflows-in-5-minutes/)

## Instalación

```bash
pip install clore-ai
```

El SDK proporciona dos clientes:

* **`CloreAI`** — síncrono (más simple, bueno para scripts)
* **`AsyncCloreAI`** — asíncrono (más rápido para operaciones concurrentes)

Ambos comparten los mismos métodos y devuelven los mismos modelos Pydantic.

***

## Síncrono vs asíncrono — cuándo usar cada uno

| Caso de uso                       | Cliente        | Por qué                                        |
| --------------------------------- | -------------- | ---------------------------------------------- |
| Scripts simples, tareas puntuales | `CloreAI`      | Código más simple, sin `async/await`           |
| Bucles de monitoreo               | `CloreAI`      | Las comprobaciones secuenciales funcionan bien |
| Consultas masivas al marketplace  | `AsyncCloreAI` | Solicitudes concurrentes = más rápido          |
| Creación de pedidos por lotes     | `AsyncCloreAI` | Crear varios pedidos en paralelo               |
| Aplicaciones web                  | `AsyncCloreAI` | E/S sin bloqueo                                |

### Ejemplo síncrono

```python
from clore_ai import CloreAI

client = CloreAI()  # Usa la variable de entorno CLORE_API_KEY

servers = client.marketplace(gpu="RTX 4090")
print(f"Encontrados {len(servers)} servidores")

client.close()  # O usa un gestor de contexto
```

### Ejemplo asíncrono

```python
import asyncio
from clore_ai import AsyncCloreAI

async def main():
    async with AsyncCloreAI() as client:
        servers = await client.marketplace(gpu="RTX 4090")
        print(f"Encontrados {len(servers)} servidores")

asyncio.run(main())
```

### Gestores de contexto (recomendado)

Ambos clientes admiten gestores de contexto para limpieza automática:

```python
# Síncrono
with CloreAI() as client:
    wallets = client.wallets()

# Asíncrono
async with AsyncCloreAI() as client:
    wallets = await client.wallets()
```

***

## Configuración del cliente

```python
client = CloreAI(
    api_key="your_key",      # O establece la variable de entorno CLORE_API_KEY
    base_url="https://api.clore.ai/v1",  # Punto final personalizado de la API
    timeout=30.0,            # Tiempo de espera de la solicitud en segundos
    max_retries=3            # Reintentos en caso de limitación de tasa / errores de red
)
```

El SDK incluye un limitador de tasa integrado:

* **Solicitudes generales:** 1 solicitud/segundo
* **`create_order`:** tiempo de espera de 5 segundos entre llamadas
* **Errores de límite de tasa (código 5):** Retroceso exponencial automático

***

## Filtrado del marketplace

El `marketplace()` el método obtiene todos los servidores disponibles y filtra en el cliente:

```python
from clore_ai import CloreAI

client = CloreAI()

# Todos los servidores disponibles
all_servers = client.marketplace()

# Filtrar por modelo de GPU (coincidencia de subcadena sin distinción de mayúsculas/minúsculas)
rtx_4090s = client.marketplace(gpu="RTX 4090")

# Filtrar por múltiples criterios
budget_gpus = client.marketplace(
    gpu="RTX 4090",
    max_price_usd=1.0,       # Máx. $1.00/hora
    min_gpu_count=2,          # Al menos 2 GPU
    min_ram_gb=64.0,          # Al menos 64 GB de RAM del sistema
    available_only=True       # Solo servidores disponibles (predeterminado)
)
```

### Filtrado avanzado (en el cliente)

Para filtros que no están integrados en el método, filtra el `Servidor` objetos devueltos por ti mismo:

```python
servers = client.marketplace(gpu="RTX 4090")

# Servidores en la UE con alta fiabilidad
eu_servers = [
    s for s in servers
    if s.location and s.location.upper() in ("DE", "FR", "NL", "FI")
    and s.reliability and s.reliability >= 0.95
]

# Ordenar por precio
cheapest = sorted(servers, key=lambda s: s.price_usd or float("inf"))
print(f"Más barato: Servidor {cheapest[0].id} — ${cheapest[0].price_usd:.4f}/h")
```

### Campos del modelo de servidor

Cada `MarketplaceServer` objeto tiene estos atributos y propiedades de conveniencia:

| Campo            | Tipo                  | Descripción                                                          |
| ---------------- | --------------------- | -------------------------------------------------------------------- |
| `id`             | `int`                 | ID del servidor (úsalo en `create_order`)                            |
| `gpu_model`      | `str \| None`         | Descripción de la GPU a partir de las especificaciones (propiedad)   |
| `gpu_count`      | `int`                 | Número de GPU de `gpu_array` (propiedad)                             |
| `ram_gb`         | `float \| None`       | RAM del sistema en GB (propiedad, de `specs.ram`)                    |
| `price_usd`      | `float \| None`       | Precio bajo demanda en USD (propiedad, de `price.usd.on_demand_usd`) |
| `spot_price_usd` | `float \| None`       | Precio spot en USD (propiedad)                                       |
| `disponibles`    | `bool`                | Si el servidor no está alquilado (propiedad)                         |
| `location`       | `str \| None`         | Código de país de las especificaciones de red (propiedad)            |
| `specs`          | `ServerSpecs \| None` | Especificaciones de hardware (cpu, ram, disco, gpu, red)             |
| `price`          | `ServerPrice \| None` | Estructura completa de precios                                       |
| `rented`         | `bool \| None`        | Si el servidor está actualmente alquilado                            |

***

## Gestión de pedidos

### Creación de pedidos

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="on-demand",               # "on-demand" o "spot"
    currency="bitcoin",             # Moneda de pago
    ssh_password="MySecurePass",    # Acceso SSH
    ports={"22": "tcp", "8888": "http"},  # Asignaciones de puertos
    env={"HF_TOKEN": "hf_xxx"},    # Variables de entorno
    command="bash /start.sh",       # Comando de inicio personalizado
    jupyter_token="my_token"        # Token de Jupyter Notebook
)

print(f"ID del pedido: {order.id}")
print(f"IP: {order.pub_cluster}")
print(f"Puertos: {order.tcp_ports}")
```

### Referencia `create_order` Parámetros

| Parámetro            | Tipo    | Requerido | Descripción                               |
| -------------------- | ------- | --------- | ----------------------------------------- |
| `server_id`          | `int`   | ✅         | Servidor a alquilar                       |
| `image`              | `str`   | ✅         | Imagen Docker                             |
| `type`               | `str`   | ✅         | `"on-demand"` o `"spot"`                  |
| `currency`           | `str`   | ✅         | Moneda de pago (p. ej., `"bitcoin"`)      |
| `ssh_password`       | `str`   | —         | Contraseña SSH                            |
| `ssh_key`            | `str`   | —         | Clave pública SSH                         |
| `ports`              | `dict`  | —         | Asignaciones de puertos (`{"22": "tcp"}`) |
| `env`                | `dict`  | —         | Variables de entorno                      |
| `jupyter_token`      | `str`   | —         | Token de Jupyter Notebook                 |
| `command`            | `str`   | —         | Comando de inicio                         |
| `spot_price`         | `float` | —         | Precio de oferta spot                     |
| `required_price`     | `float` | —         | Precio requerido                          |
| `autossh_entrypoint` | `str`   | —         | Punto de entrada Auto SSH                 |

### Listado de pedidos

```python
# Solo pedidos activos
active = client.my_orders()
for o in active:
    print(f"Pedido {o.id}: tipo={o.type}, IP={o.pub_cluster}, estado={o.status}")

# Incluir pedidos completados
all_orders = client.my_orders(include_completed=True)
```

### Campos del modelo de pedido

| Campo         | Tipo            | Descripción                                       |
| ------------- | --------------- | ------------------------------------------------- |
| `id`          | `int`           | ID del pedido                                     |
| `server_id`   | `int \| None`   | ID del servidor alquilado                         |
| `type`        | `str`           | `"on-demand"` o `"spot"`                          |
| `status`      | `str \| None`   | Estado del pedido                                 |
| `image`       | `str \| None`   | Imagen Docker                                     |
| `currency`    | `str \| None`   | Moneda de pago                                    |
| `price`       | `float \| None` | Precio                                            |
| `pub_cluster` | `str \| None`   | IP pública / nombre de host                       |
| `tcp_ports`   | `dict \| None`  | Asignaciones de puertos (p. ej., `{"22": 50022}`) |
| `created_at`  | `str \| None`   | Marca de tiempo de creación                       |

### Monitoreo de pedidos

```python
import time

def wait_for_ready(client, order_id, timeout=120):
    """Espera a que un pedido obtenga una IP pública."""
    for _ in range(timeout // 10):
        orders = client.my_orders()
        order = next((o for o in orders if o.id == order_id), None)
        if order and order.pub_cluster:
            return order
        time.sleep(10)
    raise TimeoutError(f"El pedido {order_id} no estuvo listo después de {timeout}s")

# Uso
order = client.create_order(server_id=142, image="cloreai/ubuntu22.04-cuda12", type="on-demand", currency="bitcoin")
ready = wait_for_ready(client, order.id)
print(f"SSH: ssh root@{ready.pub_cluster} -p {ready.tcp_ports.get('22', 22)}")
```

### Cancelación de pedidos

```python
# Cancelar con motivo opcional
client.cancel_order(order_id=38, issue="Trabajo completado")

# Cancelar todos los pedidos activos
orders = client.my_orders()
for order in orders:
    client.cancel_order(order.id, issue="Limpieza")
    print(f"Pedido cancelado {order.id}")
```

***

## Gestión de servidores (para hosters)

Si alojas GPU en Clore, el SDK te permite administrar tus servidores:

### Lista tus servidores

```python
my_servers = client.my_servers()
for s in my_servers:
    print(f"Servidor {s.id}: {s.gpu_model} — {s.status}")
```

### Obtener configuración del servidor

```python
config = client.server_config("MyGPU-Rig")
print(f"Nombre: {config.name}")
print(f"Visibilidad: {config.visibility}")
print(f"En línea: {config.online}")
print(f"Alquiler mínimo: {config.mrl}h")
print(f"Precio bajo demanda: {config.on_demand_price}")
print(f"Precio spot: {config.spot_price}")
```

### Actualizar la configuración del servidor

```python
client.set_server_settings(
    name="MyGPU-Rig",
    availability=True,       # Hacer que el servidor esté disponible
    mrl=24,                  # Alquiler mínimo de 24 h
    on_demand=0.0001,        # Precio bajo demanda en BTC
    spot=0.00000113          # Precio spot en BTC
)
print("Configuración actualizada")
```

***

## Mercado spot

Los pedidos spot pueden interrumpirse si alguien supera tu oferta. Aproximadamente un tercio de los servidores fija spot por debajo del precio bajo demanda (mediana \~13% menos); el resto lista spot al precio bajo demanda.

### Ver ofertas spot

```python
offers = client.spot_marketplace(server_id=6)
for offer in offers:
    print(f"Pedido {offer.get('order_id')}: precio={offer.get('price')}")
```

### Crear un pedido spot

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="spot",
    currency="bitcoin",
    spot_price=0.0001,       # Tu precio de oferta
    ssh_password="MyPass"
)
print(f"Pedido spot {order.id} creado")
```

### Ajustar el precio spot

```python
# Sube tu oferta para evitar que te superen
client.set_spot_price(order_id=39, price=0.000003)
```

### Estrategia de puja spot

```python
from clore_ai import CloreAI

client = CloreAI()

def smart_spot_bid(server_id, premium_pct=5):
    """Ofrece un poco por encima del precio spot mínimo actual."""
    offers = client.spot_marketplace(server_id=server_id)
    if not offers:
        print("No hay ofertas spot — usa el precio bajo demanda como referencia")
        return None

    min_price = min(o["price"] for o in offers)
    bid = min_price * (1 + premium_pct / 100)
    print(f"Mínimo del mercado: {min_price}, ofertando: {bid:.8f} (+{premium_pct}%)")
    return bid

# Uso
bid = smart_spot_bid(server_id=142, premium_pct=10)
if bid:
    order = client.create_order(
        server_id=142,
        image="cloreai/ubuntu22.04-cuda12",
        type="spot",
        currency="bitcoin",
        spot_price=bid
    )
```

***

## Operaciones de cartera

### Comprobar saldos

```python
wallets = client.wallets()
for w in wallets:
    print(f"{w.name}: {w.balance:.8f}")
    if w.deposit:
        print(f"  Dirección de depósito: {w.deposit}")
```

### Alerta de saldo bajo

```python
from clore_ai import CloreAI

def check_balance(min_btc=0.001):
    """Alerta si el saldo BTC está por debajo del umbral."""
    client = CloreAI()
    wallets = client.wallets()

    for w in wallets:
        if w.name.lower() == "bitcoin" and w.balance < min_btc:
            print(f"⚠️  Saldo BTC bajo: {w.balance:.8f} (mínimo: {min_btc})")
            return False

    print("✅ Saldos correctos")
    return True

check_balance(min_btc=0.001)
```

***

## Mejores prácticas para el manejo de errores

### Jerarquía de excepciones

```
CloreAPIError (base)
├── DBError           (código 1) — error de base de datos
├── InvalidInputError (código 2) — entrada no válida
├── AuthError         (código 3) — clave API no válida
├── InvalidEndpointError (código 4) — endpoint incorrecto
├── RateLimitError    (código 5) — limitado por tasa (reintentado automáticamente)
└── FieldError        (código 6) — error específico de campo
```

### Manejo básico de errores

```python
from clore_ai import CloreAI
from clore_ai.exceptions import (
    CloreAPIError,
    AuthError,
    RateLimitError,
    InvalidInputError
)

client = CloreAI()

try:
    order = client.create_order(
        server_id=999999,
        image="cloreai/ubuntu22.04-cuda12",
        type="on-demand",
        currency="bitcoin"
    )
except AuthError:
    print("Clave API no válida — verifica CLORE_API_KEY")
except InvalidInputError as e:
    print(f"Entrada incorrecta: {e}")
except RateLimitError:
    print("Límite de tasa alcanzado — el SDK reintenta automáticamente, pero se excedieron los reintentos máximos")
except CloreAPIError as e:
    print(f"Error de API (código {e.code}): {e}")
```

### Patrón de reintento con retroceso

El SDK tiene reintentos integrados para límites de velocidad y errores de red (`max_retries=3`). Para reintentos a nivel de aplicación:

```python
import time
from clore_ai import CloreAI
from clore_ai.exceptions import CloreAPIError, RateLimitError

def retry_operation(func, max_attempts=3, base_delay=2.0):
    """Reintentar una operación de la API de Clore con retroceso exponencial."""
    for attempt in range(max_attempts):
        try:
            return func()
        except RateLimitError:
            if attempt < max_attempts - 1:
                delay = base_delay * (2 ** attempt)
                print(f"Límite de velocidad alcanzado, reintentando en {delay}s...")
                time.sleep(delay)
            else:
                raise
        except CloreAPIError as e:
            if e.code in (1,):  # Los errores de BD pueden ser transitorios
                if attempt < max_attempts - 1:
                    time.sleep(base_delay)
                    continue
            raise

# Uso
client = CloreAI()
servers = retry_operation(lambda: client.marketplace(gpu="RTX 4090"))
```

***

## Consejos de rendimiento

### 1. Reutiliza el cliente

```python
# ❌ Mal — crea una nueva conexión HTTP cada vez
for _ in range(10):
    client = CloreAI()
    client.marketplace()
    client.close()

# ✅ Bien — reutiliza la conexión HTTP
client = CloreAI()
for _ in range(10):
    client.marketplace()
client.close()
```

### 2. Usa async para operaciones concurrentes

```python
import asyncio
from clore_ai import AsyncCloreAI

async def compare_gpus():
    async with AsyncCloreAI() as client:
        # Ejecuta 3 búsquedas concurrentemente
        rtx4090, rtx3090, a100 = await asyncio.gather(
            client.marketplace(gpu="RTX 4090"),
            client.marketplace(gpu="RTX 3090"),
            client.marketplace(gpu="A100"),
        )

        print(f"RTX 4090: {len(rtx4090)} servidores")
        print(f"RTX 3090: {len(rtx3090)} servidores")
        print(f"A100: {len(a100)} servidores")

asyncio.run(compare_gpus())
```

### 3. Creación asíncrona de órdenes en lote

```python
import asyncio
from clore_ai import AsyncCloreAI

async def batch_deploy(server_ids):
    async with AsyncCloreAI() as client:
        tasks = [
            client.create_order(
                server_id=sid,
                image="cloreai/ubuntu22.04-cuda12",
                type="on-demand",
                currency="bitcoin",
                ssh_password="BatchPass123",
                ports={"22": "tcp"}
            )
            for sid in server_ids
        ]
        orders = await asyncio.gather(*tasks, return_exceptions=True)

        for sid, result in zip(server_ids, orders):
            if isinstance(result, Exception):
                print(f"Servidor {sid}: FALLÓ — {result}")
            else:
                print(f"Servidor {sid}: Orden {result.id} creada")

        return orders

# Despliega en 3 servidores a la vez
asyncio.run(batch_deploy([142, 305, 891]))
```

{% hint style="warning" %}
**Nota:** El SDK impone un tiempo de espera de 5 segundos entre `create_order` las llamadas. Incluso en modo asíncrono, las órdenes se espacian para respetar los límites de velocidad.
{% endhint %}

### 4. Cierra los clientes cuando termines

```python
# El gestor de contexto se encarga de esto automáticamente
with CloreAI() as client:
    # trabajar...
    pass  # client.close() se llama automáticamente

# O cerrar manualmente
client = CloreAI()
try:
    # trabajar...
    pass
finally:
    client.close()
```

***

## Ejemplo completo: autoescalado de trabajadores GPU

```python
import asyncio
import time
from clore_ai import AsyncCloreAI
from clore_ai.exceptions import CloreAPIError

async def auto_scale(
    gpu_model="RTX 4090",
    max_price=2.0,
    target_workers=3,
    image="cloreai/ubuntu22.04-cuda12"
):
    """Mantener un grupo de trabajadores GPU."""
    async with AsyncCloreAI() as client:
        # 1. Verificar las órdenes actuales
        current_orders = await client.my_orders()
        active_count = len(current_orders)
        print(f"Trabajadores activos: {active_count}/{target_workers}")

        if active_count >= target_workers:
            print("Ya se alcanzó el objetivo. No hay nada que hacer.")
            return

        # 2. Encontrar servidores disponibles
        servers = await client.marketplace(gpu=gpu_model, max_price_usd=max_price)
        servers.sort(key=lambda s: s.price_usd or float("inf"))

        needed = target_workers - active_count
        candidates = servers[:needed]

        if len(candidates) < needed:
            print(f"Solo hay {len(candidates)} servidores disponibles (se necesitan {needed})")

        # 3. Desplegar
        for server in candidates:
            try:
                order = await client.create_order(
                    server_id=server.id,
                    image=image,
                    type="on-demand",
                    currency="bitcoin",
                    ssh_password="WorkerPass123",
                    ports={"22": "tcp"}
                )
                print(f"Desplegado en el servidor {server.id} → orden {order.id}")
            except CloreAPIError as e:
                print(f"No se pudo desplegar en {server.id}: {e}")

asyncio.run(auto_scale())
```

***

## Siguientes pasos

* [Automatización de CLI](/guides/guides_v2-es/avanzado/cli-automation.md) — Scripts Bash, CI/CD, operaciones por lotes
* [Procesamiento por lotes](/guides/guides_v2-es/avanzado/batch-processing.md) — Procesa cargas de trabajo grandes en GPUs de Clore
* [Integración de API](/guides/guides_v2-es/avanzado/api-integration.md) — Conecta servicios de IA a tus aplicaciones


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-es/avanzado/python-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
