API de imágenes y host de imágenes
Los endpoints de imágenes listan los pósters, fondos, logos, perfiles y fotogramas de un título como rutas de archivo. Luego cargas cada archivo desde img.amodb.dev en un tamaño fijo, sin clave y con 30 días de caché perimetral. Las rutas y el JSON coinciden con TMDB v3, así que tu código de imágenes funciona tras cambiar la URL base.
Obtener las imágenes de una película
GET / lista todas las imágenes conocidas de una película en tres arrays: backdrops, logos y posters. Cada entrada tiene un file_path, su tamaño en píxeles y una etiqueta de idioma. El archivo en sí no está en el JSON. Lo cargas desde el host de imágenes en un segundo paso.
El ejemplo pide imágenes en inglés y sin texto, elige el primer póster en inglés y luego obtiene ese archivo con 500 píxeles de ancho.
curl -s "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null" \
-H "Authorization: Bearer $AMODB_KEY"
curl -s -o poster.jpg -w "%{http_code}\n" \
"https://img.amodb.dev/t/p/w500/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg"const url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { posters } = await res.json();
if (posters.length === 0) throw new Error('no posters');
const poster = posters.find((p) => p.iso_639_1 === 'en') ?? posters[0];
const posterUrl = `https://img.amodb.dev/t/p/w500${poster.file_path}`;
const img = await fetch(posterUrl);
console.log(img.status, img.headers.get('content-type'), posterUrl);import os
import requests
r = requests.get(
"https://api.amodb.dev/3/movie/550/images",
params={"include_image_language": "en,null"},
headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
timeout=10,
)
r.raise_for_status()
posters = r.json()["posters"]
if not posters:
raise SystemExit("no posters")
poster = next((p for p in posters if p["iso_639_1"] == "en"), posters[0])
poster_url = "https://img.amodb.dev/t/p/w500" + poster["file_path"]
img = requests.get(poster_url, timeout=10)
print(img.status_code, img.headers.get("content-type"), poster_url)<?php
$url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
$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");
}
$posters = json_decode($body, true)['posters'];
if (!$posters) {
throw new RuntimeException('no posters');
}
$poster = $posters[0];
foreach ($posters as $p) {
if ($p['iso_639_1'] === 'en') {
$poster = $p;
break;
}
}
$posterUrl = 'https://img.amodb.dev/t/p/w500' . $poster['file_path'];
$img = curl_init($posterUrl);
curl_setopt_array($img, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]);
curl_exec($img);
echo curl_getinfo($img, CURLINFO_HTTP_CODE), ' ', curl_getinfo($img, CURLINFO_CONTENT_TYPE), ' ', $posterUrl, PHP_EOL;
curl_close($img);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Images struct {
Posters []struct {
FilePath string `json:"file_path"`
Language string `json:"iso_639_1"`
} `json:"posters"`
}
func main() {
url := "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
client := &http.Client{Timeout: 10 * time.Second}
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
panic(res.Status)
}
var images Images
if err := json.NewDecoder(res.Body).Decode(&images); err != nil || len(images.Posters) == 0 {
panic("no posters")
}
poster := images.Posters[0]
for _, p := range images.Posters {
if p.Language == "en" {
poster = p
break
}
}
posterURL := "https://img.amodb.dev/t/p/w500" + poster.FilePath
img, err := client.Get(posterURL)
if err != nil {
panic(err)
}
defer img.Body.Close()
fmt.Println(img.StatusCode, img.Header.Get("Content-Type"), posterURL)
}Parámetros
movie_idrutaobligatorioId numérico de la película, por ejemplo550.api_keyconsultauno de los dosTu clave. Usa esta o el encabezadoAuthorization: Bearer.include_image_languageconsultaCódigos ISO 639-1 separados por comas. Añadenullpara incluir imágenes sin texto, por ejemploen,null. Solo llegan los idiomas indicados. Sin él obtienes todos los idiomas.languageconsultaSe acepta pero se ignora en este endpoint. Usainclude_image_languagepara elegir idiomas.
Campos de la respuesta
| Campo | Tipo | Notas |
|---|---|---|
id | entero | El id de la película. |
backdrops, logos, posters | array | Un array por tipo de imagen, en ese orden de claves. Cada uno puede estar vacío. |
file_path | texto | Empieza con una barra. Añádelo a un tamaño en el host de imágenes. Los pósters y las imágenes de fondo son .jpg, los logos son .png o .svg. |
width, height | entero | Tamaño en píxeles del archivo original. 0 cuando el tamaño no se conoce. |
aspect_ratio | número | Ancho dividido por alto. Los pósters rondan 0.667 y las imágenes de fondo 1.778. 0 cuando el tamaño no se conoce. |
iso_639_1 | texto o null | Idioma del texto de la imagen. null significa sin texto. |
vote_average, vote_count | número, entero | Siempre 0 en las imágenes de películas y series, así que no sirven para ordenar imágenes. Conserva el orden que devuelve la API. |
Ejemplo de respuesta
Se muestran dos entradas por array.
{
"id": 550,
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 1440,
"iso_639_1": null,
"file_path": "/c6OLXfKAk5BKeR6broC8pYiCquX.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2560
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": "en",
"file_path": "/b9HyPoxwxjxkWEUL5ErZdhApQe2.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1920
}
],
"logos": [
{
"aspect_ratio": 4.638,
"height": 389,
"iso_639_1": "en",
"file_path": "/7Uqhv24pGJs4Ns31NoOPWFJGWNG.png",
"vote_average": 0,
"vote_count": 0,
"width": 1804
},
{
"aspect_ratio": 0.848,
"height": 1295,
"iso_639_1": "en",
"file_path": "/4XkF0Rf7gvSfea8fYLFbU5tmuJw.png",
"vote_average": 0,
"vote_count": 0,
"width": 1098
}
],
"posters": [
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0,
"height": 0,
"iso_639_1": null,
"file_path": "/qkrH7rjYKU4KOwwIl6wF6H3ISvm.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 0
}
]
}El segundo póster es una entrada cuyo tamaño no se conoce. El archivo se carga igualmente.
Construir la URL de una imagen
Une tres partes: https:/, un segmento de tamaño y el file_path. La ruta ya empieza con una barra, así que https:/ más / es una URL completa.
Un tamaño es w más un ancho en píxeles, h más un alto en píxeles, o original para el archivo guardado. El host sirve diez tamaños: w92, w154, w185, w300, w342, w500, w780, w1280, h632 y original. Cualquier otro tamaño, como w400, responde 404. /3/configuration lista los tamaños que encajan con cada tipo de imagen.
| Tipo | Campo de configuración | Tamaños |
|---|---|---|
| Póster | poster_sizes | w92, w154, w185, w342, w500, w780, original |
| Imagen de fondo | backdrop_sizes | w300, w780, w1280, original |
| Logo | logo_sizes | w92, w154, w185, w300, w500, original |
| Perfil | profile_sizes | w185, h632, original |
| Fotograma | still_sizes | w92, w185, w300, original |
El host no comprueba el tipo, así que un póster también se carga en w1280. Quédate con los tamaños listados para cada tipo, para que tu código coincida con la configuración y con cualquier cliente de TMDB.
El host de imágenes no necesita clave, y la caché perimetral guarda cada archivo 30 días. Como no se envía clave, las cargas de imágenes no cuentan para tus solicitudes por minuto. Las respuestas llevan Access-Control-Allow-Origin: *, así que un navegador puede cargarlas desde cualquier página. Un tamaño desconocido o un archivo desconocido responde 404 con un cuerpo JSON.
Imágenes responsivas con srcset
Elige el tamaño más pequeño que aún cubra el ancho mostrado multiplicado por la densidad de pantalla. Un póster que se muestra con 171 píxeles CSS de ancho necesita w342 en una pantalla 2x. Esta función auxiliar construye un srcset a partir de una ruta guardada.
const WIDTHS = [185, 342, 500, 780];
function posterSrcset(filePath) {
return WIDTHS.map((w) => `https://img.amodb.dev/t/p/w${w}${filePath} ${w}w`).join(', ');
}
const path = '/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg';
console.log(`<img src="https://img.amodb.dev/t/p/w342${path}" srcset="${posterSrcset(path)}" sizes="(max-width: 600px) 50vw, 342px" alt="Fight Club poster">`);Otros endpoints de imágenes
Todas las rutas de abajo usan la base https:/ y devuelven los arrays que indica la segunda columna.
| Método y ruta | Arrays devueltos | Parámetros |
|---|---|---|
GET / | backdrops, logos, posters | include_image_language |
GET / | posters, siempre vacío | ninguno |
GET / | stills, una entrada con width y height en 0 | ninguno |
GET / | profiles | ninguno |
Para películas y series también puedes añadir append_ a una llamada de detalles. include_image_language funciona ahí también. El bloque añadido no tiene un id propio.
Los perfiles de personas son el único lugar donde vote_average lleva una puntuación real. El id de una respuesta de temporada o de episodio es el id de la temporada o del episodio. Consulta personas y series de TV para los registros principales.
amodb no ofrece rutas de imágenes para colecciones, productoras y cadenas. Sus registros ya contienen un poster_path, backdrop_path o logo_path, descritos en colecciones y productoras y cadenas de TV.
El endpoint de configuración
GET /3/configuration devuelve un bloque images con secure_base_url fijado en https:/, base_url (la forma http simple) y las listas de tamaños backdrop_sizes, logo_sizes, poster_sizes, profile_sizes y still_sizes. Léelo una vez al iniciar, usa la forma segura y construye las URL a partir de él en lugar de fijar los tamaños en el código. La caché perimetral lo guarda 24 horas.
Errores
| HTTP | status_code | Cuándo |
|---|---|---|
| 401 | 7 | La clave falta o es incorrecta en una llamada JSON. |
| 402 | 36 | El periodo del plan ha terminado. |
| 404 | 34 | No hay ninguna persona, temporada o episodio con ese id, o hay un tamaño o un archivo desconocido en el host de imágenes. |
| 405 | 37 | Usaste un método de escritura en una ruta de imágenes. |
| 429 | 25 | Superaste tus solicitudes por minuto. Espera los segundos de Retry-After. |
Un id de película o serie que no existe no es un error aquí: la ruta de imágenes responde 200 con arrays vacíos, mientras que la ruta de detalles responde 404. Consulta errores para ver la lista completa. Las solicitudes de archivos de imagen son cargas HTTP simples, así que comprueba el código de estado de la respuesta en lugar de analizar un cuerpo JSON.
Notas prácticas
- Guarda
file_pathen tu base de datos y construye la URL al mostrar la imagen. Así, un tamaño o un host nuevo cuesta un solo cambio de código. - Pasa
include_image_languagecon tu idioma de visualización másnull, por ejemploru,null, para que no aparezcan pósters con texto en otros idiomas. - Un array vacío es un 200 normal. Recurre al
poster_pathdel registro de detalles de película. - Usa
w1280para imágenes de fondo a todo el ancho yw780para tarjetas.originales el archivo guardado a resolución completa y es mucho más grande quew780. - Las copias de la caché perimetral viven 30 días y se comparten, así que un archivo que pidió un cliente se sirve desde la caché al siguiente.