Movie posters from an image host that needs no key
A poster URL is https:/. 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
w92tooriginal. - 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 / | 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.
Start in 3 steps
- Choose a plan on pricing. It sets the rate of the JSON calls: 20, 40 or 120 requests per minute.
- Create a key in your account dashboard.
- Read
poster_pathfrom 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.
# 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"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_: 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.
{
"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= 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_pathagain after that to see whether it changed. - Keep the key on your server. Send the browser the image URLs only.
- When
poster_pathis 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. 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.