🐶 Dog API by kinduff

The Dog API provides dog data as a service!

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

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.

JavaScript
const res = await fetch("https://dogapi.dog/api/v2/facts");
const json = await res.json();
console.log(json.data[0].attributes.body);
Python
import requests

res = requests.get("https://dogapi.dog/api/v2/facts")
print(res.json()["data"][0]["attributes"]["body"])
curl
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.

HTML
<img src="https://dogapi.dog/api/v2/breeds/image?size=medium" alt="A random dog">
Python
import requests

res = requests.get("https://dogapi.dog/api/v2/breeds/image", allow_redirects=False)
print(res.headers["Location"])
curl
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.

JavaScript
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;
}
Python
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.

JavaScript
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

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.

Link for your course
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.