---
name: comfyui_search_civitai_models
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: impure
signature: "def comfyui_search_civitai_models(query: str, *, types: str = \"Checkpoint\", base_model: str | None = None, sort: str = \"Highest Rated\", limit: int = 20, token: str | None = None) -> dict"
description: "Busca modelos/LoRAs en Civitai via su API publica GET /api/v1/models y normaliza cada resultado a {name, type, base_model, version_id, download_url, nsfw} (primera version del modelo). La busqueda publica funciona SIN token; el token solo sube el rate limit y desbloquea modelos restringidos. Impura: HTTP GET a civitai.com. Solo stdlib."
tags: [comfyui, ml, civitai, search, models, stable-diffusion, http]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["json", "urllib.error", "urllib.parse", "urllib.request"]
params:
  - name: query
    desc: "Texto de busqueda (nombre del modelo, ej. 'dreamshaper')."
  - name: types
    desc: "Tipo(s) de modelo Civitai, CSV: Checkpoint, LORA, TextualInversion, Controlnet, VAE, Upscaler, ... keyword-only."
  - name: base_model
    desc: "Filtra por modelo base (ej. 'SD 1.5', 'SDXL 1.0'). None no filtra. keyword-only."
  - name: sort
    desc: "Orden Civitai: 'Highest Rated', 'Most Downloaded', 'Newest'. keyword-only."
  - name: limit
    desc: "Numero maximo de resultados (1-100). keyword-only."
  - name: token
    desc: "API token de Civitai (header Authorization Bearer). Opcional: la busqueda publica funciona sin el. No hardcodear: pasar desde pass/vault. keyword-only."
output: "dict {ok, items, count, error}. items = lista de {name, type, base_model, version_id, download_url, nsfw} (primera version de cada modelo). ok=False con error si la peticion falla; una busqueda sin resultados devuelve ok=True con items=[] (no es error)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/ml/comfyui_search_civitai_models.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_search_civitai_models import comfyui_search_civitai_models

# Busqueda publica, sin token:
out = comfyui_search_civitai_models("dreamshaper", types="Checkpoint", limit=5)
for it in out["items"]:
    print(it["name"], it["base_model"], "->", it["download_url"])
# out["items"][0] == {"name": "DreamShaper", "type": "Checkpoint",
#   "base_model": "SD 1.5", "version_id": 128713,
#   "download_url": "https://civitai.com/api/download/models/128713", "nsfw": False}

# Filtrar LoRAs SDXL, con token desde pass (si lo hubiera):
# import subprocess
# tok = subprocess.run(["pass","civitai/api-token"], capture_output=True, text=True).stdout.strip() or None
# loras = comfyui_search_civitai_models("detail", types="LORA", base_model="SDXL 1.0", token=tok)
```

El `download_url` que devuelve se pasa directo a `comfyui_download_model(url=...)`
para bajar el modelo a la carpeta correcta.

## Cuando usarla

Cuando necesites descubrir un checkpoint o LoRA por nombre/tema antes de
descargarlo: te da el `download_url` listo para `comfyui_download_model`. Encadena
con `comfyui_resolve_workflow_deps` para resolver los modelos que le faltan a un
workflow ajeno.

## Gotchas

- Impura: HTTP GET a `civitai.com`. Requiere conexion a internet (NO usa el server
  local de ComfyUI, asi que no choca con reinicios del server).
- La busqueda publica funciona sin token. Sin token el rate limit es mas bajo y
  algunos modelos restringidos no aparecen o su `download_url` exige login.
- `download_url` apunta a `api/download/models/<versionId>`: muchos modelos exigen
  token para descargar aunque salgan en la busqueda (Civitai responde HTML de
  login). `comfyui_download_model` detecta ese HTML y devuelve ok=False.
- Solo se devuelve la PRIMERA version de cada modelo (`modelVersions[0]`, la mas
  reciente). Para versiones antiguas consulta la API por `version_id`.
- Una busqueda sin resultados NO es error: devuelve `ok=True, items=[], count=0`.
- No hardcodear el token: pasarlo desde `pass`/vault.
