The Studio Ghibli API was one of the friendliest "learn to fetch" datasets ever published: 22 films, characters, locations, no keys, lovely nested links between everything. Thousands of tutorials, bootcamp exercises, YouTube videos, and README examples open with the same line โ and here is what that line does today (verified September 2026):
curl https://ghibliapi.herokuapp.com/films
# โ HTTP 404
# โ <title>No such app</title> โ Heroku's error page, not the API's
Heroku shut down its free dynos in November 2022 and the original host never came back. The tutorial you're following isn't wrong โ its data source's landlord evicted it.
Credit where due: the community mirror at ghibliapi.vercel.app is excellent. In our September 2026 checks it answered in ~0.5s, needs no key, sends real CORS headers (browser fetch() works), serves the same five collections (22 films, 57 people, 25 locations, 7 species, 3 vehicles), supports exact-match filters (?director=Hayao Miyazaki โ 9 films) and ?limit= / ?fields= โ and it even preserved the original record UUIDs, so the 2baf70d1-โฆ film id hardcoded in a 2019 tutorial still resolves. If all you need is to finish the tutorial, replace the host and carry on:
- https://ghibliapi.herokuapp.com/films
+ https://ghibliapi.vercel.app/films
Where you'll hit its edges (each verified September 2026):
?title=Totoro returns [] โ you need the full string ?title=My Neighbor Totoro. There's no substring or full-text search.?sortBy=rt_score&order=desc comes back in storage order (we got 95, 97, 93โฆ). A "top-rated Ghibli films" exercise means sorting client-side โ where rt_score being the string "95" will bite your .sort().POST /films answers 201 with a fresh UUID โ and GET on that id is 404, the collection count stays 22. Fine for a read-only viewer; a trap the moment your exercise adds a favorites button.The whole dataset is ~90KB of JSON. Pull it once, keep the original UUIDs as a field (import renumbers ids to integers, so we stash the UUID first), and host your own copy โ no signup:
for e in films people locations species vehicles; do
curl -s "https://ghibliapi.vercel.app/$e" > "$e.json"
done
jq -n '{films: input | map({uuid:.id} + .),
people: input | map({uuid:.id} + .),
locations: input | map({uuid:.id} + .),
species: input | map({uuid:.id} + .),
vehicles: input | map({uuid:.id} + .)}' \
films.json people.json locations.json species.json vehicles.json > db.json
curl -s -X POST "https://mockbird.mockbird.workers.dev/api/projects/import?name=ghibli" \
-H 'content-type: application/json' --data-binary @db.json
# โ {"id":"YOUR_ID","adminKey":"YOUR_ADMIN_KEY",
# "resources":[{"name":"films","records":22},{"name":"people","records":57},โฆ]}
Every field is kept verbatim โ including the nested people/locations URL arrays inside each film (the importer notes them as warnings and stores them untouched), and the image/movie_banner poster URLs, which point at TMDB and still render in your app straight from your copy.
All of these ran against a live import while writing this guide:
BASE=https://mockbird.mockbird.workers.dev/m/YOUR_ID
curl "$BASE/films?director=Hayao Miyazaki" # exact match โ same 9 films as upstream
curl "$BASE/films?title_like=totoro" # substring, case-insensitive โ My Neighbor Totoro
curl "$BASE/films?q=witch" # full-text across every field โ 5 films
curl "$BASE/films?sortBy=rt_score&order=desc" # numeric-aware even on "97"-style strings
curl "$BASE/films?rt_score_gte=97&select=title,rt_score"
# โ Grave of the Fireflies 97 ยท Only Yesterday 100 ยท Spirited Away 97 ยท Princess Kaguya 100
curl "$BASE/films?uuid=2baf70d1-42bb-4437-b551-e5fed5a87abe" # the stashed UUIDs still look up
And writes are real. Add the film the dataset is missing, get it back, keep it:
curl -X POST "$BASE/films" -H 'content-type: application/json' \
-d '{"title":"The Boy and the Heron","director":"Hayao Miyazaki","release_date":"2023","rt_score":"96"}'
# โ {"id":23,โฆ}
curl "$BASE/films/23" # โ it persists โ X-Total-Count is now 23
curl -X PATCH "$BASE/films/3" -H 'content-type: application/json' -d '{"favorite":true}'
# โ your favorites button now has a backend
Pagination comes with an honest X-Total-Count header (list responses default to 20 per page โ use ?limit=/?page=), /m/YOUR_ID/types.ts generates a TypeScript Film interface from your records (?format=zod for Zod), and failure drills are a query param away: ?mock_delay=3000, ?mock_status=500, ?mock_ratelimit=5 โ the parts of a fetch exercise no polite public API will simulate for you.
| ghibliapi.herokuapp.com | ghibliapi.vercel.app | Your copy | |
|---|---|---|---|
| Alive (Sep 2026) | โ 404 "No such app" | โ ~0.5s, CORS-open | โ yours until you delete it |
| Setup | โ | โ none at all | one curl (or paste in the dashboard) |
| Original UUIDs | โ | โ preserved | kept in a uuid field (ids become integers) |
| Exact filters / projection | โ | โ ?director=, ?fields= | โ same, plus ?select= |
| Substring / full-text search | โ | โ ?title=Totoro โ [] | โ ?title_like=, ?q= |
| Sorting / ranges | โ | โ sortBy ignored | โ ?sortBy=, ?rt_score_gte= |
| Writes persist | โ | โ 201 then GET-back 404 | โ POST/PATCH/DELETE are real |
| New films appear upstream | โ | when the community updates it | you add them (see above) |
The fair split: for a quick read-only viewer, the vercel.app successor is the easiest thing on this page โ use it and thank its maintainers. The moment your exercise needs search, sorting, a favorites button, or data that survives someone else's hosting decisions, spend the one curl and own the copy. Current status of the successor: /status/ghibli. Related: xkcd API, Rick and Morty API, GitHub Jobs (another dead tutorial API), mock any third-party API.