Teaching with the Dog API
A lot of the people who use this API are students. I see it linked from Canvas, Google Classroom, Moodle, Schoology, Colab notebooks, The Odin Project and at least one hiring take-home. This page is for whoever wrote those lessons, and for whoever is about to write the next one.
It has 283 breeds, 9 groups, 483 facts and 2,354 pictures. Small enough to understand in an afternoon, big enough to need pagination.
Why it's safe to build a course on
- It's been running since 2016, and I'm not going anywhere.
- No key, no sign-up. Nobody spends the first twenty minutes of a lesson making an account, and nobody leaks a key in a public repo or a screen share.
- CORS is open.
fetch()works from a static page, a CodePen, a notebook orlocalhost, no proxy needed. - Versions don't vanish. When v2 shipped, v1 kept answering, and it still does. Your old slides keep working.
- The rate limit is generous. 300 requests per minute per IP, which a whole room sharing one connection stays well under. No monthly quota to run out of halfway through a term.
- It's free, for classes, workshops, tutorials and videos, paid or not.
My promise: I won't remove an endpoint or change what an existing endpoint returns in a way that breaks code written against it. New things get added; old things stay. If something ever has to go, it will be announced here well ahead of time, and the old version will keep answering for at least a full school year after that.
Beginner
Fetch a random fact
Goal: make one request, read JSON, put a value on the page.
GET /api/v2/facts returns one random fact; ?limit=
asks for up to 5. Then add a button that fetches another one.
const res = await fetch("https://dogapi.dog/api/v2/facts");
const json = await res.json();
console.log(json.data[0].attributes.body);
import requests
res = requests.get("https://dogapi.dog/api/v2/facts")
print(res.json()["data"][0]["attributes"]["body"])
curl "https://dogapi.dog/api/v2/facts?limit=3"
Show a random dog
Goal: see an API answer without writing any JavaScript.
GET /api/v2/breeds/image redirects to a random dog picture, so an
<img> can point straight at it. ?size= takes
thumb, medium, large or
full.
<img src="https://dogapi.dog/api/v2/breeds/image?size=medium" alt="A random dog">
import requests
res = requests.get("https://dogapi.dog/api/v2/breeds/image", allow_redirects=False)
print(res.headers["Location"])
curl -sI "https://dogapi.dog/api/v2/breeds/image" | grep -i location
Intermediate
List breeds, one page at a time
Goal: walk a paginated collection. GET /api/v2/breeds
takes page[number] and page[size], and the response
carries links.next and meta.pagination. Follow
next until it's gone. filter[has_images]=true keeps
only breeds that have a picture.
let url = "https://dogapi.dog/api/v2/breeds?page[size]=20&page[number]=1";
while (url) {
const json = await (await fetch(url)).json();
json.data.forEach((breed) => console.log(breed.attributes.name));
url = json.links.next;
}
import requests
url = "https://dogapi.dog/api/v2/breeds"
params = {"page[size]": 20, "page[number]": 1}
while url:
json = requests.get(url, params=params).json()
for breed in json["data"]:
print(breed["attributes"]["name"])
url, params = json["links"].get("next"), None
A breed and its group
Goal: follow a relationship. GET /api/v2/breeds/:id
returns a breed whose relationships.group.data.id points at
GET /api/v2/groups/:id. Two requests, one join. Going the other
way, GET /api/v2/groups/:id?include=breeds brings every breed of
a group along in included. Ask for an id that doesn't exist too,
and handle the 404 without breaking the page.
const breed = (await (await fetch("https://dogapi.dog/api/v2/breeds/036feed0-da8a-42c9-ab9a-57449b530b13")).json()).data;
const groupId = breed.relationships.group.data.id;
const group = (await (await fetch(`https://dogapi.dog/api/v2/groups/${groupId}`)).json()).data;
console.log(`${breed.attributes.name} is in the ${group.attributes.name}`);
Project ideas
- Breed quiz. Show a picture from
/api/v2/breeds/:id/imageand four breed names; the student guesses.filter[has_images]=truegives you the breeds worth asking about. - Flashcards. Breed name on one side, description, origin and life span on the other.
- Comparison chart. Plot
life,male_weightandfemale_weightacross a group, then argue about what the ranges actually mean. - "Which group?" sorter. Drag breeds into their groups, checked against
/api/v2/groups?include=breeds. - Take-home interview. "Build a page that lists breeds with search and pagination, shows a detail view with the breed's group and picture, and handles loading and error states." Small enough for an evening, with enough edges to talk about.
Every endpoint and parameter is in the API v2 reference, with a try-it button on each one.
Share this with your class
If you link students to the site, use this link. Course platforms strip the referrer, so most classes show up to me as anonymous "direct" traffic. The tags on the end let me see that a class came by, and nothing more: no cookies, no accounts, nothing about your students.
https://dogapi.dog/?utm_campaign=teach&utm_medium=course&utm_source=classroom
And I'd really like to hear about your course: what you teach, who it's for, what broke. Email me at [email protected] or open an issue on GitHub. Knowing a class depends on something is the best reason I have to keep it exactly the way it is.