# API de palabras clave

> Los endpoints de palabras clave devuelven las etiquetas de trama de una película o serie de TV, cada una con un id y un nombre. Puedes consultar una palabra clave, listar las películas que la llevan o pasar los ids a discover con with_keywords para crear páginas temáticas. Las películas responden en keywords y las series en results.

## Endpoint

`GET /3/movie/{movie_id}/keywords`

```bash
curl "https://api.amodb.dev/3/movie/550/keywords" \
  -H "Authorization: Bearer $AMODB_KEY"
```

Respuesta:

```json
{
  "id": 550,
  "keywords": [
    {
      "id": 818,
      "name": "based on novel or book"
    },
    {
      "id": 825,
      "name": "support group"
    },
    {
      "id": 851,
      "name": "dual identity"
    }
  ]
}
```

## Obtener las palabras clave de una película

`GET /3/movie/{movie_id}/keywords` devuelve las etiquetas de trama de una película. Una palabra clave es una frase corta, como un tema, un escenario o un tipo de fuente, y tiene un id numérico que puedes reutilizar en [discover](https://amodb.dev/es/discover/).

El ejemplo carga las palabras clave de una película y luego pide a discover las películas más populares que comparten su primera palabra clave.

```bash
curl -s "https://api.amodb.dev/3/movie/550/keywords" \
  -H "Authorization: Bearer $AMODB_KEY"

curl -s "https://api.amodb.dev/3/discover/movie?with_keywords=818&sort_by=popularity.desc" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const base = 'https://api.amodb.dev/3';
const headers = { Authorization: `Bearer ${process.env.AMODB_KEY}` };

const kw = await fetch(`${base}/movie/550/keywords`, { headers });
if (!kw.ok) throw new Error(`keywords failed with ${kw.status}`);
const { keywords } = await kw.json();
console.log(keywords.map((k) => `${k.id}:${k.name}`).join(', '));

if (keywords.length > 0) {
  const first = keywords[0];
  const found = await fetch(`${base}/discover/movie?with_keywords=${first.id}&sort_by=popularity.desc`, { headers });
  if (!found.ok) throw new Error(`discover failed with ${found.status}`);
  const page = await found.json();
  console.log(first.name, page.results.slice(0, 5).map((m) => m.title));
}
```

```python
import os
import requests

base = "https://api.amodb.dev/3"
headers = {"Authorization": f"Bearer {os.environ['AMODB_KEY']}"}

r = requests.get(f"{base}/movie/550/keywords", headers=headers, timeout=10)
r.raise_for_status()
keywords = r.json()["keywords"]
print(", ".join(f"{k['id']}:{k['name']}" for k in keywords))

if keywords:
    first = keywords[0]
    d = requests.get(
        f"{base}/discover/movie",
        params={"with_keywords": first["id"], "sort_by": "popularity.desc"},
        headers=headers,
        timeout=10,
    )
    d.raise_for_status()
    print(first["name"], [m["title"] for m in d.json()["results"][:5]])
```

```php
<?php
function amodb_get(string $url): array {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($status !== 200) {
        throw new RuntimeException("amodb returned $status");
    }
    return json_decode($body, true);
}

$base = 'https://api.amodb.dev/3';
$keywords = amodb_get("$base/movie/550/keywords")['keywords'];
if ($keywords) {
    $first = $keywords[0];
    $found = amodb_get("$base/discover/movie?with_keywords={$first['id']}&sort_by=popularity.desc");
    $titles = array_column(array_slice($found['results'], 0, 5), 'title');
    echo $first['name'], ': ', implode(', ', $titles), PHP_EOL;
}
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

var client = &http.Client{Timeout: 10 * time.Second}

func get(path string, out any) error {
	req, _ := http.NewRequest("GET", "https://api.amodb.dev/3"+path, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("amodb returned %s", res.Status)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

type Keywords struct {
	Keywords []struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"keywords"`
}

type Page struct {
	Results []struct {
		Title string `json:"title"`
	} `json:"results"`
}

func main() {
	var kw Keywords
	if err := get("/movie/550/keywords", &kw); err != nil {
		panic(err)
	}
	if len(kw.Keywords) == 0 {
		return
	}
	first := kw.Keywords[0]
	var page Page
	if err := get(fmt.Sprintf("/discover/movie?with_keywords=%d&sort_by=popularity.desc", first.ID), &page); err != nil {
		panic(err)
	}
	fmt.Println(first.Name)
	for i, m := range page.Results {
		if i == 5 {
			break
		}
		fmt.Println(" ", m.Title)
	}
}
```

### Parámetros

| Parámetro  | En       | Obligatorio    | Descripción                                                 |
| ---------- | -------- | -------------- | ----------------------------------------------------------- |
| `movie_id` | ruta     | sí             | Id numérico de la película, por ejemplo `550`.              |
| `api_key`  | consulta | uno de los dos | Tu clave. Usa esta o el encabezado `Authorization: Bearer`. |

### Campos de la respuesta

| Campo             | Tipo   | Notas                                                                                           |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------- |
| `id`              | entero | El id de la película.                                                                           |
| `keywords`        | array  | Objetos con `id` y `name`. Vacío cuando la película no tiene ninguna.                           |
| `keywords[].id`   | entero | Id de la palabra clave. Úsalo en `with_keywords` y en las rutas de palabras clave de más abajo. |
| `keywords[].name` | texto  | El texto de la palabra clave.                                                                   |

### Ejemplo de respuesta

```json
{
  "id": 550,
  "keywords": [
    { "id": 818, "name": "based on novel or book" },
    { "id": 825, "name": "support group" },
    { "id": 851, "name": "dual identity" }
  ]
}
```

La respuesta real contiene 14 palabras clave. Esta se recorta a las 3 primeras. Los nombres de las palabras clave no se traducen, así que `language` no tiene efecto aquí.

## Palabras clave de una serie de TV

`GET /3/tv/{series_id}/keywords` usa la misma clave y devuelve los mismos objetos, pero el array se llama `results`. El id de la ruta es el de la serie, por ejemplo `1396`.

```json
{
  "id": 1396,
  "results": [
    { "id": 1508, "name": "new mexico" },
    { "id": 2231, "name": "drug dealer" }
  ]
}
```

La respuesta real contiene 29 entradas y esta se recorta a las 2 primeras. Un cliente que atiende ambos tipos de contenido puede leer `data.keywords ?? data.results` y tratar los dos casos igual.

## Registros de palabras clave y sus películas

| Método y ruta                      | Devuelve                                                                                | Parámetros         |
| ---------------------------------- | --------------------------------------------------------------------------------------- | ------------------ |
| `GET /keyword/{keyword_id}`        | `{ id, name }`                                                                          | ninguno            |
| `GET /keyword/{keyword_id}/movies` | Lista paginada de películas con `id`, `page`, `results`, `total_pages`, `total_results` | `language`, `page` |

Ambas rutas usan el id numérico de un array de palabras clave. La lista de películas tiene la misma forma de elemento que otras [listas de películas](https://amodb.dev/es/trending-lists/), con `genre_ids` y `poster_path`, y además el `id` de la palabra clave en el nivel superior. `include_adult` se acepta y se ignora. Una página posterior a `total_pages` devuelve un array `results` vacío. No existe una lista de series para una palabra clave, así que usa `/discover/tv?with_keywords=` para TV.

## Combinar palabras clave con discover

Para cualquier cosa más allá de una lista simple, filtra en [discover](https://amodb.dev/es/discover/). Ordena, pagina y combina palabras clave con otros filtros, y cubre TV además de películas.

| Objetivo                                    | Cadena de consulta                                 |
| ------------------------------------------- | -------------------------------------------------- |
| Películas con una palabra clave             | `/discover/movie?with_keywords=818`                |
| Películas con ambas de dos palabras clave   | `/discover/movie?with_keywords=818,4565`           |
| Películas sin una palabra clave             | `/discover/movie?without_keywords=818`             |
| Series con una palabra clave                | `/discover/tv?with_keywords=9712`                  |
| Series con cualquiera de dos palabras clave | `/discover/tv?with_keywords=818%7C9712`            |
| Una palabra clave dentro de un género       | `/discover/movie?with_keywords=818&with_genres=18` |

Una coma entre ids significa todos ellos. Escribe una barra vertical entre ids, como en `with_keywords=818|4565`, para que coincida cualquiera. Los resultados de discover se guardan en la caché perimetral durante 30 minutos.

## Encontrar el id de una palabra clave

Para convertir texto en un id, llama a `GET /3/search/keyword?query=heist`. Devuelve una lista paginada de pares `id` y `name`, 20 por página. Consulta la [búsqueda](https://amodb.dev/es/search/) para ver todas las rutas de búsqueda. Una búsqueda de palabras clave sin coincidencias responde hoy 500 con `status_code` 11 en lugar de una lista vacía. Las respuestas de búsqueda se guardan en caché 10 minutos, pero cada llamada sigue contando para tus solicitudes por minuto, así que aplica debounce a un cuadro de autocompletado.

## Errores

| HTTP | status_code | Cuándo                                                                                          |
| ---- | ----------- | ----------------------------------------------------------------------------------------------- |
| 401  | 7           | La clave falta o es incorrecta.                                                                 |
| 402  | 36          | El periodo del plan ha terminado.                                                               |
| 404  | 34          | No hay ninguna palabra clave con ese id en `/keyword/{keyword_id}` ni en su lista de películas. |
| 405  | 37          | Usaste un método de escritura en una ruta de palabras clave.                                    |
| 429  | 25          | Superaste tus solicitudes por minuto. Espera los segundos de `Retry-After`.                     |
| 500  | 11          | Una consulta a `/search/keyword` que no coincide con nada.                                      |

Un id de película o serie desconocido en las rutas de palabras clave no da un 404. Recibes HTTP 200 con el id y un array vacío.

Consulta los [errores](https://amodb.dev/es/errors/) para ver todos los códigos y los [límites de solicitudes](https://amodb.dev/es/rate-limits/) para los límites de cada plan.

## Notas prácticas

- Guarda el `id` de la palabra clave, no el nombre. Un nombre puede reformularse, un id no cambia.
- Las listas de palabras clave se guardan en caché junto con los detalles del título durante 6 horas. Para páginas de etiquetas basta una actualización semanal.
- Un solo título tiene una lista corta de etiquetas. Para páginas temáticas, parte de un id de palabra clave y lista sus títulos en discover, no al revés.
- Usa `without_keywords` para quitar ruido, por ejemplo una etiqueta que nunca quieres en una página familiar.
- Un array vacío es una respuesta válida para títulos poco conocidos. Oculta la fila de etiquetas en lugar de mostrar un error.

## Preguntas y respuestas

### ¿Por qué la respuesta de TV usa results y las películas usan keywords?

Así lo estructura TMDB v3, y amodb lo mantiene para que los clientes existentes sigan funcionando. Las películas devuelven un array keywords. Las series de TV devuelven los mismos objetos bajo results. Lee data.keywords ?? data.results.

### ¿Cómo encuentro el id de una palabra clave a partir de su nombre?

Llama a la ruta de búsqueda de palabras clave con un query, por ejemplo /3/search/keyword?query=heist. Devuelve una lista paginada de pares id y name. Elige la coincidencia exacta y guarda el id.

### ¿Puedo usar un mismo id de palabra clave para películas y TV?

Sí. Un id de palabra clave es el mismo valor en discover de películas y en discover de TV, así que with_keywords funciona en ambas rutas. Guarda un id por tema y reutilízalo para los dos tipos de contenido.

### ¿Cómo combino varias palabras clave en una consulta?

Une los ids en with_keywords con una coma para exigir todos, o con una barra vertical para aceptar cualquiera. Usa without_keywords para excluir ids. Mantén la URL dentro del límite de longitud de tu cliente HTTP.

## Páginas relacionadas

- [API Discover](https://amodb.dev/es/discover/): API Discover con los mismos filtros que TMDB v3. Lista películas y series por género, reparto, productora, palabra clave, año, nota, duración y plataforma.
- [API de búsqueda](https://amodb.dev/es/search/): API de búsqueda con rutas y JSON de TMDB v3. Encuentra películas, series, personas, colecciones, productoras y palabras clave por texto, con filtros de año.
- [API de detalles de película](https://amodb.dev/es/movies/): API de detalles de película con rutas y JSON de TMDB v3. Obtén una película por id y añade créditos, puntuaciones y moods con append_to_response, en 14 idiomas.
- [API de series de TV](https://amodb.dev/es/tv-series/): API de series de TV con las mismas rutas y JSON que TMDB v3. Obtén una serie con temporadas, cadenas y próximo episodio, más clasificaciones y plataformas.
- [Géneros y listas de consulta](https://amodb.dev/es/genres/): API de géneros con rutas TMDB v3: ids de género de películas y TV con nombres traducidos, más listas de países, idiomas, oficios, clasificaciones y proveedores.
- [Inicio rápido](https://amodb.dev/es/quickstart/): Haz tu primera llamada a amodb: consigue una clave, pide los detalles de una película en curl, JavaScript, Python, PHP o Go y carga el póster. En 2 minutos.

Actualizado: 2026-10-18. Source: https://amodb.dev/es/keywords/
