# Movie poster API and image CDN

> Posters load from https://img.amodb.dev/t/p/{size}/{file} with no API key. The file part is poster_path from any movie, search or list reply. Size names run from w92 to original, and include_image_language picks posters by language. Only the JSON calls need your key.

## Movie posters from an image host that needs no key

A poster URL is `https://img.amodb.dev/t/p/{size}/{file}`. The file part is the `poster_path` string from any movie, search or list reply, and it starts with a slash. The size part is a name such as `w342`. Images need no key, so browsers and apps can load them directly. The JSON that tells you the file name still needs your key, and amodb is an independent API that speaks the TMDB v3 protocol, so existing image code keeps working after a host change.

What you get:

- Posters, backdrops, logos, profile photos and episode stills on one host.
- Ten size names, from `w92` to `original`.
- Poster lists per title, with language, width and height on each item.
- A 30 day edge cache for images.

## What data you get

| Image                    | File name comes from                                | Common sizes                                              |
| ------------------------ | --------------------------------------------------- | --------------------------------------------------------- |
| Movie poster             | `poster_path` on `/3/movie/{movie_id}` and in lists | `w92`, `w154`, `w185`, `w342`, `w500`, `w780`, `original` |
| Backdrop                 | `backdrop_path`                                     | `w300`, `w780`, `w1280`, `original`                       |
| Person photo             | `profile_path` on `/3/person/{person_id}`           | `w185`, `h632`, `original`                                |
| Company or network logo  | `logo_path`                                         | `w92`, `w154`, `w185`, `w300`, `w500`, `original`         |
| Episode still            | `still_path`                                        | `w92`, `w185`, `w300`, `original`                         |
| All posters of one title | `GET /3/movie/{movie_id}/images`                    | `posters` array                                           |

TV series, seasons and collections carry `poster_path` too. Read the exact size lists from the `images` block of `GET /3/configuration` once at startup instead of hardcoding them. The images reference is on [images](https://amodb.dev/images-api/).

## Start in 3 steps

1. Choose a plan on [pricing](https://amodb.dev/pricing/). It sets the rate of the JSON calls: 20, 40 or 120 requests per minute.
2. Create a key in your account dashboard.
3. Read `poster_path` from a details or list call, then put it behind the image host and a size name.

## Example request

The Go program lists the posters of The Matrix (movie id 603) in English and without text, picks the widest one, prints a ready `srcset` and saves the `w342` file. The key goes to the JSON call only.

```bash
# the JSON call needs the key
curl -s "https://api.amodb.dev/3/movie/603/images?include_image_language=en,null" \
  -H "Authorization: Bearer $AMODB_KEY"

# the image call does not
curl -s -o poster.jpg "https://img.amodb.dev/t/p/w342/aOIuZAjPaRIE6CMzbazvcHuHXDc.jpg"
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

const imgBase = "https://img.amodb.dev/t/p/"

type poster struct {
	FilePath string  `json:"file_path"`
	Lang     *string `json:"iso_639_1"`
	Width    int     `json:"width"`
}

type images struct {
	Posters []poster `json:"posters"`
}

func srcset(file string) string {
	var parts []string
	for _, w := range []int{185, 342, 500, 780} {
		parts = append(parts, fmt.Sprintf("%sw%d%s %dw", imgBase, w, file, w))
	}
	return strings.Join(parts, ", ")
}

func main() {
	client := &http.Client{Timeout: 10 * time.Second}

	url := "https://api.amodb.dev/3/movie/603/images?include_image_language=en,null"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(res.Status)
	}

	var imgs images
	if err := json.NewDecoder(res.Body).Decode(&imgs); err != nil {
		panic(err)
	}
	if len(imgs.Posters) == 0 {
		fmt.Println("no posters")
		return
	}

	best := imgs.Posters[0]
	for _, p := range imgs.Posters {
		if p.Width > best.Width {
			best = p
		}
	}
	lang := "none"
	if best.Lang != nil {
		lang = *best.Lang
	}
	fmt.Println("best poster:", best.FilePath, "language:", lang, "width:", best.Width)
	fmt.Println("srcset:", srcset(best.FilePath))

	// Image requests carry no key.
	pic, err := client.Get(imgBase + "w342" + best.FilePath)
	if err != nil {
		panic(err)
	}
	defer pic.Body.Close()
	if pic.StatusCode != http.StatusOK {
		panic(pic.Status)
	}
	out, err := os.Create("poster.jpg")
	if err != nil {
		panic(err)
	}
	defer out.Close()
	io.Copy(out, pic.Body)
}
```

A trimmed images reply for `include_image_language=en,null`: 3 of the 145 posters, an English one, the widest English one and a textless one. The reply also holds `backdrops` and `logos`, left out here.

```json
{
  "id": 603,
  "posters": [
    {
      "aspect_ratio": 0.667,
      "height": 2250,
      "iso_639_1": "en",
      "file_path": "/dXNAPwY7VrqMAo51EKhhCJfaGb5.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1500
    },
    {
      "aspect_ratio": 0.667,
      "height": 3000,
      "iso_639_1": "en",
      "file_path": "/aOIuZAjPaRIE6CMzbazvcHuHXDc.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 2000
    },
    {
      "aspect_ratio": 0.701,
      "height": 1426,
      "iso_639_1": null,
      "file_path": "/iLWzJxatuwwMT76dtMuwh3FEp2X.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1000
    }
  ]
}
```

## Choose a size

The number in a `w` size is the image width in pixels, and `h632` is a height. Small names save bandwidth, large names keep detail on dense screens.

| Size           | Typical use                           |
| -------------- | ------------------------------------- |
| `w92`          | Search suggestions and tiny list rows |
| `w154`, `w185` | Dense lists and cards                 |
| `w342`         | Grid tiles on phones                  |
| `w500`         | Detail page                           |
| `w780`         | Large screens and 2x displays         |
| `original`     | Archive or print, the unscaled file   |

Give the browser several widths and let it choose. The Go program above prints a `srcset` with `185w`, `342w`, `500w` and `780w` candidates. Pair it with a `sizes` attribute that matches your layout, for example `sizes="(max-width: 600px) 45vw, 185px"` on the `img` element.

## Poster languages

Each item in `posters` has an `iso_639_1` value, either a language code or null for artwork without text. The `include_image_language` parameter takes a comma separated list of codes.

| Value     | Returns                                  |
| --------- | ---------------------------------------- |
| `en`      | Posters marked English                   |
| `en,null` | English posters and posters without text |
| `null`    | Only posters without text                |

A good fallback order is the poster in your user's language, then a textless one, then English. Inside each group, sort by `width` and keep the first on ties. `vote_average` and `vote_count` are `0` on movie and TV images, so they rank nothing. Poster languages describe the text printed on the artwork, so they do not follow the 14 interface languages. Check `iso_639_1` on what comes back, because the set depends on the title.

## Storing and caching posters

- Save `poster_path`, not the full URL. Any size can be built from it later.
- The edge caches images for 30 days. Details are cached for 6 hours, so read `poster_path` again after that to see whether it changed.
- Keep the key on your server. Send the browser the image URLs only.
- When `poster_path` is null, render a placeholder instead of a broken image.

## How it compares

| Capability            | Scrape pages for images            | Host your own copies            | amodb image host                                    |
| --------------------- | ---------------------------------- | ------------------------------- | --------------------------------------------------- |
| Key needed            | None, the terms of each site apply | None                            | No key for images                                   |
| Size variants         | Whatever size the page serves      | You generate and store each one | Fixed size names in the URL, `w92` up to `original` |
| Finding the file      | Parse HTML                         | Your own database               | `poster_path` in every reply                        |
| Artwork languages     | Page by page                       | Whatever you collected          | `include_image_language` on the images endpoints    |
| Storage and bandwidth | Yours, plus the source's           | Yours                           | The browser loads from `img.amodb.dev`              |
| Cache                 | Yours                              | Yours                           | 30 days at the edge                                 |

## Pricing

Image downloads carry no key. Your plan limits only the JSON calls that give you file names, and list replies already include `poster_path` for every item, so one list call feeds a whole grid. Choose 20, 40 or 120 requests per minute on [pricing](https://amodb.dev/pricing/). Access is prepaid for 1, 3, 6, 12 or 24 months with 0, 10, 20, 30 and 40 percent off, nothing renews by itself, and there is no free tier.

## Questions and answers

### Do poster downloads need an API key?

No. Images come from https://img.amodb.dev/t/p/{size}/{file} without a key, so a browser can load them directly. Only the JSON calls that return poster_path use your key.

### Which poster sizes are available?

The size names are w92, w154, w185, w300, w342, w500, w780, w1280, h632 and original. Which ones fit an image type is listed in the images block of GET /3/configuration.

### How do I get a poster in a given language?

Call /3/movie/{id}/images?include_image_language=en,null and read iso_639_1 on each poster. The value null marks artwork without text. Then choose by width. vote_average and vote_count are 0 on movie and TV images.

### How long are images cached?

The edge keeps images for 30 days. Store the poster_path value, not the full URL, so you can build any other size later without another lookup.

### What do I show when poster_path is null?

Some titles have no poster. Show a placeholder from your own design, and optionally call the images endpoint to look for other posters before you give up.

## Related pages

- [Images API and the TMDB image path](https://amodb.dev/images-api/): Build a TMDB image path: https://img.amodb.dev/t/p/{size}/{file}. List posters, backdrops and logos per title, then load them in 10 sizes with no key.
- [Movie details API](https://amodb.dev/movies/): Movie details API with the same paths and JSON as TMDB v3. Get one movie by id, add credits, ratings and moods with append_to_response, in 14 languages.
- [TV series API, the TV show endpoint](https://amodb.dev/tv-series/): TV show API reference for GET /3/tv/{series_id}: names, status, seasons, networks and the next episode, in TMDB v3 JSON with content ratings and providers.
- [Search API](https://amodb.dev/search/): Search API with the same paths and JSON as TMDB v3. Find movies, TV shows, people, collections, companies and keywords by text, with year filters and paging.
- [Quickstart](https://amodb.dev/quickstart/): Make your first amodb call: get a key, request movie details in curl, JavaScript, Python, PHP or Go, then load the poster from img.amodb.dev. Under 2 minutes.
- [Authentication](https://amodb.dev/authentication/): How to send your amodb key: api_key in the query, an Authorization Bearer header, or apikey for OMDb calls. Key format, storage, rotation and the 401 errors.

Updated: 2026-10-18. Source: https://amodb.dev/movie-poster-api/
