# API de base de datos de películas para tu propio catálogo

> Usa amodb como fuente de tu propia base de datos de películas. Películas, personas, productoras, colecciones, palabras clave y géneros comparten ids numéricos, así que las filas se unen sin problema. Carga con discover por ventanas de fechas, actualiza por antigüedad y cruza tus ids con find. JSON de TMDB v3, de 20 a 120 solicitudes por minuto.

## Crea tu propia base de datos de películas desde una API

amodb es una API independiente que habla el protocolo TMDB v3. Películas, personas, productoras, colecciones, palabras clave y géneros tienen ids numéricos y se referencian entre sí, así que puedes llenar tablas SQL con ellos y mantenerlas al día. Carga las tablas con la paginación de discover, actualízalas por antigüedad y cruza tus propios identificadores con `find`. amodb no tiene un feed de cambios, por lo que actualizar es simplemente volver a pedir las filas que conservas.

Qué obtienes:

- Seis tipos de entidad enlazados con ids numéricos estables.
- Paginación de discover para la primera carga: hasta 20 resultados por página, 500 páginas por consulta.
- Ventanas de fechas de estreno en discover, para dividir la primera carga y para listar los estrenos nuevos.
- Búsquedas por ID externo con `imdb_id`, `tvdb_id`, `wikidata_id` y `kinopoisk_id`.

## Qué datos obtienes

Este es el modelo de datos. Cada fila es una tabla en una copia relacional.

| Entidad       | Se lee con                          | Clave           | Enlaza con                                                                                                     |
| ------------- | ----------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
| Película      | `GET /3/movie/{movie_id}`           | `id`, `imdb_id` | `genres[].id`, `production_companies[].id`, `belongs_to_collection.id`                                         |
| Persona       | `GET /3/person/{person_id}`         | `id`, `imdb_id` | Películas mediante `/3/movie/{movie_id}/credits`                                                               |
| Productora    | `GET /3/company/{company_id}`       | `id`            | Películas mediante `with_companies` en discover. `parent_company` siempre es `null` y `description` está vacío |
| Colección     | `GET /3/collection/{collection_id}` | `id`            | `parts[]`, una lista de películas                                                                              |
| Palabra clave | `GET /3/keyword/{keyword_id}`       | `id`            | Películas mediante `/3/keyword/{keyword_id}/movies` o `with_keywords`                                          |
| Género        | `GET /3/genre/movie/list`           | `id`            | `genres[].id` en las películas                                                                                 |

Las relaciones de muchos a muchos necesitan tablas de unión: `movie_genre`, `movie_company`, `movie_keyword` y `movie_credit`. Usa `credit_id` de [créditos](https://amodb.dev/es/credits/) como clave de la última. Las listas de campos están en [películas](https://amodb.dev/es/movies/), [personas](https://amodb.dev/es/people/) y [colecciones](https://amodb.dev/es/collections/).

## Empieza en 3 pasos

1. Compara las tres velocidades de solicitudes en [precios](https://amodb.dev/es/pricing/). Una velocidad mayor acorta la primera carga y no cambia nada más.
2. Crea una clave en el panel de tu cuenta y guárdala en una variable de entorno.
3. Llama a `/3/discover/movie` con una ventana de un día de fechas de estreno y luego obtén cada id de la lista.

## Ejemplo de solicitud

El script en PHP lista las películas estrenadas ayer y hoy con discover y hace un upsert de cada registro en una tabla SQLite. Espera 3 segundos entre lecturas de detalle para mantenerse por debajo de 20 solicitudes por minuto, reintenta un 429 después de `Retry-After` y borra las filas cuyo id ahora devuelve 404.

```bash
curl -s "https://api.amodb.dev/3/discover/movie?primary_release_date.gte=2026-10-17&primary_release_date.lte=2026-10-18&sort_by=primary_release_date.asc&page=1" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```php
<?php
const BASE = 'https://api.amodb.dev/3';

function amodb(string $path, array $query = []): ?array
{
    $url = BASE . $path . '?' . http_build_query($query);
    for ($attempt = 0; $attempt < 3; $attempt++) {
        $retryAfter = 1;
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
            CURLOPT_HEADERFUNCTION => function ($handle, $line) use (&$retryAfter) {
                if (stripos($line, 'Retry-After:') === 0) {
                    $retryAfter = max(1, (int) trim(substr($line, 12)));
                }
                return strlen($line);
            },
        ]);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($status === 429) {
            sleep($retryAfter);
            continue;
        }
        if ($status === 404) {
            return null;
        }
        if ($status !== 200) {
            throw new RuntimeException("amodb returned $status for $path");
        }
        return json_decode($body, true);
    }
    throw new RuntimeException("Still rate limited on $path");
}

$db = new PDO('sqlite:movies.sqlite');
$db->exec('CREATE TABLE IF NOT EXISTS movie (id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, release_date TEXT, raw TEXT)');
$save = $db->prepare('INSERT OR REPLACE INTO movie (id, imdb_id, title, release_date, raw) VALUES (?, ?, ?, ?, ?)');
$drop = $db->prepare('DELETE FROM movie WHERE id = ?');

$from = date('Y-m-d', strtotime('-1 day'));
$to = date('Y-m-d');
$page = 1;
do {
    $list = amodb('/discover/movie', [
        'primary_release_date.gte' => $from,
        'primary_release_date.lte' => $to,
        'sort_by' => 'primary_release_date.asc',
        'page' => $page,
    ]);
    foreach ($list['results'] as $row) {
        $movie = amodb('/movie/' . $row['id'], ['language' => 'en-US']);
        if ($movie === null) {
            $drop->execute([$row['id']]);
            continue;
        }
        $save->execute([$movie['id'], $movie['imdb_id'] ?? null, $movie['title'], $movie['release_date'] ?? null, json_encode($movie)]);
        sleep(3);
    }
    $page++;
} while ($page <= min($list['total_pages'], 500));
```

Discover devuelve filas de lista, no registros completos. Estos son 2 de los 20 resultados de la página 1. Lee `/3/movie/{id}` para obtener el resto de los campos.

```json
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/siepLRGVjM8yLKppApkD3uSh8Y0.jpg",
      "genre_ids": [
        35
      ],
      "id": 1800227,
      "original_language": "en",
      "original_title": "Sly's Pack",
      "overview": "Nesmith, Gary, & Tony are smoking weed on the roof of a parking garage, but a...",
      "popularity": 0.0071,
      "poster_path": "/3HpIWZUI7jmpKZkfZNdchS2WsvN.jpg",
      "release_date": "2026-10-17",
      "title": "Sly's Pack",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    },
    {
      "adult": false,
      "backdrop_path": null,
      "genre_ids": [
        99
      ],
      "id": 1800168,
      "original_language": "en",
      "original_title": "The Good Life",
      "overview": "The Good LIfe is based on the best-selling book by Drs. Robert Waldinger and...",
      "popularity": 0.0143,
      "poster_path": null,
      "release_date": "2026-10-17",
      "title": "The Good Life",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    }
  ],
  "total_pages": 15,
  "total_results": 299
}
```

## Llena y actualiza las tablas

| Tarea                | Llamada                                                                                                         | Límites                                                                                                                                           |
| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Primera carga        | `GET /3/discover/movie` con `sort_by` y una ventana de fechas                                                   | `page` de 1 a 500, hasta 20 resultados por página, es decir 10,000 por consulta. Divide por `primary_release_date.gte` y `.lte` para ir más allá. |
| Estrenos nuevos      | `GET /3/discover/movie` con `primary_release_date.gte` fijado en tu última ejecución, y `GET /3/movie/upcoming` | En caché durante 30 minutos.                                                                                                                      |
| Actualizar filas     | `GET /3/movie/{movie_id}` para filas con más de 6 horas                                                         | No hay feed de cambios. Actualiza los títulos que abren los usuarios y el resto con menos frecuencia.                                             |
| Unir tus propios ids | `GET /3/find/{external_id}`                                                                                     | Un id por llamada, con la fuente elegida mediante `external_source`.                                                                              |
| Carga bajo demanda   | `GET /3/movie/{movie_id}` cuando un usuario abre un título                                                      | Guarda la respuesta con una marca de tiempo.                                                                                                      |

La ventana de paginación es la decisión de diseño principal. Cuando `total_pages` en una respuesta supera 500, la ventana contiene más de 10,000 filas, así que pártela por la mitad hasta que quepa. Un año reciente necesita meses, y un solo día de estrenos son unos cientos de títulos. Guarda el tramo que terminaste para que una ejecución interrumpida pueda reanudarse. Las reglas de coincidencia para la unión están en [buscar por ID externo](https://amodb.dev/es/find-by-external-id/).

## Dimensiona la sincronización

Cada fila que actualizas cuesta una lectura de detalle. Un plan de 20 solicitudes por minuto da 1,200 llamadas por hora, uno de 40 da 2,400 y uno de 120 da 7,200. Una página de discover lista 20 ids con una sola llamada. Ejecuta la actualización en horas de poca carga si tu aplicación comparte la clave, porque el límite es por clave, no por script. Consulta [límites de solicitudes](https://amodb.dev/es/rate-limits/) para ver los encabezados que indican lo que queda en la ventana.

## Caché en dos capas

| Capa             | Contiene                                   | TTL                                                                                |
| ---------------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
| Caché perimetral | Detalles y créditos                        | 6 horas                                                                            |
| Caché perimetral | Listas de discover, tendencias y populares | 30 minutos                                                                         |
| Caché perimetral | Búsqueda                                   | 10 minutos                                                                         |
| Caché perimetral | Configuración                              | 24 horas                                                                           |
| Caché perimetral | Imágenes                                   | 30 días                                                                            |
| Tus tablas       | Las filas que conservas                    | Hasta que tu regla de antigüedad indique volver a pedirlas, nunca antes de 6 horas |

La caché perimetral se comparte entre claves, pero el límite de solicitudes se aplica antes que ella, así que un acierto de caché sigue gastando una solicitud de tu minuto. Releer una fila con más frecuencia que cada 6 horas devuelve la misma copia y desperdicia cuota. Deja que una regla de antigüedad decida cuándo volver a pedirla.

## Cómo se compara

| Capacidad                     | Hacer scraping de las páginas tú mismo | Archivos de datos estáticos                   | amodb                                                              |
| ----------------------------- | -------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------ |
| Actualidad                    | Tan reciente como tu último rastreo    | Tan reciente como el último archivo publicado | Detalles en caché 6 horas, vuelve a pedirlos por antigüedad        |
| Actualizaciones incrementales | Rastrear de nuevo y comparar           | Descargar un archivo nuevo                    | Discover con una ventana de fechas de estreno, sin feed de cambios |
| Ids entre entidades           | URL de las páginas                     | Los define el editor                          | Ids numéricos, `imdb_id` y `/3/find`                               |
| Qué ejecutas                  | Rastreadores y almacenamiento          | Almacenamiento y un trabajo de importación    | Almacenamiento y un script de sincronización                       |
| Límite de solicitudes         | Lo fijan los sitios que rastreas       | No aplica                                     | 20, 40 o 120 solicitudes por minuto, contadas por clave            |
| Idiomas por registro          | Los que ofrezca cada página            | Depende de los archivos                       | 14, elegidos con `language`                                        |

## Precios

La primera carga es la parte costosa, y la limita tu número de solicitudes por minuto. Elige 20, 40 o 120 por minuto en [precios](https://amodb.dev/es/pricing/). Paga por 1, 3, 6, 12 o 24 meses con un descuento del 0, 10, 20, 30 o 40 por ciento. Nada se renueva automáticamente, no hay nivel gratuito y todos los planes leen los mismos datos.

## Preguntas y respuestas

### ¿Cómo mantengo al día una copia local?

amodb no tiene un feed de cambios, así que actualiza por antigüedad. Los detalles se guardan en la caché perimetral durante 6 horas, por lo que no vuelvas a pedir una fila antes de ese plazo. Detecta los estrenos nuevos con discover y primary_release_date.gte fijado en la fecha de tu última ejecución.

### ¿Cómo cargo la primera copia?

Recorre /3/discover/movie con page de 1 a 500 (hasta 20 resultados cada una, es decir 10,000 por consulta) y divide por ventanas de primary_release_date para ir más allá. Solo marzo de 2025 reúne unos 4,400 títulos. Después lee /3/movie/{id} por cada id que conserves.

### ¿Cuánto tarda la primera carga con mi límite de solicitudes?

Con 20 solicitudes por minuto puedes hacer 1,200 llamadas por hora, con 40 son 2,400 y con 120 son 7,200. Divide el número de lecturas de detalle que necesitas entre esa cifra para planificar la ejecución.

### ¿Puedo unir los ids de amodb con los ids que ya guardo?

Sí. /3/find/{external_id} acepta imdb_id, tvdb_id, wikidata_id y kinopoisk_id y devuelve el registro de amodb que coincide. Los registros de películas también incluyen imdb_id, así que la unión funciona en ambos sentidos.

### ¿Hay un nivel gratuito para construir una base de datos?

No. El acceso es prepago por 1, 3, 6, 12 o 24 meses y no se renueva solo. Todos los planes leen los mismos datos, así que un plan mayor solo hace que la primera carga y las actualizaciones sean más rápidas.

## Páginas relacionadas

- [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 detalles de persona](https://amodb.dev/es/people/): API de detalles de persona con rutas y JSON de TMDB v3. Obtén un actor o miembro del equipo por id, con biografía, filmografía, ids externos y populares.
- [API de colecciones](https://amodb.dev/es/collections/): API de colecciones de películas con rutas TMDB v3: obtén una saga por id con todas sus partes, lee belongs_to_collection en una película y busca por nombre.
- [Productoras y cadenas de TV](https://amodb.dev/es/companies-networks/): Consulta productoras y cadenas de TV con rutas TMDB v3. Lee nombre, país y logo por id, y filtra discover por el id de una productora o de una cadena.
- [Buscar por id externo](https://amodb.dev/es/find-by-external-id/): Busca una película, serie o persona por id de IMDb, TVDB, Wikidata o Kinopoisk con el endpoint find de TMDB, y lee los ids externos y los datos de Wikidata.
- [Límites de solicitudes](https://amodb.dev/es/rate-limits/): amodb limita cada clave por solicitudes por minuto en una ventana deslizante de 60 segundos: 20, 40 o 120 según el plan. Lee los encabezados y gestiona el 429.

Actualizado: 2026-10-18. Source: https://amodb.dev/es/movie-database-api/
