# API obrazów i host obrazów

> Endpointy obrazów wypisują plakaty, tła, loga, profile i kadry tytułu jako ścieżki plików. Każdy plik ładujesz potem z img.amodb.dev w stałym rozmiarze, bez klucza i z cache na edge przez 30 dni. Ścieżki i JSON odpowiadają TMDB v3, więc istniejący kod do obrazów działa po zmianie bazowego URL.

## Endpoint

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

```bash
curl "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null" \
  -H "Authorization: Bearer $AMODB_KEY"
```

Odpowiedź:

```json
{
  "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
    },
    {
      "aspect_ratio": 1.778,
      "height": 2160,
      "iso_639_1": "en",
      "file_path": "/ewMhHVyXQKMAviVWM7InPjZqpSr.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 3840
    }
  ],
  "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
    },
    {
      "aspect_ratio": 1.457,
      "height": 1299,
      "iso_639_1": "en",
      "file_path": "/lm0VCzO1ZFhyx81k5Fzkex4u6C8.png",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1893
    }
  ],
  "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
    },
    {
      "aspect_ratio": 0,
      "height": 0,
      "iso_639_1": null,
      "file_path": "/efBb4gjjKneUoBVgfFOUu2OwihS.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 0
    }
  ]
}
```

## Pobieranie obrazów filmu

`GET /3/movie/{movie_id}/images` wypisuje wszystkie znane obrazy jednego filmu jako trzy tablice: `backdrops`, `logos` i `posters`. Każda pozycja ma `file_path`, rozmiar w pikselach i tag języka. Samego pliku w JSON nie ma. Ładujesz go z hosta obrazów w drugim kroku.

Przykład prosi o obrazy angielskie i bez tekstu, wybiera pierwszy angielski plakat, a potem pobiera ten plik o szerokości 500 pikseli.

```bash
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"
```

```js
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);
```

```python
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
<?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);
```

```go
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)
}
```

### Parametry

| Parametr                 | Gdzie | Wymagany      | Opis                                                                                                                                                                            |
| ------------------------ | ----- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `movie_id`               | path  | tak           | Numeryczne ID filmu, na przykład `550`.                                                                                                                                         |
| `api_key`                | query | jeden z dwóch | Twój klucz. Użyj go albo nagłówka `Authorization: Bearer`.                                                                                                                      |
| `include_image_language` | query | nie           | Kody ISO 639-1 oddzielone przecinkami. Dodaj `null`, aby uwzględnić obrazy bez tekstu, na przykład `en,null`. Wracają tylko wymienione języki. Bez niego dostajesz każdy język. |
| `language`               | query | nie           | Przyjmowany, ale ignorowany w tym endpoincie. Do wyboru języków użyj `include_image_language`.                                                                                  |

### Pola odpowiedzi

| Pole                            | Typ             | Uwagi                                                                                                                     |
| ------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `id`                            | integer         | ID filmu.                                                                                                                 |
| `backdrops`, `logos`, `posters` | array           | Jedna tablica na rodzaj obrazu, w tej kolejności kluczy. Każda może być pusta.                                            |
| `file_path`                     | string          | Zaczyna się od ukośnika. Dołącz go do rozmiaru na hoście obrazów. Plakaty i tła to `.jpg`, loga to `.png` lub `.svg`.     |
| `width`, `height`               | integer         | Rozmiar oryginalnego pliku w pikselach. `0`, gdy rozmiar nie jest znany.                                                  |
| `aspect_ratio`                  | number          | Szerokość podzielona przez wysokość. Plakaty mają około 0.667, tła około 1.778. `0`, gdy rozmiar nie jest znany.          |
| `iso_639_1`                     | string lub null | Język tekstu na obrazie. `null` oznacza brak tekstu.                                                                      |
| `vote_average`, `vote_count`    | number, integer | Zawsze `0` dla obrazów filmów i seriali, więc nie da się nimi porządkować obrazów. Zachowaj kolejność zwracaną przez API. |

### Przykładowa odpowiedź

Pokazano po dwie pozycje w każdej tablicy.

```json
{
  "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
    }
  ]
}
```

Drugi plakat pokazuje pozycję, której rozmiar nie jest znany. Plik mimo to się ładuje.

## Budowanie URL obrazu

Połącz trzy części: `https://img.amodb.dev/t/p/`, segment rozmiaru i `file_path`. Ścieżka już zaczyna się od ukośnika, więc `https://img.amodb.dev/t/p/w500` plus `/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg` to kompletny URL.

Rozmiar to `w` plus szerokość w pikselach, `h` plus wysokość w pikselach albo `original` dla zapisanego pliku. Host obsługuje dziesięć rozmiarów: `w92`, `w154`, `w185`, `w300`, `w342`, `w500`, `w780`, `w1280`, `h632` i `original`. Każdy inny rozmiar, na przykład `w400`, odpowiada 404. `/3/configuration` wymienia rozmiary pasujące do każdego rodzaju obrazu.

| Rodzaj | Pole konfiguracji | Rozmiary                                                  |
| ------ | ----------------- | --------------------------------------------------------- |
| Plakat | `poster_sizes`    | `w92`, `w154`, `w185`, `w342`, `w500`, `w780`, `original` |
| Tło    | `backdrop_sizes`  | `w300`, `w780`, `w1280`, `original`                       |
| Logo   | `logo_sizes`      | `w92`, `w154`, `w185`, `w300`, `w500`, `original`         |
| Profil | `profile_sizes`   | `w185`, `h632`, `original`                                |
| Kadr   | `still_sizes`     | `w92`, `w185`, `w300`, `original`                         |

Host nie sprawdza rodzaju, więc plakat ładuje się też w `w1280`. Trzymaj się rozmiarów wymienionych dla danego rodzaju, aby twój kod zgadzał się z konfiguracją i każdym klientem TMDB.

Host obrazów nie wymaga klucza, a edge trzyma każdy plik przez 30 dni. Ponieważ klucz nie jest wysyłany, ładowanie obrazów nie zużywa twoich żądań na minutę. Odpowiedzi niosą `Access-Control-Allow-Origin: *`, więc przeglądarka może je ładować z dowolnej strony. Nieznany rozmiar lub nieznany plik odpowiada 404 z treścią JSON.

## Responsywne obrazy ze srcset

Wybierz najmniejszy rozmiar, który nadal pokrywa wyświetlaną szerokość razy gęstość ekranu. Plakat wyświetlany na szerokość 171 pikseli CSS potrzebuje `w342` na ekranie 2x. Ta funkcja pomocnicza buduje `srcset` z zapisanej ścieżki.

```js
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">`);
```

## Inne endpointy obrazów

Wszystkie poniższe ścieżki używają bazy `https://api.amodb.dev/3` i zwracają tablice wymienione w drugiej kolumnie.

| Metoda i ścieżka                                                             | Zwracane tablice                                              | Parametry                |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------ |
| `GET /tv/{series_id}/images`                                                 | `backdrops`, `logos`, `posters`                               | `include_image_language` |
| `GET /tv/{series_id}/season/{season_number}/images`                          | `posters`, zawsze pusta                                       | brak                     |
| `GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/images` | `stills`, jedna pozycja z `width` i `height` ustawionymi na 0 | brak                     |
| `GET /person/{person_id}/images`                                             | `profiles`                                                    | brak                     |

W filmach i serialach możesz też dodać `append_to_response=images` do wywołania szczegółów. `include_image_language` działa tam również. Dołączony blok nie ma własnego `id`.

Profile osób to jedyne miejsce, w którym `vote_average` niesie prawdziwą ocenę. `id` w odpowiedzi sezonu lub odcinka to ID sezonu lub odcinka. Rekordy nadrzędne opisują strony [osoby](https://amodb.dev/pl/people/) i [seriale](https://amodb.dev/pl/tv-series/).

amodb nie udostępnia ścieżek images dla kolekcji, firm i sieci. Ich rekordy zawierają już jedno `poster_path`, `backdrop_path` lub `logo_path`, opisane na stronach [kolekcje](https://amodb.dev/pl/collections/) i [firmy i sieci telewizyjne](https://amodb.dev/pl/companies-networks/).

## Endpoint konfiguracji

`GET /3/configuration` zwraca blok `images` z `secure_base_url` ustawionym na `https://img.amodb.dev/t/p/`, `base_url` (zwykła forma http) oraz listami rozmiarów `backdrop_sizes`, `logo_sizes`, `poster_sizes`, `profile_sizes` i `still_sizes`. Odczytaj go raz przy starcie, użyj formy bezpiecznej i buduj z niego URL-e zamiast wpisywać rozmiary na stałe. Edge trzyma go w cache przez 24 godziny.

## Błędy

| HTTP | status_code | Kiedy                                                                                     |
| ---- | ----------- | ----------------------------------------------------------------------------------------- |
| 401  | 7           | Brakuje klucza lub jest błędny w wywołaniu JSON.                                          |
| 402  | 36          | Okres planu dobiegł końca.                                                                |
| 404  | 34          | Brak osoby, sezonu lub odcinka o tym ID albo nieznany rozmiar lub plik na hoście obrazów. |
| 405  | 37          | Użyto metody zapisu na ścieżce images.                                                    |
| 429  | 25          | Przekroczono limit żądań na minutę. Poczekaj tyle sekund, ile podaje `Retry-After`.       |

Nieistniejące ID filmu lub serialu nie jest tu błędem: ścieżka images odpowiada 200 z pustymi tablicami, podczas gdy ścieżka szczegółów odpowiada 404. Pełną listę zobacz w [błędy](https://amodb.dev/pl/errors/). Żądania plików obrazów to zwykłe ładowanie HTTP, więc sprawdzaj kod statusu odpowiedzi zamiast parsować treść JSON.

## Praktyczne uwagi

- Zapisuj `file_path` w bazie danych i buduj URL przy renderowaniu. Nowy rozmiar lub host kosztuje wtedy jedną zmianę kodu.
- Przekaż `include_image_language` z językiem wyświetlania plus `null`, na przykład `ru,null`, by nie pojawiały się plakaty z obcym tekstem.
- Pusta tablica to zwykłe 200. Użyj zapasowego `poster_path` z rekordu [szczegółów filmu](https://amodb.dev/pl/movies/).
- Użyj `w1280` dla teł hero na całą szerokość i `w780` dla kart. `original` to zapisany plik w pełnej rozdzielczości i jest dużo większy niż `w780`.
- Kopie na edge żyją 30 dni i są współdzielone, więc plik zażądany przez jednego klienta jest serwowany z cache następnemu.

## Pytania i odpowiedzi

### Czy żądania obrazów wymagają klucza API?

Nie. Pliki pod img.amodb.dev/t/p/{size}/{file} są publiczne, więc możesz używać ich bezpośrednio w znacznikach img i w CSS. Klucza potrzebują tylko endpointy JSON, które wypisują ścieżki plików.

### Jakie rozmiary obrazów mogę zażądać?

w92, w154, w185, w300, w342, w500, w780, w1280, h632 i original. Każdy inny rozmiar odpowiada 404. /3/configuration wymienia, które z nich pasują do każdego typu obrazu, a tabela rozmiarów na tej stronie to powtarza.

### Jak pobrać plakaty tylko w jednym języku?

Przekaż include_image_language z listą oddzieloną przecinkami, na przykład en,null. Wartość null dodaje obrazy bez tekstu. Wracają tylko wymienione języki. Parametr language nie filtruje obrazów.

### Dlaczego ścieżka pliku zaczyna się od ukośnika?

Ścieżki w stylu TMDB zawierają początkowy ukośnik, aby można je dołączyć wprost do segmentu rozmiaru. Zapisuj samą ścieżkę i buduj pełny URL przy renderowaniu.

## Powiązane strony

- [API szczegółów filmu](https://amodb.dev/pl/movies/): API szczegółów filmu z tymi samymi ścieżkami i JSON co TMDB v3. Pobierz film po ID, dołącz obsadę, oceny i nastroje przez append_to_response, 14 języków.
- [API szczegółów serialu](https://amodb.dev/pl/tv-series/): API szczegółów serialu z tymi samymi ścieżkami i JSON co TMDB v3. Serial po ID z sezonami, sieciami, klasyfikacją wiekową i dostawcami streamingu.
- [API szczegółów osoby](https://amodb.dev/pl/people/): API szczegółów osoby z tymi samymi ścieżkami i JSON co TMDB v3. Aktor lub ekipa po ID z biografią, filmografią, zewnętrznymi ID i listą popularnych osób.
- [API kolekcji](https://amodb.dev/pl/collections/): API kolekcji filmów ze ścieżkami TMDB v3: pobierz franczyzę po ID ze wszystkimi filmami, odczytaj belongs_to_collection w filmie i szukaj kolekcji po nazwie.
- [API plakatów filmowych i CDN obrazów](https://amodb.dev/pl/movie-poster-api/): API plakatów filmowych z hostem obrazów bez klucza. Rozmiary od w92 do original, listy srcset i wybór plakatów według języka przez include_image_language.
- [Szybki start](https://amodb.dev/pl/quickstart/): Pierwsze wywołanie amodb: zdobądź klucz, pobierz szczegóły filmu w curl, JavaScript, Python, PHP lub Go, potem załaduj plakat z img.amodb.dev. Poniżej 2 minut.

Zaktualizowano: 2026-10-18. Source: https://amodb.dev/pl/images-api/
