# Dog Image API > The same nine views of every dog breed, on a transparent background. 298 dog breeds, nine views per set, free transparent previews and full-size PNG files through an API. The images are AI-generated illustrations made to one standard. Site: https://dogimage.dev. Short index: https://dogimage.dev/llms.txt # API reference Base URL: https://dogimage.dev | Request | Key | Returns | |---|---|---| | GET /api/v1/breeds | no | JSON: views and every breed with its variants | | GET /api/v1/breeds/{breed} | no | JSON: one breed with preview links for every view | | GET /api/v1/images/{breed}/{variant}/{view}?size=512 or 1024 | no | 302 redirect to the transparent WebP preview | | GET /api/v1/images/{breed}/{variant}/{view}?size=full | yes | 200, the 2048 pixel transparent PNG | | GET /api/v1/usage | yes | JSON: month, plan, limit, used, remaining | - `{breed}` is a slug such as `golden-retriever`. `{variant}` is a color slug, or `default` for the breed's main set. - `{view}` is one of: `front` (Front), `front-left` (Front three-quarter, left), `left` (Left side), `rear-left` (Rear three-quarter, left), `rear` (Rear), `rear-right` (Rear three-quarter, right), `right` (Right side), `front-right` (Front three-quarter, right), `head` (Head portrait). - Key: created on the account page, sent as `Authorization: Bearer `, from a server only. - Browser apps on other sites may read the two breeds endpoints and the preview files. - Full-size replies carry `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. - Full-size files are meant to be downloaded once and stored on your side. - Errors are JSON `{"error": {"code", "message"}}`: 400 bad_size, 401 missing_or_invalid_key, 402 no_active_plan, 404 unknown_breed / unknown_variant / unknown_view, 429 quota_used_up, 502 file_unavailable (not counted), 503 plan_check_failed. ```bash curl https://dogimage.dev/api/v1/breeds curl -L "https://dogimage.dev/api/v1/images/pug/default/front-left?size=1024" -o pug.webp curl "https://dogimage.dev/api/v1/images/pug/default/front-left?size=full" -H "Authorization: Bearer $DOG_IMAGE_API_KEY" -o pug.png ``` # Pricing: Dog Image API Prices are per month, in US dollars. One image is one view of one set at full size (2048 x 2048 pixel transparent PNG). A plan counts distinct images per calendar month (UTC); fetching the same image again in the same month is free. ## Free - Price: $0 - Includes: previews of every image at 512 and 1024 pixels (transparent WebP), on the site and through the API, no key needed - Condition: a visible credit link next to the image or on the same page ## Starter - Price: $19/month - Includes: 200 images a month at full size, transparent PNG, 2048 pixels - Use: in your own products (apps, sites, documents, print, packaging). No credit needed. No resale of the images as a collection ## Pro - Price: $49/month - Includes: 700 images a month at full size, transparent PNG, 2048 pixels - Use: in your own products (apps, sites, documents, print, packaging). No credit needed. No resale of the images as a collection ## All images - Price: $89/month - Includes: every image a month at full size, transparent PNG, 2048 pixels - Use: in your own products (apps, sites, documents, print, packaging). No credit needed. No resale of the images as a collection Catalog today: 298 breeds, 329 sets, 9 views per set. Subscribe: https://dogimage.dev/pricing (sign in, then pick a plan). Cancel any time; access lasts until the paid month ends. License: https://dogimage.dev/license. Docs: https://dogimage.dev/docs. # License, in short - Free previews (512 and 1024 pixels): any use, commercial included, with a visible link that credits Dog Image API. - Full-size files (paid plans): use in your own products. No credit needed. Not allowed: reselling, sublicensing or republishing the images as a collection, dataset or image library, or building a competing image service with them. Files fetched while a plan was active stay usable in what was built with them. - Breed names, color names and summaries come from Wikidata (CC0) and Wikipedia (CC BY-SA 4.0). The catalog JSON is shared under CC BY-SA 4.0. - The images are AI-generated illustrations. They show no real dog and are not a breed standard. # Breeds French Bulldog, Golden Retriever, German Shepherd, Dachshund, Doberman, Poodle, Great Dane, Cane Corso, Australian Shepherd, Chihuahua, Bernese Mountain Dog, Border Collie, Rottweiler, Shih Tzu, Beagle, Cavapoo, Cocker Spaniel, Pug, Shiba Inu, Boston Terrier, Bulldog, Cavalier King Charles Spaniel, Chow Chow, Irish Wolfhound, Newfoundland, Basset Hound, Bichon Frise, Bull Terrier, Labrador Retriever, Tibetan Mastiff, American Bulldog, American Bully, Australian Cattle Dog, Borzoi, Cockapoo, Dogo Argentino, German Shorthaired Pointer, Goldendoodle, Jack Russell Terrier, Labradoodle, Rhodesian Ridgeback, Akita, American Staffordshire Terrier, Basenji, Irish Setter, Maltese, Portuguese Water Dog, Shar Pei, Siberian Husky, Saint Bernard, Staffordshire Bull Terrier, Yorkshire Terrier, Afghan Hound, American Pit Bull Terrier, Black Mouth Cur, Bloodhound, Boerboel, Boxer, Bullmastiff, Catahoula Leopard Dog, English Mastiff, Giant Schnauzer, Italian Greyhound, Miniature Schnauzer, Presa Canario, Rat Terrier, Vizsla, Airedale Terrier, Cairn Terrier, Dutch Shepherd, English Cocker Spaniel, King Charles Spaniel, Lhasa Apso, Nova Scotia Duck Tolling Retriever, Pekingese, Scottish Terrier, Beauceron, Belgian Malinois, Brittany Spaniel, Chesapeake Bay Retriever, Coton de Tulear, Greyhound, Havanese, Leonberger, Miniature Pinscher, Shetland Sheepdog, Chinese Crested Dog, English Setter, English Springer Spaniel, Pembroke Welsh Corgi, Plott Hound, Samoyed, Alaskan Klee Kai, Australian Kelpie, Bedlington Terrier, Border Terrier, Carolina Dog, Keeshond, Lagotto Romagnolo, Mountain Cur, Neapolitan Mastiff, Norwegian Elkhound, Old English Sheepdog, Saluki, Schipperke, Affenpinscher, American Cocker Spaniel, Clumber Spaniel, English Pointer, Puggle, Redbone Coonhound, Rough Collie, American Eskimo Dog, Biewer Terrier, Bouvier des Flandres, Caucasian Shepherd Dog, Japanese Chin, Silken Windhound, Treeing Walker Coonhound, Bluetick Coonhound, Cardigan Welsh Corgi, Czechoslovakian Wolfdog, English Shepherd, Greater Swiss Mountain Dog, Irish Terrier, Irish Water Spaniel, Komondor, Norwich Terrier, Patterdale Terrier, Standard Schnauzer, Tibetan Terrier, Wire Fox Terrier, Akbash, Alaskan Husky, American Foxhound, Black Russian Terrier, Blue Lacy, Dogue de Bordeaux, Eurasier, German Pinscher, Gordon Setter, Miniature American Shepherd, Norfolk Terrier, Otterhound, Parson Russell Terrier, Pharaoh Hound, Pudelpointer, Scottish Deerhound, Teddy Roosevelt Terrier, Wetterhoun, Xoloitzcuintle, Black and Tan Coonhound, Bolognese, Briard, Canaan Dog, Dalmatian, Ibizan Hound, Japanese Spitz, Kerry Blue Terrier, Norwegian Lundehund, Soft-Coated Wheaten Terrier, Swedish Vallhund, Tibetan Spaniel, American Hairless Terrier, Australian Terrier, Bearded Collie, Chinook, Finnish Lapphund, Finnish Spitz, German Spitz, Karelian Bear Dog, Kuvasz, Manchester Terrier, Mudi, Puli, Skye Terrier, Smooth Collie, Spanish Water Dog, Thai Ridgeback, Toy Fox Terrier, West Highland White Terrier, American Water Spaniel, Barbet, Icelandic Sheepdog, Jagdterrier, Olde English Bulldogge, Welsh Springer Spaniel, Azawakh, Dandie Dinmont Terrier, English Foxhound, Basset Fauve de Bretagne, Field Spaniel, Fila Brasileiro, Glen of Imaal Terrier, Hovawart, Kooikerhondje, Korean Jindo, Lancashire Heeler, Miniature Bull Terrier, Berger Picard, Portuguese Podengo, Smooth Fox Terrier, Spanish Mastiff, Spinone Italiano, American English Coonhound, Bavarian Mountain Hound, Bully Kutta, Entlebucher Mountain Dog, Flat-Coated Retriever, Kai Ken, Peruvian Hairless Dog, Pyrenean Mastiff, Tamaskan Dog, White Shepherd, Aidi, Alapaha Blue Blood Bulldog, Armenian Gampr, Australian Stumpy Tail Cattle Dog, Bohemian Shepherd, Harrier, Norwegian Buhund, Petit Basset Griffon Vendeen, Pumi, Russian Toy, Saarloos Wolfdog, Sealyham Terrier, Sloughi, Small Munsterlander, Tosa, Treeing Tennessee Brindle, Wirehaired Vizsla, Calupoh, Taiwan Dog, German Longhaired Pointer, Karakachan Dog, Koolie, Lowchen, Polish Lowland Sheepdog, Stabyhoun, Sussex Spaniel, Yakutian Laika, American Leopard Hound, Cesky Terrier, Irish Red and White Setter, Large Munsterlander, Canadian Eskimo Dog, Cirneco dell'Etna, Kokoni, Lapponian Herder, Great Pyrenees, Toy Manchester Terrier, Transylvanian Hound, Broholmer, Curly-Coated Retriever, Dunker, East European Shepherd, Miniature Fox Terrier, Picardy Spaniel, Prague Ratter, Volpino Italiano, Australian Silky Terrier, Bergamasco Shepherd, Carpathian Shepherd Dog, Huntaway, Japanese Terrier, Kunming Dog, Landseer, Phu Quoc Ridgeback, Rafeiro do Alentejo, Swedish Lapphund, Tornjak, West Siberian Laika, Xiasi Dog, Barbado da Terceira, Basque Shepherd Dog, Black Norwegian Elkhound, Braque du Bourbonnais, Brazilian Terrier, Bull Arab, Catalan Sheepdog, Croatian Sheepdog, Drever, English Toy Terrier, German Spaniel, Indian Spitz, Kromfohrlander, Mexican Lapdog, Moscow Watchdog, Nureongi, Pyrenean Sheepdog, Rajapalayam, Russian Tsvetnaya Bolonka, Sapsali, Sarabi Mastiff, Tatra Shepherd Dog, Welsh Sheepdog, Aksaray Malaklisi, Alano Espanol. --- Source: https://dogimage.dev/guides/tailwind-dog-breed-card # A dog breed card with Tailwind CSS Build a reusable dog breed card with Tailwind CSS: image on a tinted stage, name, link and credit, as HTML and a React component, with a grid and dark mode. A breed card is a stage, a name and a credit. Put the transparent preview on a tinted square, add the breed name as a link to its page, and keep the Dog Image API credit link visible. This guide builds that card in HTML, then as a React component. ## The idea Dog Image API previews are transparent WebP files. A transparent image shows whatever is behind it, so the stage color is the card's whole background story. One tinted square behind the dog makes every card look alike, whatever the breed's coat color. The card has four parts: 1. A square stage with a tint, holding the image. 2. The breed name, linking to the breed page. 3. A short line saying what the picture is. 4. The credit link. If you have not set up Tailwind yet, add it to a Vite project with the official plugin: `npm install tailwindcss @tailwindcss/vite`, register `tailwindcss()` in `vite.config`, and put `@import "tailwindcss";` at the top of your CSS file. In Tailwind 4, dark mode follows the visitor's system setting by default, which is what we use here. ## The card in HTML ```html
Pug, AI-generated illustration, front three-quarter view

Pug

AI-generated illustration of an adult dog.

Pug image: Dog Image API
``` Why these classes: - `aspect-square` matches the square canvas of every preview, so the stage never changes shape while the image loads. - `object-contain` keeps the whole image inside the stage. The dog fills at most 80 percent of its canvas, so a margin is already built in. - `overflow-hidden` with `rounded-2xl` clips the stage to the card's corners. - The `dark:` classes change only colors. The image is untouched. The name link and the credit link point to the same breed page here, which is the form the site gives for the credit. The visible credit text is what the free license asks for: a link to Dog Image API next to the image or on the same page. See the [license](/license). ## The same card as a React component Cards repeat, so make the name and slug into props: ```tsx type DogBreedCardProps = { slug: string; name: string; view?: string; }; const API = "https://dogimage.dev"; export function DogBreedCard({ slug, name, view = "front-left" }: DogBreedCardProps) { const page = `${API}/dogs/${slug}`; return (
{`${name},

{name}

AI-generated illustration of an adult dog.

{name} image: Dog Image API
); } ``` Every class is written out in full. Tailwind finds classes by scanning your source for complete strings, so avoid building them from pieces such as `` `bg-${color}-50` ``, which the scanner would not see. ## A responsive grid ```tsx const breeds = [ { slug: "pug", name: "Pug" }, { slug: "golden-retriever", name: "Golden Retriever" }, { slug: "german-shepherd", name: "German Shepherd" }, { slug: "beagle", name: "Beagle" }, { slug: "dachshund", name: "Dachshund" }, { slug: "samoyed", name: "Samoyed" }, ]; export function BreedGrid() { return (
); } ``` One column on a phone, two from the `sm` breakpoint, three from `lg`. To fill the grid from the live catalog instead of a fixed list, fetch `GET /api/v1/breeds`, which needs no key and allows cross-origin reads. The [React picker guide](/guides/react-dog-breed-picker) shows that request. ## Dark mode, checked Switch your operating system to dark mode and reload. The card flips to `stone-900`, the stage to `stone-800`, and the text to light colors. Dark coats stay readable against `stone-800` in most cases, but look at your own breeds. If a black dog sinks into the stage, lighten the dark stage to `stone-700`. To control the theme with a toggle instead of the system setting, Tailwind 4 lets you redefine the `dark` variant with `@custom-variant` in your CSS. The Tailwind documentation covers it. ## Other angles Pass `view="head"` for a head portrait that fits small cards in a list, or `view="left"` for a side profile. The [left side gallery](/views/left) shows the whole catalog from that angle, and the [docs](/docs) list every view id. ## Questions **Why put the dog on a tinted stage?** The preview has a transparent background, so it takes the color behind it. A soft tint gives the dog an even backdrop and keeps every card in a grid looking the same. **Do I need to change anything for dark mode images?** No file changes. The image stays transparent, so only the stage color changes. The dog is an illustration with its own colors, so check that dark coats such as black Poodles still read on your dark stage. **Which Tailwind version is this written for?** Tailwind CSS 4, where you add one CSS import and the classes work. Every class used here also exists in version 3. --- Source: https://dogimage.dev/guides/sveltekit-dog-breed-gallery # A dog breed gallery in SvelteKit Build a nine-view dog breed gallery in SvelteKit with a server load function, a dynamic route, Svelte 5 runes, and a lightbox that swaps between angles. A SvelteKit gallery for one breed is a dynamic route with a `+page.server.js` load function and a `+page.svelte` that loops over the nine views. The load function calls `GET /api/v1/breeds/{breed}`, which needs no key, and hands the page the breed name, summary and view list. This guide uses SvelteKit 2 with Svelte 5. Create a project if you need one: ```bash npx sv create dog-gallery cd dog-gallery npm install ``` Choose the minimal template. ## The load function Create `src/routes/dogs/[breed]/+page.server.js`. It calls the API, turns a missing breed into a 404, and also fetches the catalog so the page knows the label of each view: ```js // src/routes/dogs/[breed]/+page.server.js import { error } from "@sveltejs/kit"; const API = "https://dogimage.dev"; export async function load({ params, fetch, setHeaders }) { const [breedRes, catalogRes] = await Promise.all([ fetch(`${API}/api/v1/breeds/${params.breed}`), fetch(`${API}/api/v1/breeds`), ]); if (breedRes.status === 404) error(404, "Breed not found"); if (!breedRes.ok || !catalogRes.ok) error(502, "The image catalog is not reachable"); const breed = await breedRes.json(); const catalog = await catalogRes.json(); setHeaders({ "cache-control": "public, max-age=3600" }); return { slug: breed.slug, name: breed.name, summary: breed.summary, views: catalog.views, }; } ``` In SvelteKit 2, `error()` throws on its own, so you do not write `throw`. The `fetch` passed into `load` is SvelteKit's version, which is safe on the server and in the browser. The page returns only the pieces it needs. The nine preview URLs are predictable, so the page builds them from the slug and a view id. ## The gallery page Create `src/routes/dogs/[breed]/+page.svelte`: ```svelte {data.name} from nine angles

{data.name}

{#if data.summary?.text}

{data.summary.text}

{/if} {#if active} {/if}

{data.name} image: Dog Image API. {#if data.summary?.url} Summary from Wikipedia. {/if}

``` Start the dev server with `npm run dev` and open `/dogs/beagle`. You get nine tiles, and a click opens the 1024 pixel preview of that view. ## How the pieces fit **Runes.** `$props()` receives the load result, `$state` holds the open view and `$derived` finds its label. On Svelte 4 you would write `export let data` and a reactive `$:` statement instead, and `on:click` instead of `onclick`. **Eager small, lazy large.** The grid loads 512 pixel files with `loading="lazy"`, and only the clicked view loads at 1024. Both sizes are WebP files with transparent backgrounds. The credit link sits on the same page as the images, which is what the free preview terms ask for. **Unknown slugs.** Visit `/dogs/not-a-breed` and the API answers with a 404 and a list of valid choices. The load function turns that into SvelteKit's error page. If you would rather prerender, export a `prerender` constant and a list of entries from the catalog, but then you must keep the data current by rebuilding. ## Color variants French Bulldog, Golden Retriever, German Shepherd, Dachshund, Doberman, Poodle and Great Dane come in several colors. The breed response lists each one under `variants`, with its own `views`. To let the visitor pick a color, return `breed.variants` from the load function and use the chosen variant slug in the URL instead of `default`. `default` always points to the breed's main set. ## Where the full-size files fit Do not use a key in `+page.svelte` or any file under `src/lib` that the browser can import. If you want 2048 pixel PNGs, a server-only module such as `$lib/server/` or a script run at build time can fetch them with `Authorization: Bearer` and the key in `DOG_IMAGE_API_KEY`. SvelteKit blocks client code from importing anything inside `$lib/server`, which gives you a useful guard. The [Next.js guide](/guides/nextjs-dog-breed-images) has a complete download script that works the same way here. Pricing for full-size plans is on the [pricing page](/pricing). Compare angles across every breed in the [front three-quarter gallery](/views/front-left). ## Questions **Why use a load function instead of fetching in the component?** A load function runs before the page renders, so the gallery arrives with its data and works with server rendering. It also lets SvelteKit return a proper 404 for an unknown breed. **Which views does a breed gallery show?** Nine: front, front-left, left, rear-left, rear, rear-right, right, front-right and head. They come from the same dog, on the same transparent canvas. **Do I need a key for the gallery?** No. The breed endpoint and the preview images are keyless. A key is only needed for full-size PNG files, which you should fetch from a server. --- Source: https://dogimage.dev/guides/angular-dog-breed-picker # A dog breed picker in Angular Build a dog breed picker with a standalone Angular component, HttpClient and signals: a filtered list with a front three-quarter preview and a credit link. In Angular, a breed picker takes one standalone component. Inject `HttpClient`, load `GET /api/v1/breeds` into a signal with `toSignal`, filter it with a `computed` value, and show the front three-quarter preview of the selected breed with its credit link. The code below targets Angular 19 or newer, where components are standalone and `ng new` sets up the project without modules. ## Set up ```bash npm install -g @angular/cli # skip if you already have it ng new breed-picker --style=css --ssr=false cd breed-picker ``` The global install is optional: `npx @angular/cli new breed-picker` works too. Answer the prompts as you like. This guide only touches three files. ## Provide HttpClient Open `src/app/app.config.ts` and add `provideHttpClient`: ```ts import { ApplicationConfig, provideZoneChangeDetection } from "@angular/core"; import { provideHttpClient } from "@angular/common/http"; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideHttpClient(), ], }; ``` If your generated file has a different set of providers, such as the zoneless one in the newest versions, keep them and just add `provideHttpClient()` to the array. ## The component Replace `src/app/app.ts` with the following. The template is inline, so there is only one file to read. ```ts import { Component, computed, inject, signal } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { toSignal } from "@angular/core/rxjs-interop"; import { catchError, map, of } from "rxjs"; const API = "https://dogimage.dev"; interface Breed { slug: string; name: string; } interface CatalogState { status: "loading" | "ready" | "error"; breeds: Breed[]; } @Component({ selector: "app-root", template: `

Pick a breed

@if (catalog().status === "error") {

The breed list could not be loaded. Reload to try again.

}
    @for (breed of matches(); track breed.slug) {
  • } @empty { @if (catalog().status === "ready") {
  • No breed matches.
  • } }
@if (selected(); as breed) {
{{ breed.name }} image: Dog Image API
} @else {

Select a breed to see it.

}
`, styles: ` .wrap { max-width: 720px; margin: 2rem auto; padding: 0 1rem; font-family: system-ui, sans-serif; } input { display: block; width: 100%; padding: 0.5rem; margin: 0.25rem 0 1rem; box-sizing: border-box; } .layout { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; } .list { list-style: none; margin: 0; padding: 0; max-height: 360px; overflow-y: auto; } button { width: 100%; text-align: left; padding: 0.4rem 0.6rem; border: 0; background: transparent; cursor: pointer; } button.active { background: #e8eefc; } figure { margin: 0; } img { background: #f4f1ea; border-radius: 8px; } `, }) export class App { private readonly http = inject(HttpClient); protected readonly query = signal(""); protected readonly selected = signal(null); protected readonly catalog = toSignal( this.http.get<{ breeds: Breed[] }>(`${API}/api/v1/breeds`).pipe( map((data): CatalogState => ({ status: "ready", breeds: data.breeds })), catchError(() => of({ status: "error", breeds: [] })), ), { initialValue: { status: "loading", breeds: [] } as CatalogState }, ); protected readonly matches = computed(() => { const q = this.query().trim().toLowerCase(); const { breeds } = this.catalog(); return q ? breeds.filter((b) => b.name.toLowerCase().includes(q)) : breeds; }); protected previewUrl(slug: string): string { return `${API}/api/v1/images/${slug}/default/front-left?size=512`; } protected creditUrl(slug: string): string { return `${API}/dogs/${slug}`; } } ``` `src/app/app.html` and `src/app/app.css` are no longer used. You can delete them, or leave them, as the component no longer references them. Run it: ```bash ng serve ``` Open `http://localhost:4200`, type "pug" and click the one result. ## How it works **One request, one signal.** `toSignal` subscribes to the HTTP observable when the component is created and unsubscribes when it is destroyed. The `initialValue` means the signal is never undefined, so the template needs no null checks. If the request fails, `catchError` turns the failure into a state the template can show. **Filtering is a `computed`.** `matches` depends on `query` and `catalog`. It recalculates only when one of them changes, and the `@for` block tracks by `slug` so Angular reuses the DOM nodes of breeds that stay in the list. **The image URL is built, not stored.** The catalog gives you slugs and names. The preview address is `/api/v1/images/{slug}/default/front-left?size=512`. It redirects to a transparent WebP, and the `` follows the redirect. The cross-origin rule for images does not apply to display, so no extra Angular configuration is needed. ## The credit link Previews are free for any use, including commercial, when a visible link credits Dog Image API on the same page or next to the image. The caption link covers that, in the form each breed page gives you. The [license](/license) has the details. ## Next steps - Change `front-left` to `head` for a small avatar, or add a second signal for the current view and a row of buttons. The nine ids are listed in the [docs](/docs). - Keep the key out of this app. Full-size PNG files need `Authorization: Bearer ` and belong on a server, as the [Node.js download guide](/guides/nodejs-download-dog-images) shows. - The same structure in other frameworks is in the [Vue selector guide](/guides/vue-dog-breed-selector) and the [React picker guide](/guides/react-dog-breed-picker). ## Questions **Why use toSignal instead of subscribing in the component?** toSignal turns the HTTP result into a signal and unsubscribes for you when the component is destroyed. The template then reads the value the same way as any other signal. **Does this work without a key?** Yes. The breed list and the 512 pixel previews are keyless, and the API allows cross-origin reads from browser apps. Never put a key in Angular code, because it ships to every visitor. **Which Angular version does the code need?** It targets Angular 19 or newer, where components are standalone by default. The template uses the built-in @if and @for blocks. --- Source: https://dogimage.dev/guides/vue-dog-breed-selector # A dog breed selector in Vue 3 Write a dog breed selector in Vue 3 with script setup: a reusable component that loads the catalog, filters it with a computed and shows a transparent preview. A Vue 3 breed selector is one single-file component: load the catalog once in `onMounted`, filter it with a `computed`, and bind the chosen slug to an ``. The component below also emits the selected breed, so a parent form can use it with `v-model`. ## The plan You need Vue 3.3 or later for `defineModel`. The component fetches `GET /api/v1/breeds`, which needs no key, and shows the preview for the selected breed. If you start from scratch: ```bash npm create vue@latest breed-selector cd breed-selector npm install ``` Pick the defaults. TypeScript is optional, and the sample uses plain JavaScript. ## The component Create `src/components/BreedSelector.vue`: ```vue ``` Use it from `src/App.vue`: ```vue ``` Run `npm run dev`. The select starts on Samoyed, and the preview loads in the front three-quarter right view. ## Why it is built this way **Filter and select together.** A native `` in the parent that sets a `ref` and passes it as `:view` is enough to let people rotate the dog. The catalog's `views` array carries the human labels if you want to build that list from data instead of typing it. ## Colors and variants Seven breeds ship in several colors, among them Poodle and Dachshund. Each entry in `variants` has a `slug`, a `name` and a `kind` of `color`, `pattern` or `coat`. To preview a variant, replace `default` in the URL with its slug, for example `/api/v1/images/poodle//front?size=512`, where `` comes from the catalog. For the breeds that have only one set, `default` is the only value you need. ## Licensing and limits The previews are free, including in commercial products, if a visible link credits Dog Image API on the same page. The figcaption link covers that. The images are AI-generated illustrations, not photos, so label them that way if your users might think otherwise. This component uses no key and should not get one. For full-size downloads, see the [Next.js guide](/guides/nextjs-dog-breed-images), which does them server side. The full list of endpoints is in the [docs](/docs), and every breed page, such as [Samoyed](/dogs/samoyed), shows the exact credit snippet. ## Questions **Can I use this selector in a Nuxt app?** The component is plain Vue 3, so it works in Nuxt. Because it fetches in the browser, wrap it in a client-only boundary if you want to avoid fetching during server rendering. **How do I show a color variant such as a blue French Bulldog?** Each breed in the catalog lists its variants with a slug. Use the variant slug in the image URL in place of the word default, for example french-bulldog/blue/front. **Is there a Vue package for Dog Image API?** No. There is no SDK. The API is plain HTTP and JSON, so fetch is all you need. --- Source: https://dogimage.dev/guides/dog-breeds-list-json # A list of dog breeds as JSON Get a dog breed list as JSON from one keyless endpoint: the fields, a jq name list, a JavaScript select menu, and the CC BY-SA 4.0 license behind the list. `GET https://dogimage.dev/api/v1/breeds` returns every breed in the catalog as JSON, with no key. Each breed has a slug, a display name, a crossbreed flag, a default variant and its color variants. On 2026-11-14 the list held 298 breeds. ## The request ```bash curl -s https://dogimage.dev/api/v1/breeds ``` The reply has two top-level keys. `views` lists the nine image views, and `breeds` lists the breeds. A shortened reply looks like this: ```json { "views": [ { "id": "front", "label": "Front" }, { "id": "front-left", "label": "Front three-quarter, left" } ], "breeds": [ { "slug": "french-bulldog", "name": "French Bulldog", "crossbreed": false, "default_variant": "fawn", "variants": [ { "slug": "blue", "name": "Blue", "kind": "color" }, { "slug": "fawn", "name": "Fawn", "kind": "color" } ] } ] } ``` ## The fields Each item in `breeds` has these fields: - `slug`: the identifier for URLs and API paths, such as `golden-retriever`. - `name`: the display name, such as `Golden Retriever`. - `crossbreed`: `true` or `false`. - `default_variant`: the slug of the breed's main set, such as `fawn`. - `variants`: the sets for that breed. Each has a `slug`, a `name` and a `kind`, which is `color`, `pattern` or `coat`. Most breeds have one set. Seven breeds have several colors: French Bulldog, Golden Retriever, German Shepherd, Dachshund, Doberman, Poodle and Great Dane. That is why the catalog has more sets than breeds. The `views` array is the same for every breed, so you read it once. Its ids are what you place at the end of an image path. ## A plain name list with jq Names only, one per line: ```bash curl -s https://dogimage.dev/api/v1/breeds | jq -r '.breeds[].name' ``` A CSV of slug and name: ```bash curl -s https://dogimage.dev/api/v1/breeds \ | jq -r '.breeds[] | [.slug, .name] | @csv' ``` Only breeds that come in more than one set: ```bash curl -s https://dogimage.dev/api/v1/breeds \ | jq -r '.breeds[] | select(.variants | length > 1) | .name' ``` Count the breeds: ```bash curl -s https://dogimage.dev/api/v1/breeds | jq '.breeds | length' ``` ## A select menu in JavaScript The endpoint allows cross-origin reads, so a browser page on any site can call it. This fills a ` ``` The value of each option is the slug. When a visitor picks a breed, you can fetch `/api/v1/breeds/{slug}` for its image URLs. If you show a preview, add the credit link that the [license](/license) requires. The same idea in Node 18 or later, to write a plain list of names to a file: ```js const res = await fetch("https://dogimage.dev/api/v1/breeds"); const { breeds } = await res.json(); const names = breeds.map((breed) => breed.name).sort(); console.log(names.join("\n")); ``` ## Cache it, and handle errors The list changes only when the catalog changes. Fetch it at build time or cache it on your server for a day, rather than on every page view. The API states no per-request rate limit, but a cached list is faster for your visitors anyway. In code, check `res.ok` before you parse the reply, and fall back to your cached copy if the request fails. Errors come back as JSON in the form `{"error": {"code": "...", "message": "..."}}`. ## Where the names come from Breed names and color names come from Wikidata, which is published under CC0. Breed summaries come from Wikipedia, under CC BY-SA 4.0. The catalog JSON, including this list, is shared under CC BY-SA 4.0. In practice, if you republish the list or something built from it, credit the source and share your version under the same license. The [license page](/license) is the reference, and it is marked as a draft not yet reviewed by a lawyer, so read it yourself before you rely on it for a commercial product. The images follow different terms than the JSON. ## Which breeds are in the list The list is not a kennel club registry, and it does not include every breed anyone has ever named. It is drawn from Wikidata and ranked by search interest. For the longer story of why breed counts differ, see [how many dog breeds there are](/guides/how-many-dog-breeds-are-there). To browse the result as pictures, open the [all breeds page](/dogs). ## Questions **Do I need an API key to get the breed list?** No. GET /api/v1/breeds is open and needs no key. Browser apps on other sites may call it directly. **How many breeds are in the list?** On 2026-11-14 the list held 298 breeds. The count changes as the catalog grows, so read the length of the breeds array instead of hardcoding a number. **Can I reuse the list in my own project?** Yes. The catalog JSON is shared under CC BY-SA 4.0, which asks for credit and for sharing adaptations under the same license. Read the license text for the exact terms. **Why is there a slug and a name?** The name is for people to read, such as French Bulldog. The slug, such as french-bulldog, is the stable identifier you put in URLs and API paths. --- Source: https://dogimage.dev/guides/nextjs-dog-breed-images # Add dog breed images to a Next.js app Add dog breed images to a Next.js App Router project: cached server fetches, a nine-view breed page, and a script that saves full-size PNGs to your own storage. In the App Router, fetch the breed catalog in a server component with a revalidate time, render nine preview images on a breed page, and download full-size PNGs with a separate Node script that sends your key. Previews can be hot-linked. Full-size files should end up in your own storage. This guide uses Next.js 15 or later with the `app` directory. Server components are async by default, so the data code stays short. ## Fetch the catalog on the server Put the base URL and two helpers in `lib/dogs.ts`. The `revalidate` option tells Next.js to cache the response and refresh it at most once a day: ```ts // lib/dogs.ts export const API = "https://dogimage.dev"; export type Variant = { slug: string; name: string; kind: string }; export type BreedSummary = { slug: string; name: string; crossbreed: boolean; default_variant: string; variants: Variant[]; }; export type View = { id: string; label: string }; export async function getCatalog(): Promise<{ views: View[]; breeds: BreedSummary[] }> { const res = await fetch(`${API}/api/v1/breeds`, { next: { revalidate: 86400 } }); if (!res.ok) throw new Error(`Catalog request failed: ${res.status}`); return res.json(); } export function previewUrl(breed: string, view: string, variant = "default", size = 512) { return `${API}/api/v1/images/${breed}/${variant}/${view}?size=${size}`; } ``` The catalog already includes the nine views with their labels, so you never hardcode them. ## A list page `app/dogs/page.tsx` lists every breed with a link to its detail page: ```tsx // app/dogs/page.tsx import Link from "next/link"; import { getCatalog } from "@/lib/dogs"; export default async function DogsPage() { const { breeds } = await getCatalog(); return (

Dog breeds

    {breeds.map((b) => (
  • {b.name}
  • ))}
); } ``` ## A breed page with nine views The detail route reads the slug, asks for the single breed, and maps over the views. `generateStaticParams` builds every breed page at build time, and unknown slugs return a 404 page. ```tsx // app/dogs/[breed]/page.tsx import { notFound } from "next/navigation"; import { API, getCatalog, previewUrl } from "@/lib/dogs"; export async function generateStaticParams() { const { breeds } = await getCatalog(); return breeds.map((b) => ({ breed: b.slug })); } export default async function BreedPage({ params, }: { params: Promise<{ breed: string }>; }) { const { breed } = await params; const res = await fetch(`${API}/api/v1/breeds/${breed}`, { next: { revalidate: 86400 }, }); if (res.status === 404) notFound(); if (!res.ok) throw new Error(`Breed request failed: ${res.status}`); const data = await res.json(); const { views } = await getCatalog(); return (

{data.name}

{data.summary?.text &&

{data.summary.text}

}
{views.map((v) => (
{/* eslint-disable-next-line @next/next/no-img-element */} {`${data.name},
{v.label}
))}

{data.name} image: Dog Image API {data.summary?.url && ( <> {" "} Summary from Wikipedia. )}

); } ``` On Next.js 14 and earlier, `params` is a plain object, so drop the `Promise` type and the `await`. A plain `` is used because the preview URL is a redirect to a WebP file. If you prefer `next/image`, add the host to `images.remotePatterns` in `next.config.ts` and test that the redirect resolves in your setup. Two licensing notes. The breed summary text comes from Wikipedia under CC BY-SA 4.0, so keep the attribution link. The credit link to Dog Image API is required for previews. ## Download full-size PNGs into your project Previews are 512 and 1024 pixels. Full-size files are 2048 x 2048 transparent PNGs, available with a paid plan. Each one is 1 to 7 MB, so you do not want to fetch them per visitor. Download them once instead, from a script that runs on your machine or in CI. Create `scripts/download-dogs.mjs`: ```js // scripts/download-dogs.mjs import { mkdir, writeFile, access } from "node:fs/promises"; import path from "node:path"; const API = "https://dogimage.dev"; const key = process.env.DOG_IMAGE_API_KEY; if (!key) throw new Error("Set DOG_IMAGE_API_KEY first"); const breeds = process.argv.slice(2); if (breeds.length === 0) breeds.push("pug", "beagle"); const catalog = await (await fetch(`${API}/api/v1/breeds`)).json(); const viewIds = catalog.views.map((v) => v.id); for (const breed of breeds) { const outDir = path.join("public", "dog-images", breed); await mkdir(outDir, { recursive: true }); for (const view of viewIds) { const file = path.join(outDir, `${view}.png`); try { await access(file); continue; // already stored, skip } catch {} let res; for (let attempt = 0; attempt < 3; attempt++) { res = await fetch(`${API}/api/v1/images/${breed}/default/${view}?size=full`, { headers: { Authorization: `Bearer ${key}` }, }); if (res.status !== 502 && res.status !== 503) break; // retry only these two } if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error(`${breed}/${view}: ${res.status} ${body.error?.code ?? ""}`); } await writeFile(file, Buffer.from(await res.arrayBuffer())); console.log(`saved ${file} (quota left: ${res.headers.get("X-Quota-Remaining")})`); } } ``` Run it with the key in the environment: ```bash DOG_IMAGE_API_KEY=your-key node scripts/download-dogs.mjs pug samoyed ``` The script skips files it already has, retries the two retryable errors (`502 file_unavailable` and `503 plan_check_failed`), and stops with the error code on anything else. A `402` means no active plan, and a `429` means the monthly quota is used up. Each breed costs nine images against a plan that counts distinct images per month, so check the [pricing page](/pricing) before you loop over the whole catalog. If you keep the key in `.env.local`, run the script with `node --env-file=.env.local scripts/download-dogs.mjs pug` (Node 20.6 or later), and never prefix the variable with `NEXT_PUBLIC_`. The file `public/dog-images/pug/front.png` is then served by your own app at `/dog-images/pug/front.png`. Read the [license](/license) before you ship. Full-size files can go into your own product without credit, but you cannot republish them as an image library or dataset. ## Questions **Should a Next.js page load the full-size PNGs from the API at request time?** No. Full-size files are meant to be downloaded once and stored on your side, and the API should not be hot-linked in pages. Use a script to download them, then serve your own copies. **Does fetching the same full-size image twice use my quota twice?** No. A plan counts distinct full-size images per calendar month in UTC, so fetching the same image again in the same month is free. A failed fetch with a 502 is not counted. **Where should the API key live in a Next.js project?** In a server-only environment variable named DOG_IMAGE_API_KEY, without the NEXT_PUBLIC_ prefix. Never import it into a client component. --- Source: https://dogimage.dev/guides/wordpress-dog-breed-images # Add dog breed images to a WordPress site Add dog breed images to WordPress with a Custom HTML block or a small shortcode in a child theme. Both show a preview with the credit link, no plugin needed. You can show a dog breed image in WordPress without a plugin. Paste an `` tag and the credit link into a Custom HTML block, or add a small shortcode to a child theme so any post can print the image with `[dog_breed slug="pug" view="front-left"]`. ## Pick the image URL Every breed has a preview at a predictable address. Use the breed slug, the word `default` for the main set, a view id, and a size of 512 or 1024: ``` https://dogimage.dev/api/v1/images/pug/default/front-left?size=512 ``` The address answers with a redirect to a transparent WebP file, and an `` tag follows it on its own. No key is needed for previews. The images are AI-generated illustrations, not photos, so say so if your readers might expect real dogs. Breed slugs are lowercase with hyphens, such as `golden-retriever`. You can browse them all on the [breeds page](/dogs). ## Way 1: a Custom HTML block This is the quickest route and fits a single post. In the block editor, add a block, search for "Custom HTML" and paste this: ```html
Pug, AI-generated illustration, front three-quarter view
Pug image: Dog Image API
``` Three points about this snippet: - The `figcaption` link is the credit. Free previews may be used for any purpose, commercial included, if a visible link to Dog Image API sits next to the image or on the same page. Each breed page gives you this exact snippet. - The background is transparent in the file. The `#f4f1ea` tint is only there so the dog stands out on a white page. Remove it if you do not want it. - `width` and `height` reserve space so the page does not jump while the image loads. The downside is repetition. Ten breeds means ten pasted blocks. That is where a shortcode helps. ## Way 2: a shortcode in a child theme Edit your child theme, not the parent theme, so an update does not erase the code. If you do not have a child theme yet, the WordPress Theme Handbook explains how to make one. Open the child theme's `functions.php` and add this at the bottom: ```php 'pug', 'view' => 'front-left', 'name' => '', ), $atts, 'dog_breed' ); $views = array( 'front', 'front-left', 'left', 'rear-left', 'rear', 'rear-right', 'right', 'front-right', 'head', ); $slug = sanitize_title( $atts['slug'] ); $view = in_array( $atts['view'], $views, true ) ? $atts['view'] : 'front-left'; $name = '' !== $atts['name'] ? $atts['name'] : ucwords( str_replace( '-', ' ', $slug ) ); if ( '' === $slug ) { return ''; } $src = 'https://dogimage.dev/api/v1/images/' . rawurlencode( $slug ) . '/default/' . $view . '?size=512'; $page = 'https://dogimage.dev/dogs/' . rawurlencode( $slug ); $alt = sprintf( '%s, AI-generated illustration, %s view', $name, $view ); return sprintf( '
%2$s
%4$s
', esc_url( $src ), esc_attr( $alt ), esc_url( $page ), esc_html( $name . ' image: Dog Image API' ) ); } add_shortcode( 'dog_breed', 'mysite_dog_breed_shortcode' ); ``` Now write this in any post, page or Shortcode block: ``` [dog_breed slug="pug" view="front-left"] [dog_breed slug="golden-retriever" view="left" name="Golden Retriever"] ``` ### Why the output is escaped A shortcode attribute comes from whoever edits the post, so treat it as input. The function escapes each value for the place it lands: - `esc_url()` for the `src` and `href`, which also blocks a `javascript:` address. - `esc_attr()` for the `alt` text inside an HTML attribute. - `esc_html()` for the visible link text. The `view` is checked against the nine known ids and falls back to `front-left`. The slug goes through `sanitize_title()`, which keeps only lowercase letters, digits and hyphens. Note the `%%` in the `max-width:100%%` style: inside `sprintf` a literal percent sign must be doubled. ### Choosing a view The nine ids are `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right`, `front-right` and `head`. Use `head` for a small avatar next to a comment or a byline, and `left` for a clean side profile. The [left side gallery](/views/left) shows every breed from that angle. ## Check it Preview a post that holds the shortcode. You should see the dog and the caption link. If the page shows the shortcode text instead, the code is not loaded: confirm the file is in the active child theme. If the image is missing, open the image URL in a browser tab and check the slug against the breed list. ## Limits to know Hot-linking previews is fine, and your visitors' browsers fetch them directly. Full-size PNG files are different. They need an API key, count against a monthly plan, and are meant to be downloaded once and stored on your side. Read the [license](/license) before using either kind in a product, and see the [docs](/docs) for the full list of endpoints. For a plain JavaScript version of the same idea that works on any site, see the [breed autocomplete guide](/guides/javascript-dog-breed-autocomplete). ## Questions **Is there a WordPress plugin for Dog Image API?** No. There is no plugin and no SDK. The previews are plain image URLs, so a Custom HTML block or a few lines of PHP in a child theme is all you need. **Will the shortcode slow my site down?** It prints one image tag and makes no request to the API from your server. The browser loads the preview file, which is served with a one-year immutable cache header. **Can I use the full-size PNG files in WordPress?** Yes, but download them once with your key, from a server or your own computer, and upload them to the Media Library. Do not put the key in a post, a block or theme code that visitors can read. --- Source: https://dogimage.dev/guides/ai-generated-dog-images-what-to-know # AI-generated dog breed images: what to know first Dog Image API images are AI-generated illustrations. Learn how sets are checked, where details can be wrong, when not to use them, and how to label them. The dog images in Dog Image API are AI-generated illustrations. They show no real dog, they are not photos, and they are not a breed standard. Use them as visual stand-ins in apps, charts and layouts. Do not use them to identify a dog, judge health or prove pedigree. ## What AI-generated means here A computer model produced each picture. No dog was photographed, and no artist drew each image by hand. The result is an illustration of one adult dog, standing, shown from the same nine views for every breed. That is why the catalog can be consistent. It is also why the images carry a limit: a model draws what a breed usually looks like, and it can get a detail wrong in a way a person would not. ## How sets are checked Each set passes an automatic check of its nine views before it is published. Sets are also reviewed by a person over time. Read that as quality control, not a guarantee. An automatic check can confirm that a set is complete and framed to the standard. It cannot confirm that every marking on a coat matches a particular breed. The review by a person happens over time, so a set may be live before anyone has looked at it closely. The [nine-view standard](/guides/the-nine-view-image-standard) describes what is checked for framing. ## Known limits Expect these kinds of mistakes, and look at an image before you rely on it: - **Tails.** Length, curl or carriage may not match the breed. - **Coats.** Texture, length or feathering may be off. - **Markings and colors.** Spots, masks, patches or shades may not follow what a breed club accepts. - **Small anatomy.** Paws, ears or eyes can look slightly wrong in any view. - **No individual.** Each image shows a generic dog, not a particular animal and not a show-quality example. The images are also not a breed standard. A breed standard is a written description kept by a kennel organization. This catalog does not replace one. For the breed's accepted description, go to the organization that keeps it. ## When not to use them Do not use the images where accuracy about a real dog matters: - **Identification.** Do not use them to tell a visitor which breed their dog is, or to label a found dog. - **Health.** Do not use them to show how a healthy dog, a sick dog or a body condition looks. - **Pedigree and sales.** Do not use them to represent a litter, a sire, a dam or a dog for sale. - **Legal evidence.** Do not submit them as proof of what an animal looks like, such as in a dispute or a claim. - **Anything that says "this is a real dog."** A product that implies a photo of a real animal should use real photos. They suit lighter jobs: a breed picker, a quiz, a game, a printed chart, a placeholder in a design, an illustration beside an article. If you are unsure, see how a [photo API compares](/guides/dog-api-alternatives-compared). ## Labeling good practice Tell readers what they are looking at. A short, plain label is enough: - Put "AI-generated illustration" in a caption, a footnote or the page text where the images appear. - Do not call the images photos, and do not describe them as real dogs. - Keep the label with the image when the image is shared or exported. - If you show a free preview, also keep the credit link the [license](/license) requires. A caption can be as simple as this: ```html
Illustration of a fawn Pug, front view
AI-generated illustration. Pug image: Dog Image API
``` ## A note on rules Some places have transparency rules for AI-made content. The European Union's AI Act, for example, has an [Article 50](https://artificialintelligenceact.eu/article/50/) on transparency obligations for certain AI systems and content. Whether and how a rule applies depends on who you are, what you publish and where. Check the rules that apply to you. This guide is not legal advice. The reference for what you may do with these images is the [license page](/license), which is marked as a draft not yet reviewed by a lawyer. ## Questions **Are these photos of real dogs?** No. They are AI-generated illustrations. They show no real dog, and no image is a photo. **Can I use them to identify a dog's breed?** No. They are illustrations of one typical-looking adult dog per set, not a breed standard, and details can be wrong. Use a vet, a breed club or a DNA test for identification. **Do I have to say the images are AI-generated?** Saying so is good practice, and some places have transparency rules for AI-made content. Check the rules that apply to you. This guide is not legal advice. **Does a person review the images?** Each set passes an automatic check of its nine views before it is published, and sets are reviewed by a person over time. That reduces errors but does not remove them. --- Source: https://dogimage.dev/guides/swiftui-pet-profile-breed-images # Breed images for a pet profile in SwiftUI Build a SwiftUI pet profile with a dog breed image: decode the catalog with Codable, add a breed picker, load the preview with AsyncImage and show the credit. Fetch `GET /api/v1/breeds` once, decode it with `Codable`, let the user pick a breed, then load the head portrait with `AsyncImage` from the `preview_512` URL. No key is involved. Show the credit link under the image, because previews are free with a visible credit. The images are AI-generated illustrations of one adult dog, drawn to one standard on a transparent background. That makes them a good fit for an avatar circle on a pet profile, where a real photo is not available yet. ## What the app does and does not hold The app never holds an API key. Previews are keyless, and the catalog calls are keyless too. Full-size PNG files need a key and are meant for a server. If your app needs a 2048 pixel file, your backend downloads it once, stores it and serves it to the app. [Cache and self-host dog breed images](/guides/cache-and-self-host-dog-images) shows that job. ## Platform note WebP decoding in `AsyncImage` needs iOS 14 or later. `AsyncImage` is a SwiftUI view that arrived in iOS 15, so the practical floor for this guide is iOS 15. On older systems, show a placeholder. ## Decode the catalog The catalog lists every breed with its variants and the nine view ids. The breed detail call returns the preview URLs. Both responses use snake case keys, so the models name them with `CodingKeys`. ```swift import SwiftUI struct Catalog: Decodable { let breeds: [BreedSummary] } struct BreedSummary: Decodable, Identifiable, Hashable { let slug: String let name: String let defaultVariant: String var id: String { slug } enum CodingKeys: String, CodingKey { case slug, name case defaultVariant = "default_variant" } } struct BreedDetail: Decodable { let variants: [Variant] struct Variant: Decodable { let slug: String let views: [String: ViewImages] } struct ViewImages: Decodable { let preview512: URL let preview1024: URL enum CodingKeys: String, CodingKey { case preview512 = "preview_512" case preview1024 = "preview_1024" } } } ``` Unknown keys such as `crossbreed`, `summary` and `full` are ignored by `Decodable`, which is what you want here. The `full` field points at the keyed endpoint, and the app never calls it. ## A small loader One `ObservableObject` owns the two requests. The breed list comes from the response, so the picker grows with the catalog and no breed is hardcoded. ```swift @MainActor final class BreedStore: ObservableObject { @Published var breeds: [BreedSummary] = [] @Published var avatarURL: URL? @Published var errorMessage: String? private let base = URL(string: "https://dogimage.dev/api/v1")! func loadCatalog() async { do { let (data, _) = try await URLSession.shared.data(from: base.appendingPathComponent("breeds")) breeds = try JSONDecoder().decode(Catalog.self, from: data).breeds } catch { errorMessage = "Could not load breeds." } } func loadAvatar(for breed: BreedSummary) async { do { let url = base.appendingPathComponent("breeds/\(breed.slug)") let (data, _) = try await URLSession.shared.data(from: url) let detail = try JSONDecoder().decode(BreedDetail.self, from: data) let variant = detail.variants.first { $0.slug == breed.defaultVariant } ?? detail.variants.first avatarURL = variant?.views["head"]?.preview512 } catch { avatarURL = nil errorMessage = "Could not load that breed." } } } ``` The breed's `default_variant` is the main color set, so it is a sensible choice for a profile. A breed such as the German Shepherd has several variants, and your screen can offer them in a second picker later. ## The profile screen The screen shows a picker, the avatar and the credit link. `AsyncImage` takes a phase closure, so you handle loading and failure in one place. ```swift struct PetProfileView: View { @StateObject private var store = BreedStore() @State private var selected: BreedSummary? var body: some View { Form { Section("Breed") { Picker("Breed", selection: $selected) { Text("Choose a breed").tag(BreedSummary?.none) ForEach(store.breeds) { breed in Text(breed.name).tag(BreedSummary?.some(breed)) } } } if let breed = selected { Section { VStack(spacing: 8) { AsyncImage(url: store.avatarURL) { phase in switch phase { case .success(let image): image.resizable().scaledToFit() case .failure: Image(systemName: "pawprint").font(.largeTitle) default: ProgressView() } } .frame(width: 160, height: 160) .background(Color(.secondarySystemBackground), in: Circle()) Link( "\(breed.name) image: Dog Image API", destination: URL(string: "https://dogimage.dev/dogs/\(breed.slug)")! ) .font(.footnote) } .frame(maxWidth: .infinity) } } } .task { await store.loadCatalog() } .task(id: selected) { if let breed = selected { await store.loadAvatar(for: breed) } } } } ``` Two details matter. The background is transparent, so the circle behind the image is what gives the avatar its shape. And the credit link uses the form each [breed page](/dogs/pug) gives: the breed name, "image: Dog Image API", linking to that breed's page on the site. ## Choosing views The `head` view is a portrait and suits an avatar. For a profile header, use a side view such as `left` at the 1024 pixel size, and keep the views consistent across your app so every pet looks like it belongs to the same set. The [left side gallery](/views/left) shows how every breed looks from that angle. ## Before you ship - Store the breed `slug` on the pet record, not the image URL. Resolve the URL when the screen opens. - Keep the credit link visible next to the image, not hidden in a settings screen. - Say "AI-generated" in your own copy if users might read the image as their own pet. - Read the [license](/license) once. Previews allow any use with the credit link. ## Questions **Do I need an API key in my SwiftUI app?** No. The breed list, the breed detail and the 512 and 1024 pixel previews are keyless. Full-size PNG files need a key, so a server of yours should fetch them. Never put the key in the app. **Which iOS version do I need to show the WebP previews?** WebP decoding needs iOS 14 or later. AsyncImage itself needs iOS 15, so a SwiftUI app that uses it should target iOS 15 or later. **Which preview size should a profile screen use?** Use the 512 pixel preview for an avatar or a list row and the 1024 pixel preview for a full-width header. Both are WebP files with a transparent background. **Are the dogs in the images real?** No. The images are AI-generated illustrations. They show no real dog and are not a breed standard. --- Source: https://dogimage.dev/guides/dog-breed-placeholders-for-shelter-websites # Breed placeholder images for shelter and adoption sites Use labeled dog breed illustrations as a placeholder when an adoption listing has no photo yet, handle mixed breeds honestly, and wire it in with a few lines. When a shelter listing has no photo yet, show a breed illustration with a clear label that says it is not the animal. Keep it visually different from a real photo, and use the catalog's `crossbreed` field to treat mixes honestly. Replace it the day a real photo arrives. ## What a placeholder is for An intake record can exist days before anyone takes a good photo. During that window the listing page still needs a card in the grid. Without a picture, listings with no photo get skipped. With a wrong picture, you mislead the person who clicks. A labeled placeholder avoids both. The line you must not cross: the placeholder is not the dog. The catalog images are AI-generated illustrations of one adult dog per set. They are not photos, they show no real animal, and they are not a breed standard. A mixed-breed dog that "looks like" a Beagle on the intake sheet will not look like the illustration, and an adopter should never find that out at the meeting. ## Make the difference obvious Three small choices keep the placeholder honest: - **A caption on the image,** inside the card and not hidden in a tooltip: "Breed illustration. Photo coming soon." - **A different frame.** Give it a dashed outline or a flat tinted background, so a grid of real photos never blends with it. The images have a transparent background, so a flat color shows through. - **No name tag on the dog.** Do not overlay the animal's name on the illustration. The name belongs to the card text. Use the `front-left` view for a card, because it shows the whole dog from a natural angle. Use `head` for a compact list or for an email. ## Mixed breeds and crossbreeds Many shelter dogs are mixes, and the intake form often records a guess. Each breed in the catalog has a `crossbreed` boolean. Entries such as the cockapoo, the goldendoodle and the puggle are marked as crossbreeds. A few rules help: - If the guess matches a crossbreed entry, use it and add "Mix" to the label. - If the guess is a purebred, show that breed's illustration and write "Possible breed mix" in the label. A guess is not a fact. - If the mix does not match any entry, or the staff do not know, do not pick the nearest. Show a plain "No photo yet" card. Do not put breed guesses in a headline that reads like a fact. "Beagle mix" with an illustration labeled as a breed illustration is fine. "Beagle" with the same picture is not. ## How to wire it The listing record keeps a photo URL and an optional breed slug. The page picks one: ```js const BASE = "https://dogimage.dev"; function listingImage(listing) { if (listing.photoUrl) { return { src: listing.photoUrl, alt: listing.name, isPlaceholder: false }; } if (listing.breedSlug) { return { src: `${BASE}/api/v1/images/${listing.breedSlug}/default/front-left?size=512`, alt: "Breed illustration", isPlaceholder: true, }; } return { src: null, alt: "No photo yet", isPlaceholder: true }; } ``` The markup for a placeholder card includes the label and the credit link, which the free preview requires: ```html
Breed illustration
Breed illustration, not this dog. Photo coming soon. Beagle image: Dog Image API
``` To populate a breed picker for staff, read `GET /api/v1/breeds`, keep the `slug` and show `name`, with the `crossbreed` flag as a "Mix" tag. The endpoint needs no key. See the [docs](/docs) for the full reference. ## Which plan fits Previews cover the website. A free plan with a visible credit link on the page is enough for most shelters. A paid plan matters only if you want full-size files, for example for a printed "photo coming soon" flyer. One image is one view of one set at full size. If your shelter sees 25 breeds regularly, one view each is 25 images, well inside Starter's 200 a month. All 298 default sets in one view is 298 images: two months on Starter, one month on Pro. Check [pricing](/pricing) for the current plans. ## License points for adoption sites Preview files may be used with a visible link crediting Dog Image API next to the image or on the same page. Full-size files go in your own site, documents and print with no credit needed. You may not resell the images, republish them as a collection, or use them to build a competing image service. If other shelters ask for your files, point them to the [dogs catalog](/dogs) instead of passing them on. Do not use the images in a way that implies a specific animal, in fundraising copy or elsewhere. [The license](/license) is a draft not yet reviewed by a lawyer, so read it before you rely on it. ## Questions **Can the illustration be shown as the animal on a listing?** No. It is an AI-generated illustration of a breed, not of the animal. Always label it as a breed illustration so no adopter thinks it is the dog they would meet. **How do I handle a mixed breed dog?** The catalog flags crossbreeds with the crossbreed field, such as the cockapoo and the goldendoodle. If the dog's mix is not listed, show a neutral no-photo card instead of forcing a match. **Do shelters need to pay?** Not for previews. They are free for commercial use with a visible credit link on the same page. Paid plans cover full-size 2048 pixel PNG files, for example for printed flyers. **Should I keep the placeholder after a real photo arrives?** No. Replace it with the real photo as soon as one exists. The placeholder is only for the time before then. --- Source: https://dogimage.dev/guides/react-dog-breed-picker # Build a dog breed picker in React Build a searchable dog breed picker in React that loads the breed list in the browser and shows each breed's preview image with the required credit link. A breed picker needs two things: the list of breeds and one image per breed. Dog Image API gives you both without a key. Fetch the catalog once with `fetch`, filter it as the user types, and point an `` at the preview URL for the selected breed. ## What you will build A text box that filters the catalog, a list of matching breeds, and a 512 pixel preview of the selected breed seen from the front three-quarter left. The preview is a transparent WebP, so it sits on any background color. The code uses Vite with React 18 or 19 and plain `fetch`. No other package is needed. Create the project if you do not have one: ```bash npm create vite@latest breed-picker -- --template react cd breed-picker npm install ``` ## How the data is shaped `GET /api/v1/breeds` returns `views` and `breeds`. Each breed has a `slug`, a `name`, a `default_variant` and a list of `variants` (colors, patterns or coats). You only need `slug` and `name` for a picker. For the image, build the URL from the slug. The word `default` stands for the breed's main set, and `front-left` is the view id: ``` https://dogimage.dev/api/v1/images/{slug}/default/front-left?size=512 ``` That URL answers with a redirect to the WebP file, which an `` follows without extra code. Pass `size=1024` if you want the larger preview. ## The component Replace `src/App.jsx` with this file: ```jsx import { useEffect, useMemo, useState } from "react"; const API = "https://dogimage.dev"; function previewUrl(slug, view = "front-left", size = 512) { return `${API}/api/v1/images/${slug}/default/${view}?size=${size}`; } export default function App() { const [breeds, setBreeds] = useState([]); const [query, setQuery] = useState(""); const [selected, setSelected] = useState(null); const [error, setError] = useState(""); useEffect(() => { const controller = new AbortController(); fetch(`${API}/api/v1/breeds`, { signal: controller.signal }) .then((res) => { if (!res.ok) throw new Error(`Breed list failed with status ${res.status}`); return res.json(); }) .then((data) => setBreeds(data.breeds)) .catch((err) => { if (err.name !== "AbortError") setError(err.message); }); return () => controller.abort(); }, []); const matches = useMemo(() => { const q = query.trim().toLowerCase(); return q ? breeds.filter((b) => b.name.toLowerCase().includes(q)) : breeds; }, [breeds, query]); return (

Pick a breed

{error &&

{error}

} setQuery(e.target.value)} placeholder="Try pug or retriever" style={{ display: "block", width: "100%", padding: "0.5rem", margin: "0.25rem 0 1rem" }} />
    {matches.map((b) => (
  • ))} {breeds.length > 0 && matches.length === 0 &&
  • No breed matches.
  • }
{selected ? (
{`${selected.name},
{selected.name} image: Dog Image API
) : (

Select a breed to see it.

)}
); } ``` Run `npm run dev` and open the local address. Type "pug" and the list narrows to one entry. Click it and the preview loads. ## Details worth knowing **The credit link is part of the free tier.** Previews are free for any use, commercial included, as long as a visible link credits Dog Image API next to the image or on the same page. The caption link above uses the form each breed page gives you. See the [license](/license) for the exact terms. **The catalog is fetched once.** The list is the same for every visitor, so one request on mount is enough. Do not hardcode the number of breeds anywhere in your code, because the live count changes as sets are added. **Cleanup matters.** The `AbortController` stops the request if the component unmounts, which avoids a state update on a component that is gone. React 18 and later run effects twice in development strict mode, so this also keeps the dev console quiet. **The background is transparent.** The preview has no backdrop, so the `#f4f1ea` fill in the sample is yours. Change it or remove it. ## Try another view The nine view ids are `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right`, `front-right` and `head`. Swap the second argument of `previewUrl` to change the angle. A `head` portrait works well in a small avatar slot, while `left` is the usual choice for a side profile card. The [Left side gallery](/views/left) shows every breed from that angle. ## Before you ship Keep the key out of this app. The full-size PNG endpoint needs `Authorization: Bearer ` and is meant for a server or a build step, so a browser picker should stay on previews. If you also want print-quality files, the [Next.js guide](/guides/nextjs-dog-breed-images) shows how to download them on the server into your own storage. Pricing for full-size plans is on the [pricing page](/pricing), and the endpoints are listed in the [docs](/docs). Say in your own interface that the dogs are AI-generated illustrations if your users might assume they are photos. The alt text in the sample does that for screen readers. ## Questions **Do I need an API key for a React breed picker?** No. The breed list and the 512 pixel previews need no key, and the API allows cross-origin reads from browser apps. Keys are only for full-size PNG downloads, which belong on a server. **Can I hot-link the preview images in my React app?** Yes. Previews may be hot-linked, and they are served with a one-year immutable cache header. Show the credit link to Dog Image API next to the image or on the same page. **Are the dog images photos?** No. They are AI-generated illustrations of a dog on a transparent background. They show no real dog and are not a breed standard. --- Source: https://dogimage.dev/guides/build-a-dog-breed-quiz # Build a dog breed quiz with real breed images Build a 10-question dog breed quiz in one HTML file. It picks random breeds in the browser, shows an illustration, offers four choices and keeps score. This quiz is one HTML file with no build step. It loads the breed list once, picks ten random breeds in the browser, shows each head illustration with four name choices, and keeps score. Save the code below as `quiz.html`, open it in a browser, and it works. ## What it does The API has no random endpoint and no search, so the browser does the choosing. Here is the plan: 1. Fetch `GET /api/v1/breeds`. It needs no key and cross-origin reads from other sites are allowed. 2. Drop the entries where `crossbreed` is true. A mix has no single look to recognize. 3. Shuffle what is left and take the first ten as the answers. 4. For each answer, pick three other breeds as wrong choices and shuffle the four names. 5. Show the answer's head illustration from `GET /api/v1/images/{breed}/{variant}/head?size=512`. This URL redirects to a free WebP preview, and an `` tag follows the redirect. The images are AI-generated illustrations of an adult dog, one set per breed. For breeds with several colors the quiz uses the `default_variant` from the list, so it does not need a second request. ## The complete file ```html Dog breed quiz

Dog breed quiz

Loading breeds...

Images are AI-generated illustrations, not photos. Dog images: Dog Image API

``` ## How the pieces fit **Choices.** `buildQuestions` shuffles the whole list once, so the ten answers never repeat. For each answer it shuffles the other breeds and takes three. The check on `slug` guarantees four different names. **The image.** The quiz sets `img.src` to the image endpoint with `size=512`. That endpoint answers with a `302` to the preview file, which is cached for a year. The `alt` text describes the task and does not give away the answer. **Credit.** Using previews requires a visible link that credits Dog Image API next to the image or on the same page. The footer link covers the whole page. After each answer the quiz also shows the breed-specific credit link in the form each breed page gives. Putting that link before the answer would reveal the name, which is why it appears afterward. **Errors.** If the list fails to load, the page says so and logs the error to the console. It does not retry in a loop. ## Ideas to extend it - Change `VIEW` to `front-left` for a full-body round, or choose a view at random per question from the nine ids in the `views` array of the breed list. - Keep the best score in `localStorage`. - Link each answer to its [breed page](/dogs/pug) or to the [side-view gallery](/views/left) for further browsing. - Read the `variants` list to add a "which color is this?" round for the seven breeds with several colors. The quiz never uses a key and never touches the full-size endpoint. For production use, put the file on any static host. The [docs](/docs) list every endpoint if you want to go beyond previews. ## Questions **Is there a random image endpoint?** No. The API has no random endpoint. This quiz loads GET /api/v1/breeds once and picks breeds in the browser. **Do I need an API key for the quiz?** No. The breed list and the 512 pixel previews need no key, and browsers on other sites may read them. A key is only for full-size downloads, and those belong on a server. **Are these real dog photos?** No. They are AI-generated illustrations. The quiz says so on screen, and you should keep that line. **Why does the quiz skip crossbreeds?** A mix such as the cockapoo has no single recognizable look, so it makes a poor quiz answer. The crossbreed field lets the quiz filter them out in one line. --- Source: https://dogimage.dev/guides/printable-dog-breed-chart # Build a printable dog breed chart One HTML file that fetches the breed list, lays out side views with names, adds print CSS for page size and breaks, and filters to your own breed list. A printable breed chart is one web page that shows a grid of dogs with their names and prints cleanly. The page below fetches the breed list, shows the left side view of each breed, and uses print CSS to set the paper size and keep each card from splitting across pages. ## What the page does 1. Fetches `GET /api/v1/breeds`, which needs no key and lists every breed with its slug, name and color variants. 2. Optionally filters that list to the breeds you choose. 3. Shows each breed as a card: image and name. 4. Uses `@page` and `break-inside` so it prints well. The images are AI-generated illustrations with a transparent background. The view id `left` is the left side view. Using one view for every card keeps the chart consistent. ## The complete file Save this as `chart.html` and open it in a browser. It loads the preview files, so you need to be online. ```html Dog breed chart

Dog breed chart

Left side view of each breed. Illustrations are AI-generated.

Images: Dog Image API

``` ## The print rules - `@page { size: A4 portrait; margin: 12mm; }` sets the paper size and margins. Use `letter portrait` for US Letter, or `A4 landscape` for a wider sheet. - `break-inside: avoid` on `.card` stops a card from splitting between two pages. - `print-color-adjust: exact` asks the browser to print the card background. Browsers hide backgrounds by default to save ink, and users can still turn that setting off in the print dialog. - The controls are hidden in print, and the credit link stays. For a chart with forced page boundaries, wrap groups of breeds in sections and add `break-after: page` to each section. ## Choose your breeds The `MY_LIST` array holds breed slugs. Add or remove slugs. Slugs are in the list that the endpoint returns, and the [breed list guide](/guides/dog-breeds-list-json) shows its shape. The "All breeds" option loads every breed, which means one request per breed for its image URL. That is a lot of requests for a screen, and it makes a long printout, so use it only when you mean to. ## What the chart does not show Each dog fills its own square frame, at most 80% of it. A Chihuahua and a Great Dane both fill their frames, so the chart does not show relative size between breeds. Add a text line for size if your reader needs it, and take that number from a source you trust. ## Credit on paper The preview license needs a visible link that credits Dog Image API on the same page. On a printed sheet the link is still printed as text, so the credit is there. Read the [license](/license) if you plan to distribute the sheets. For large prints, where 512 pixels will look soft, the paid 2048 pixel PNGs on [pricing](/pricing) are the right source. ## Questions **Does the chart show how big each breed is?** No. Each dog fills its own square frame, at most 80% of it, so a Great Dane and a Pug look about the same size. Do not use the chart to compare sizes. **How do I save the chart as a PDF?** Open the page, choose Print in your browser, and pick Save as PDF as the destination. The print rules in the page set the page size and breaks. **Do I need an API key?** No. The breed list and the preview files need no key. The credit link has to stay visible on the page, and on the printed sheet. **Can I print a sheet for a breeds list of my own?** Yes. Put the breed slugs you want in the list at the top of the script. Slugs come from the breed list endpoint. --- Source: https://dogimage.dev/guides/build-dog-breed-pages-for-a-pet-website # Build dog breed pages for a pet website Generate one page per dog breed from the breeds endpoint with a build script, then add your own text, consistent images, the Wikipedia credit and image credit. A useful breed page has text only you can write, a consistent set of images, and clear labels for anything you did not make. Generate the page skeleton from `GET /api/v1/breeds` in a build step, then add your own content to each page. The skeleton alone is not worth visiting. ## What a breed page needs Start with the reader. Someone lands on a page about a Beagle or a Samoyed. They want to know whether it fits their life, or whether your product fits their dog. A page of a picture and a paragraph copied from an encyclopedia answers neither. Put your own material first: - **Your own text.** What you know from your work: how the breed behaves in your classes, what sizes you stock, which questions your customers ask. One or two honest paragraphs beat six generic ones. - **Consistent images.** Use the same view in the same place on every page. A head portrait at the top and a three-quarter view in the body is a layout you set once in the template. - **Clear labels.** Say the images are AI-generated illustrations of an adult dog, not photos of a real animal. Say where the summary comes from. A reader who knows what they are looking at trusts the page more. - **Navigation.** Link to related breeds and to your own pages: products, articles, contact. ## Step 1: read the list The list needs no key. Each breed has a `slug`, a `name`, a `crossbreed` flag, a `default_variant` and its `variants`. Browsers on other sites may read it, but a build script on your computer or CI server is simpler. ## Step 2: fetch each breed's detail `GET /api/v1/breeds/{breed}` returns the `summary` (text, url, title, and the whole field may be null) and, for each variant, the image URLs for every view. It needs no key. ## Step 3: write the template The script below uses Node 18 or newer, which has `fetch` built in. It writes one HTML file per breed into `dist/breeds/`. It reads the live list, so new breeds appear on the next build. Put your own paragraphs in a `content/.txt` file, and the page uses it when it exists. ```js // build-breed-pages.mjs import { mkdir, readFile, writeFile } from "node:fs/promises"; const BASE = "https://dogimage.dev"; const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]); async function getJson(path) { const res = await fetch(`${BASE}${path}`); if (!res.ok) throw new Error(`${path} failed: ${res.status}`); return res.json(); } async function ownText(slug) { try { return await readFile(`content/${slug}.txt`, "utf8"); } catch (err) { if (err.code === "ENOENT") return null; throw err; } } const { breeds } = await getJson("/api/v1/breeds"); await mkdir("dist/breeds", { recursive: true }); for (const b of breeds) { const detail = await getJson(`/api/v1/breeds/${b.slug}`); const variant = detail.variants.find((v) => v.slug === detail.default_variant); const head = variant.views["head"].preview_512; const side = variant.views["front-left"].preview_1024; const mine = await ownText(b.slug); const s = detail.summary; const html = ` ${esc(b.name)} | My Pet Site

${esc(b.name)}

Illustration of a ${esc(b.name)} head ${mine ? mine.split("\n\n").map((p) => `

${esc(p)}

`).join("\n") : ""} Illustration of a ${esc(b.name)}

Illustrations are AI-generated and show no real dog. ${esc(b.name)} image: Dog Image API

${s ? `
${esc(s.text)}

Summary from ${esc(s.title)} on Wikipedia, licensed CC BY-SA 4.0.

` : ""} `; await writeFile(`dist/breeds/${b.slug}.html`, html); } console.log(`Wrote ${breeds.length} pages`); ``` Run it with `node build-breed-pages.mjs`. The script fetches one breed at a time, which keeps the load small for a few hundred pages. No request limit is stated, so do not parallelize more than you need. ## Use the Wikipedia summary correctly The breed names and color names come from Wikidata, which is CC0. The summaries come from Wikipedia, and Wikipedia text is CC BY-SA 4.0. That has two consequences for your page: 1. **Credit and link.** Name Wikipedia and link the article. The `summary.url` and `summary.title` fields give you both, and the script prints them under the quote. 2. **Share alike.** If you change the text or build on it, the result must carry the same license. The simplest path is to quote the summary unchanged, mark it as a quotation and keep your own text separate and under your own terms. The catalog JSON is also shared under CC BY-SA 4.0. If you republish the breed list itself, the same rules apply. Because `summary` can be null, the template above skips the block when there is none. Do not fill the gap with text from elsewhere unless you know its license. ## Credit the images The script uses preview files, which are free for any use with a visible link crediting Dog Image API next to the image or on the same page. The breed page of each breed offers the exact snippet, and the script prints it in that form: ```html Pug image: Dog Image API ``` Previews may be hot-linked, as the script does. If you want pages with no credit link, download full-size 2048 pixel PNG files in a build step, using a key from `DOG_IMAGE_API_KEY` on your build machine only, and host them yourself. Plans and limits are on the [pricing page](/pricing), and the [license](/license) sets the terms. You may not republish the images as a collection, so build pages around them, not a download library. ## Why your own content is the point A generated page is the same as every other generated page. Search engines and readers have no reason to prefer a skeleton of an image and a quoted summary. The part that makes a page worth a visit is what you add: your tested advice, your products, your region, your photos of real dogs, labeled as photos. A practical order of work: ship the skeleton for every breed, then write real text for the ten breeds your visitors ask about most, and add more over time. Leave a breed's text empty rather than filling it with filler. For a tour of the endpoints, see the [docs](/docs). To see how the images look on a finished page, open the [Pug page](/dogs/pug). ## Questions **Can I publish the Wikipedia summary as the page text?** Only with credit and a link to the article, and under the same license, CC BY-SA 4.0. It is also thin content by itself, so write your own text first and use the summary as a clearly marked quotation. **Does every breed have a summary?** No. The summary field may be null, so your build must handle a missing summary without breaking the page. **Do I need an API key for the pages?** Not for the breed list, the breed details or the previews. A key is only for full-size PNG files, which you download in a build step and host yourself. **How many pages will this create?** One per breed in the list. The catalog had 298 breeds on 2026-11-14, but read the live count from the response instead of hardcoding it. --- Source: https://dogimage.dev/guides/cache-and-self-host-dog-images # Cache and self-host dog breed images Download full-size dog breed PNGs once with a small Node or Python sync job, store them by breed, variant and view, and serve them from your own storage or CDN. Full-size files are PNG, 2048 pixels square, and each one counts once against your monthly plan. Download them from a server, save each under a key made of breed, variant and view, and serve that copy from your own storage or CDN. Pages then never call the API. The images are AI-generated illustrations, and each file is 1 to 7 MB. That size is one more reason to keep a copy instead of asking the API every time. ## Why download once Three facts decide the design. - The full-size endpoint needs your API key, and the key must never reach a browser or an app. - A plan counts distinct full-size images per calendar month, in UTC. The same image fetched again in the same month is free, but a month later it is a fresh count if you ask again. - The API is built for downloading, not for serving your visitors. Do not hot-link it in pages. So you fetch on a schedule or on demand, from one place, and keep what you get. Files fetched while a plan was active stay usable in what you built with them, as the [license](/license) says. ## The storage key Mirror the API path. One file is one view of one set. ```text dog/{breed}/{variant}/{view}.png dog/pug/fawn/front.png dog/golden-retriever/dark/head.png ``` The `variant` is the color slug, or the word `default` for a breed's main set. Using the same words as the API means a missing file can be named from the URL alone, and your fetch code needs no lookup table. The [breed pages](/dogs/pug) show which variants exist. ## What to download Decide the list first. A set is nine views, so a set costs nine images. On 2026-11-14 the catalog had 329 sets, which is 2,961 images. A Starter plan of 200 images covers 22 full sets, and a Pro plan of 700 covers 77. The All images plan covers every full-size image. See [pricing](/pricing) for the current plans. Most products need a short list: the breeds that appear in the product, and often one or two views each. Put that list in a file and sync only it. The live catalog is at `GET /api/v1/breeds`, and its `views` array holds the nine view ids. ## A sync job in Node This script needs Node 18 or later and no packages. It skips files that exist, retries the two retryable errors and stops when the quota is used up. ```js // sync.mjs (run on a server or in CI, never in a browser) import { mkdir, writeFile, access } from 'node:fs/promises'; import path from 'node:path'; const API = 'https://dogimage.dev/api/v1'; const KEY = process.env.DOG_IMAGE_API_KEY; const OUT = process.env.IMAGE_DIR ?? './dog'; // breed, variant, views you want. Use 'default' for the main set. const WANTED = [ { breed: 'pug', variant: 'default', views: ['front', 'left', 'head'] }, { breed: 'beagle', variant: 'default', views: ['front', 'left', 'head'] }, ]; const exists = (p) => access(p).then(() => true, () => false); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function download(breed, variant, view) { const file = path.join(OUT, breed, variant, `${view}.png`); if (await exists(file)) return 'skipped'; const url = `${API}/images/${breed}/${variant}/${view}?size=full`; for (let attempt = 1; attempt <= 3; attempt++) { const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }); if (res.status === 502 || res.status === 503) { await sleep(1000 * attempt); // not counted, retry continue; } if (res.status === 429) throw new Error('quota_used_up'); if (!res.ok) throw new Error(`${res.status} for ${url}: ${await res.text()}`); await mkdir(path.dirname(file), { recursive: true }); await writeFile(file, Buffer.from(await res.arrayBuffer())); console.log(`saved ${file} (remaining: ${res.headers.get('X-Quota-Remaining')})`); return 'saved'; } throw new Error(`gave up on ${url}`); } for (const { breed, variant, views } of WANTED) { for (const view of views) { await download(breed, variant, view); } } ``` Run it with `DOG_IMAGE_API_KEY=... node sync.mjs`. The `X-Quota-Remaining` header says how many images are left, and it reads `unlimited` on the All images plan. ## The same job in Python ```python # sync.py (needs: pip install requests) import os import time from pathlib import Path import requests API = "https://dogimage.dev/api/v1" KEY = os.environ["DOG_IMAGE_API_KEY"] OUT = Path(os.environ.get("IMAGE_DIR", "./dog")) WANTED = [ ("samoyed", "default", ["front", "right", "head"]), ("poodle", "default", ["front", "right", "head"]), ] def download(breed: str, variant: str, view: str) -> None: target = OUT / breed / variant / f"{view}.png" if target.exists(): return url = f"{API}/images/{breed}/{variant}/{view}" for attempt in range(1, 4): res = requests.get( url, params={"size": "full"}, headers={"Authorization": f"Bearer {KEY}"}, timeout=60 ) if res.status_code in (502, 503): time.sleep(attempt) continue if res.status_code == 429: raise SystemExit("Monthly quota is used up.") res.raise_for_status() target.parent.mkdir(parents=True, exist_ok=True) target.write_bytes(res.content) print("saved", target, res.headers.get("X-Quota-Remaining")) return raise RuntimeError(f"gave up on {url}") for breed, variant, views in WANTED: for view in views: download(breed, variant, view) ``` Both scripts do the same four things: skip what you have, retry the errors the API marks as retryable, stop on a used-up quota, and write the file under the storage key. ## Serve from your side Upload the folder to object storage and put a CDN in front, or serve it from your own web server. A given storage key always holds the same file, so you can set a long cache time on your copy. ```text Cache-Control: public, max-age=31536000, immutable Content-Type: image/png ``` If you replace a file later, give it a new key rather than overwriting the old one. Your pages and apps then point at `https://img.example.com/dog/pug/fawn/front.png`, which is yours. ## When hot-linking is fine Previews are different. The 512 and 1024 pixel WebP files may be hot-linked, they are served with a one-year immutable cache header, and a visible credit link next to the image or on the same page covers the license. For a landing page or a prototype, that is the simplest route. Use full-size files, stored on your side, when you need 2048 pixels, PNG, print or packaging, or when you want no credit line. ## Running the numbers Re-running the job is safe. Existing files are skipped, so a second run costs nothing. The count only grows when you add a new image to `WANTED`. A failed fetch (`502`) is not counted either. If you change plans or the month turns, you still keep every file you already have. Check the quota at any time with `GET /api/v1/usage`, which needs the key and returns the plan, the limit and what is left. For the API in full, read the [docs](/docs). ## Questions **Why not call the full-size endpoint from my pages?** The full-size files are meant to be downloaded once and stored on your side. Calling the API from pages would also need a key in the browser, and a key must stay on a server. **Does fetching the same image twice use two images of my plan?** Not in the same calendar month (UTC). A plan counts distinct full-size images per month, so a repeat fetch in that month is free. Storing the file means you never need to repeat it. **Can I hot-link the free previews?** Yes. Previews may be hot-linked, with a visible credit link next to the image or on the same page. Full-size files may not be hot-linked. **Can I publish the stored files as my own image library?** No. The license allows use in your own products, but not reselling, sublicensing or republishing the images as a collection, dataset or image library. Read the license page for the exact terms. --- Source: https://dogimage.dev/guides/compare-two-dog-breeds-side-by-side # Compare two dog breeds side by side One HTML file, plain JavaScript: pick two dog breeds and see the same view of both next to each other, with a view switcher and credit links. No key needed. Two selects, one set of view buttons and two images in a row: that is a breed comparison. This guide gives you a single HTML file with plain JavaScript. Choose two breeds, choose an angle, and both dogs appear in that same view side by side. ## Read this first: shape, not size Each dog fills its own frame. Every image is a square canvas where the dog takes up at most 80 percent, centered, whatever the breed. A Great Dane and a Chihuahua are therefore drawn at about the same size on screen. The comparison shows **shape and proportions**: leg length against body, muzzle length, ear set, coat outline. It does not show relative size. If you want to tell readers which breed is bigger, write that in text from a source you trust. Do not let the pictures imply it. The images are also AI-generated illustrations, not photos of real dogs. ## The whole page Save this as `compare.html` and open it in a browser: ```html Compare two dog breeds

Compare two dog breeds

Each dog fills its own frame, so this shows shape and proportions, not relative size. Images are AI-generated illustrations.

``` ## How it works **Load the catalog once.** `GET /api/v1/breeds` returns `views` (id and label) and `breeds` (slug, name and variants). It needs no key, and the API allows cross-origin reads, so a page opened from your disk or any other site can call it. The code stores both lists in one `state` object. **Build both selects and the view buttons from the data.** Nothing is hardcoded except two starting breeds, and the code falls back to the first and last breed if those slugs are ever missing. The view buttons come from the `views` array, so their labels match the catalog: Front; Front three-quarter, left; Left side; and so on. **One URL pattern draws both dogs.** For a slug and a view id: ``` https://dogimage.dev/api/v1/images/{slug}/default/{view}?size=512 ``` The `default` word means the breed's main set. The address redirects to a transparent WebP, which an `` follows. Changing the view just changes the last path segment on both images, so the two dogs always show the same angle. **The stage keeps frames equal.** Both `.stage` boxes use `aspect-ratio: 1` and `object-fit: contain`, so the two frames are identical squares. That is a feature for comparing outlines, and it is the reason the page cannot show size. ## Using it well The standing views share one ground line, so `left` and `right` are good for comparing back length, leg length and head carriage. `front` and `rear` show width and chest. `head` compares muzzle shape and ear set, and it is not a standing pose. The [left side gallery](/views/left) shows every breed from one angle if you want to scan many at once. Pairs with a similar outline, such as two spaniels, read clearly. Pairs that differ a lot in coat volume, such as a Samoyed and a Dachshund, compare mainly by silhouette. ## Credit and next steps Both captions link to the breed pages, which is the credit the free license asks for. It must be visible next to the image or on the same page. Read the [license](/license) before you use the page in a product. The endpoints are in the [docs](/docs). For a version that lets people search a long list instead of scrolling a select, see the [autocomplete guide](/guides/javascript-dog-breed-autocomplete). ## Questions **Why do the two dogs look the same size?** Each dog is generated to fill its own square frame, up to 80 percent of it. The images do not carry real-world scale, so a comparison shows shape and proportions only. **Can I compare a breed's colors, not just two breeds?** Yes. Breeds with several colors list them as variants in the catalog. Swap the word default in the image URL for a variant slug such as fawn or black. **Do I need a server to run the page?** No. Save it as an HTML file and open it in a browser. The breed list and previews are keyless and allow cross-origin reads, so the page can fetch them directly. --- Source: https://dogimage.dev/guides/default-dog-avatars-for-pet-profiles # Default dog avatars for pet profiles Show a dog breed's head portrait as the default avatar until the owner uploads a photo. Covers sizes, mixed breed fallbacks, labeling and a short code example. A profile with no photo looks empty. A good default is the breed's head portrait, shown until the owner uploads a picture of their own dog. It gives the profile a face, matches every other default in the list, and signals that a real photo can replace it. ## The pattern Pick the avatar in this order: 1. The owner's uploaded photo, if there is one. 2. The head portrait of the dog's breed, if you know the breed and the catalog has it. 3. A neutral placeholder of your own design. The second step is a default, not a substitute. A profile card with the breed head and a small "Illustration" label tells a visitor that the owner has not added a photo yet. When they upload one, you replace it with no change to the layout. ## Why the head portrait The `head` view is made for this. Like all views in the set, it is on a square, transparent canvas, so it fits a round or square avatar without cropping. A tinted background color of your own shows through the transparency, so you can color the frame by species, account type or theme. Every breed uses the same format, so a list of avatars lines up. The images are AI-generated illustrations of one adult dog. They show no real dog and are not a breed standard. That is a good reason to label them. Under or beside the avatar, write "Breed illustration" or similar, and keep the owner's own photo clearly separate when they add it. ## Sizes Previews come in two sizes, 512 and 1024 pixels, both WebP. Choose by where the avatar shows: - **List rows and comment threads, up to about 64 CSS pixels:** the 512 file. It stays sharp on high density screens. - **Profile header, about 128 to 256 CSS pixels:** the 512 file is enough on standard screens. Use 1024 when the avatar is large or the screen is high density. Do not pick 1024 for a long list. It sends more bytes than the layout can use. Preview files carry a one-year immutable cache header, so repeat visits cost little once a file is cached. ## Unknown and mixed breeds Some owners do not know the breed, and many dogs are mixes. The catalog has crossbreed entries such as doodles, and each breed has a `crossbreed` flag you can use to tag them. It has no generic "mixed breed" entry. So when the profile says "mixed" or "unknown", do not map it to the nearest breed. That would put the wrong face on someone's pet. Show your own neutral placeholder instead: a silhouette, a paw, or the initials of the pet's name. Seven breeds have several colors. For an avatar, the breed's `default_variant` is enough. Let the owner pick a color only if you have a place for it. ## A mapping function with a fallback Store the breed slug in your pet record, such as `golden-retriever`, not the display name. The function below builds the avatar URL, and the image falls back to the placeholder if the request fails (for example, on a slug that no longer exists). ```js const BASE = "https://dogimage.dev"; const PLACEHOLDER = "/img/pet-placeholder.svg"; // your own file let breedMap = null; async function loadBreeds() { if (breedMap) return breedMap; const res = await fetch(`${BASE}/api/v1/breeds`); if (!res.ok) throw new Error(`Breed list failed: ${res.status}`); const { breeds } = await res.json(); breedMap = new Map(breeds.map((b) => [b.slug, b])); return breedMap; } export async function avatarFor(pet) { if (pet.photoUrl) return { src: pet.photoUrl, isIllustration: false }; const breeds = await loadBreeds(); const breed = pet.breedSlug ? breeds.get(pet.breedSlug) : null; if (!breed) return { src: PLACEHOLDER, isIllustration: false }; return { src: `${BASE}/api/v1/images/${breed.slug}/${breed.default_variant}/head?size=512`, isIllustration: true, creditHref: `${BASE}/dogs/${breed.slug}`, creditText: `${breed.name} image: Dog Image API`, alt: `Illustration of a ${breed.name}`, }; } export function renderAvatar(el, avatar, petName) { const img = document.createElement("img"); img.src = avatar.src; img.alt = avatar.alt ?? petName; img.width = 64; img.height = 64; img.onerror = () => { img.src = PLACEHOLDER; }; el.append(img); } ``` Call `loadBreeds` once per page, then reuse the map for every pet in a list. The URL redirects to the WebP preview, which an `` tag follows. Cross-origin reads of the breed list are allowed, so this runs in the browser with no key. ## Credit link and labels Preview use requires a visible credit link next to the image or on the same page. For a list of many avatars, one line in the page footer or a "Breed illustrations" note near the list is enough, as long as it is visible and links to Dog Image API. On a single profile page, use the exact snippet from the breed page: ```html Beagle image: Dog Image API ``` If you would rather not show a credit link, download full-size files on a server or in a build step and host them yourself. Full-size files need a key and a plan, and you read the key from `DOG_IMAGE_API_KEY`. Check the [pricing page](/pricing) for what a plan covers and the [license](/license) for what the files may be used for. For a native app version of this pattern, see [SwiftUI pet profile breed images](/guides/swiftui-pet-profile-breed-images). To look at the heads, open any breed on the [breeds page](/dogs). ## Questions **Which view works best as an avatar?** The head portrait. It reads well in a small round or square frame and shows the face, which is what an avatar is for. **What do I show for a mixed breed?** The catalog has crossbreed entries such as doodles but no generic mixed-breed entry. For an unknown or mixed dog, show a neutral placeholder of your own instead of forcing a near match. **Do I need a key to show avatars?** No. Preview files need no key and can load straight into an img tag in the browser. Keep the credit link visible somewhere on the page. **Should the default avatar look like a real photo?** No. Label it as an illustration. The images are AI-generated and show no real dog, so the owner should never think it is their pet's picture. --- Source: https://dogimage.dev/guides/dog-api-alternatives-compared # Dog APIs compared: Dog CEO, The Dog API, Dog Image API Compare Dog CEO, The Dog API and Dog Image API on image type, key, price and data, plus which to pick for photos, random images or one visual standard. Dog CEO's Dog API and The Dog API serve pictures of real dogs. Dog Image API serves AI-generated illustrations: one adult dog per set, shown from the same nine views on a transparent background. Pick the first two for photos and random pictures. Pick this one when every breed must look the same. The facts about the two other services below come from their own pages, read on 2026-11-14. If a detail is missing, that service does not state it on those pages, and this guide does not guess. ## What each service says about itself ### Dog CEO's Dog API The [Dog API page](https://dog.ceo/dog-api/) calls itself "the internet's biggest collection of open source dog pictures." Its [documentation](https://dog.ceo/dog-api/documentation/) lists five endpoints: list all breeds, random image, by breed, by sub-breed, and a browsable breed list. The random image example is `https://dog.ceo/api/breeds/image/random`, and a variant returns several at once. The documentation states one limit: the maximum number returned is 50. The pages do not mention an API key, a price, rate limits or a license. They do link the project on GitHub and invite photo submissions through a pull request. ### The Dog API [The Dog API](https://thedogapi.com) describes "60k+ images, breeds and facts" and "650+ breeds," and uses the phrase "Real dogs, real photos." It says it is "free to start, and always free for students and teachers" and gives a "Get your API key" button. Its [API reference](https://docs.thedogapi.com/reference) shows an `x-api-key` header on each endpoint. The reference lists an image search endpoint with filters such as breed, color and age range, image lookup by id, and an upload endpoint. Image responses carry a `breeds` array with fields such as `life_span`, `temperament`, height and weight. The pages I read do not list paid plans, prices or rate limits. ### Dog Image API [Dog Image API](/docs) is a catalog of dog breed images made to one standard. Every set shows one dog from the same nine views, on a transparent background. On the day this was written the catalog had 298 breeds and 329 sets. Previews (WebP, 512 and 1024 pixels) are free with a credit link and need no key. Full-size PNG files, 2048 by 2048 pixels, need a key and a plan. The breed endpoint returns image URLs and a short text summary taken from Wikipedia, not weight, height or temperament fields. ## Side by side | | Dog CEO's Dog API | The Dog API | Dog Image API | |---|---|---|---| | Images | Pictures of dogs, open source collection | Real dog photos | AI-generated illustrations | | Shape of the images | Not stated | Varies by photo | Same nine views, square canvas, transparent background | | Key | Not stated | Key shown in the reference and signup | None for previews, key for full size | | Price | Not stated | Free to start, paid terms not listed | Free previews, plans from $19 a month for full size | | Random image | Yes | Search with filters | No | | Breed data | Breed list | Breeds with life span, temperament, size | Breed, variant and view names, plus a short summary | | Scale stated | Not stated | 60k+ images, 650+ breeds | 298 breeds, 329 sets | ## Which to pick when ### Pick Dog CEO's Dog API when You want a random dog picture fast, for a demo, a placeholder or a small app. Its endpoint is simple, and its site presents the collection as open source. Check its GitHub project for the license that applies to the pictures you use, because the pages above do not state one. ### Pick The Dog API when You need real photos, many pictures per breed, search with filters, or breed facts such as life span and temperament next to the image. If a photo is the point, an illustration is the wrong tool. Read its terms and plans on its own site before you build on it. ### Pick Dog Image API when The product shows many breeds side by side and needs them to match. Picture a breed selector, a quiz, a pet profile, a printed chart or a game where each breed needs the same angle and size. Here every breed comes in the same [nine views](/guides/the-nine-view-image-standard), the dog fills at most 80% of a square canvas, and standing views share one ground line. The transparent background means you can place a dog on any color without masking it yourself. You also get one visual standard. Photos from different sources differ in light, crop and background. That is usually fine for a gallery and a problem for a UI grid. ### Do not pick Dog Image API when - You need real photographs. These images [are AI-generated](/guides/ai-generated-dog-images-what-to-know) and show no real dog. - You need random images, search or upload. The catalog has none of these endpoints. - You need breed facts like temperament or weight. The API returns a summary text, not those fields. - You need free use of full-size files. Only previews are free, and they require a visible credit link. The [license](/license) and [pricing](/pricing) pages set the terms. ## A practical combination The services do not exclude each other. A site can show real photos from a photo API on a breed article and use the nine-view illustrations in its interface, where consistency matters. Keep the labels clear so a reader can tell the two apart. To see how the catalog looks, open [any breed page](/dogs/pug) or the [left side gallery](/views/left), which shows every breed from one angle. ## Questions **Which dog API returns real photos?** Dog CEO's Dog API and The Dog API both serve pictures of real dogs. The Dog API describes its images as real dog photos. Dog Image API serves AI-generated illustrations, not photos. **Which dog API has a random image endpoint?** Dog CEO's Dog API lists a random image endpoint on its own site. Dog Image API has no random-image endpoint; you request a specific breed, variant and view. **Can I get the same views for every breed?** Dog Image API is built for that: every set has the same nine views, on a transparent background, at the same canvas size. The other two services serve collections of pictures, not a fixed set of views per breed. **Is Dog Image API free?** Previews in WebP at 512 and 1024 pixels are free with a visible credit link. Full-size PNG files need a paid plan, starting at $19 a month. --- Source: https://dogimage.dev/guides/javascript-dog-breed-autocomplete # Dog breed autocomplete with plain JavaScript Make a dog breed autocomplete with one HTML file and no framework: a datalist version and a custom list with keyboard support, plus a live preview image. The shortest version of a breed autocomplete is a ``, which the browser turns into a suggestion dropdown. Fill it from `GET /api/v1/breeds` and the job is done. When you need styling control or a visible match list, a small custom version adds about forty lines. ## Version 1: datalist Save this as `breeds.html` and open it. It is a complete page: ```html Breed autocomplete ``` The page maps lowercase breed names to catalog entries. When the text in the box exactly matches a name, either typed or chosen from the dropdown, the preview appears. ## Where a datalist falls short A datalist is easy, but you do not control how it looks, and browsers differ in how they match. Some match anywhere in the text, others only at the start. It also cannot show a thumbnail beside each suggestion. If any of that matters, build a custom list. ## Version 2: custom list with keyboard support Replace the ``, the input's `list` attribute and the script with the following. The list is a plain `
    ` with the ARIA combobox pattern: arrow keys move, Enter picks, Escape closes. ```html ``` The list is capped at eight matches. That keeps the dropdown short and the page fast, because the full catalog sits in memory and filtering it is instant. ## Notes on the data The catalog is one JSON file of breeds, each with a `slug`, a `name` and its colors. Fetching it once is cheaper than asking the API as the user types, and the API has no search endpoint, so filtering on your side is the way. Do not hardcode the breed count anywhere. The `default` word in the image URL stands for each breed's main set, and `front` is one of nine view ids. Change it to `head` for a portrait, or see them all on the [views pages](/views/head). ## Credit and origin Each preview is free to use if a visible link credits Dog Image API on the same page, which is what the figcaption does. The pictures are AI-generated illustrations of dogs, not photos, and the alt text says so. Terms are on the [license page](/license). If you later need full-size PNGs, do not call that endpoint from this file: a key placed in browser code is public. Use a server, as described in the [Next.js guide](/guides/nextjs-dog-breed-images), and check the [docs](/docs) for the endpoint list. ## Questions **Does this work without a build step?** Yes. Save the HTML file and open it in a browser, or serve it from any static host. It uses fetch, which is built into current browsers. **Why can a page on another domain call the API?** The breed endpoints and the preview files allow cross-origin reads, so browser code on other sites can use them. The full-size endpoint is for servers and needs a key. **How many breeds can the list hold?** It holds whatever the catalog returns, which was 298 breeds on 2026-11-14. The code reads the list from the API, so it keeps up as sets are added. --- Source: https://dogimage.dev/guides/dog-breed-icons-from-head-portraits # Dog breed icons from head portraits Use the head portrait view at 512 pixels as a round avatar or breed icon, with CSS crops, background colors, list sizes and a small icon grid. Full code. A breed icon is a small round image of a dog's face. The `head` view at 512 pixels is made for it: transparent background, one breed, one standard framing. Crop it with a CSS circle, put a color behind it, and you have an avatar or a list icon with no image editing. ## The head view Every set on this site has nine views, and `head` is the head portrait. It is an AI-generated illustration, centered on a square canvas with a transparent background. The previews come in 512 and 1024 pixels. For icons, 512 is plenty, and it loads faster. You get the URL from `GET /api/v1/breeds/{breed}`. No key is needed for previews. The reply has `preview_512` for each view of each color variant. ## One round avatar The image is square, so a circle needs only `border-radius: 50%`. The background color shows through the transparent area and fills the circle. ```html Breed avatar Pug head portrait

    Pug image: Dog Image API

    ``` Pick the background from your brand. A mid-tone works on most breeds. A very dark background hides dark dogs, and a very light one hides white dogs, so test a Samoyed and a Doberman before you commit. ## Sizes for lists Set the size with CSS and keep the 512 file for every size. A few common choices: | Where | CSS size | |---|---| | Dense table row | 32 px | | List item with a name | 48 px | | Profile header | 96 px | | Picker card | 128 px | If the dog looks small inside the circle, scale the image up a little inside a clipped wrapper. A wrapper with `overflow: hidden` and `border-radius: 50%`, holding an image at `transform: scale(1.15)`, tightens the framing without a new file. ## A small icon grid without a sprite You do not need a sprite sheet. Each icon is its own cached file. Preview files are served with a one-year immutable cache header, so a repeat visit loads them from the browser cache. The page below fetches the breed list once, then fetches the detail of eight chosen breeds and builds a grid. ```html Breed icon grid

    Images: Dog Image API

    ``` For a long list, do not fetch every breed at once. Fetch a breed when its row scrolls into view, or when the user picks it. The list from `GET /api/v1/breeds` gives you names and slugs, and the [breed list guide](/guides/dog-breeds-list-json) shows how to read it. ## Consistency tips - Use the same view for every icon. Mixing head portraits with side views breaks the set. - Use the same background for the whole grid, or a fixed palette you assign by breed group. - Keep the same size in one list. The framing is standard, so faces line up. - Some breeds have several colors. Read `variants` and choose one on purpose, or let the user choose. ## Credit Previews need a visible link that credits Dog Image API on the same page. Each breed page gives a ready snippet, for example [Pug](/dogs/pug). One link near a grid of icons is enough when it is visible and clear. If you ship icons inside a product where a link does not fit, read the [license](/license) and look at the paid full-size files, which need no credit. See [pricing](/pricing). ## Questions **Which view should I use for an icon?** The head portrait, view id head. It shows the face at a size that stays readable when the icon is small. **Should I use the 512 or the 1024 preview?** Use 512 for icons up to about 200 CSS pixels. Use 1024 only if the icon is shown larger or on screens with a high pixel density. **Will the round crop cut off the dog?** It can. The dog fills at most 80% of a square canvas, so a circle crop is usually safe, but check a few breeds with large ears and scale the image slightly if one is clipped. **Do I need a credit link for every icon?** You need a visible credit link on the page that shows the images. One link on the page is enough when it sits next to the icons. --- Source: https://dogimage.dev/guides/dog-breed-images-for-dog-walking-apps # Dog breed images for dog walking and daycare apps Use dog breed illustrations as default avatars in dog walking and daycare apps: schedule lists, size badges, a React fallback component, and plan sizing. A walking or daycare app can show a breed illustration as the default avatar until an owner uploads a photo of the real dog. Use the head view in lists and the front three-quarter view on the dog's profile. Keep size in your own data, because the images do not show it. ## The default avatar problem An owner signs up, adds a dog named Bruno, and picks "Beagle". That is the whole profile. The walker's schedule for tomorrow lists eleven such dogs. A letter in a grey circle does not help a walker standing in a hallway with a leash. A beagle head does. The default avatar is also a signal of what is missing. When the illustration sits next to a "Add a photo of Bruno" prompt, the owner knows what to do. Treat the illustration as a placeholder that disappears as soon as a real photo exists. ## What the images are good for Every set shows one adult dog in the same nine views on a transparent background, so a list of different breeds looks uniform. The files are square. The dog is centered and fills at most 80% of the canvas, which leaves clean margins in a round or rounded-square avatar. They are AI-generated illustrations, not photos of real dogs. Do not describe them as a photo, and do not let them stand in for the animal's actual look in anything a walker relies on for identification at a door or a park. Color matters here: if an owner picks "Poodle" and the dog is apricot, the default illustration may not match. The catalog has multiple colors for a few breeds only, including Poodle and Golden Retriever, so offer a color step when the breed has variants and otherwise accept the mismatch. ## Size at a glance Walkers need to know a dog's size before they accept a job. The images cannot tell them. Because each dog fills the frame in its own canvas, a Dachshund and a Great Dane look similar in scale. So keep size as your own field: an owner-entered weight or a small, medium or large choice. Show it as a badge next to the avatar, and let the illustration do one job only: say which breed. Two signals with two sources are easier to trust than one picture asked to do both. ## Where each view goes - **Schedule list rows and chat headers:** `head`, in a round slot of 40 to 56 pixels. - **Dog profile:** `front-left`, large, with the owner's photo taking its place when uploaded. - **Walk summary screens:** `left`, a side view that suits a wide, short card. Pick one view per surface and stick to it. ## How to wire it A small React component that prefers the owner's photo and falls back to the breed preview. It carries the credit link, as the free preview requires: ```tsx const BASE = "https://dogimage.dev"; type Dog = { name: string; breedSlug: string; variantSlug?: string; // omit for the default set photoUrl?: string; }; export function DogAvatar({ dog, size = 48 }: { dog: Dog; size?: number }) { const src = dog.photoUrl ?? `${BASE}/api/v1/images/${dog.breedSlug}/${dog.variantSlug ?? "default"}/head?size=512`; return ( {dog.photoUrl ); } export function IllustrationCredit() { return ( Dog breed illustrations: Dog Image API ); } ``` Render `IllustrationCredit` once per screen that shows illustrations, for example in the footer of the schedule. For breed names and slugs, load `GET /api/v1/breeds` into your picker; it needs no key. Use the `slug` as the stored value. Full details are in the [docs](/docs). If you want illustrations to work offline, you can download full-size files on a server with your key in `DOG_IMAGE_API_KEY`, resize them in your build, and ship them inside the app. That route needs a paid plan. Never place the key in the app itself. ## Which plan fits One image is one view of one set at full size. - Previews only: free, with the visible credit link. Most walking apps can stay here. - A bundled head avatar for the 40 breeds your users add most: 40 full-size images, within Starter's 200 a month. - A bundled head avatar for every default set: 298 images. Starter takes two months, Pro takes one. - Head plus front-left for every default set: 596 images, within Pro's 700. See [pricing](/pricing) for what each plan includes. ## License points that matter here Previews need the visible credit link next to the image or on the same page. Full-size files, used inside your own app, need no credit. You may not republish the images as a pack, hand them to other developers, or build a competing image service from them. An app that ships avatars to its own users is the intended use; an open "download all dog avatars" endpoint is not. Read [the license](/license) for the exact wording, and note that it is a draft not yet reviewed by a lawyer. ## Questions **Do the images show how big a breed is?** No. Each dog fills up to 80% of its own square canvas, so a small breed and a large one appear at similar sizes. Store size in your own data and show it as a badge. **Can I use the preview as an in-app avatar on a phone?** Yes. Previews can be used commercially with a visible credit link on the same page. The 512 pixel WebP preview suits an avatar, and it can be hot-linked. **Should I bundle images inside the app?** Only full-size files can be stored on your side, and they come from a server or build step with your key. Do not put the key in the app. Previews can be loaded from their URLs. **What happens when the owner uploads a photo?** Show the owner's photo and stop using the breed illustration for that dog. Keep the illustration only as the fallback. --- Source: https://dogimage.dev/guides/dog-breed-images-for-pet-stores # Dog breed images for online pet stores Use dog breed images for shop by breed menus, landing pages, size guides and emails, with the plan math for 298 breeds and the license points for selling. Online pet stores can use dog breed images for shop by breed menus, breed landing pages, size guides and emails. Show the head portrait in small tiles and a three-quarter view on landing pages. Use free previews with a credit link, and buy full-size files only for print or packaging. ## Where breed images help a store Shoppers often search by breed: a harness for a Pug, a bed for a Great Dane. A breed menu with a picture next to each name is easier to scan than a long text list. The matching style matters here. If every tile has the same angle, size and empty background, the grid looks designed instead of assembled. The images in this catalog are AI-generated illustrations. They show no real dog. Use them for navigation and decoration, not as product photos. ## Shop by breed navigation Build the menu from the breed list so it follows the catalog and you do not maintain it by hand. The list needs no key and can be read from a browser. Use the `head` view in the tiles. A head portrait stays readable at a small size and leaves room for the breed name. Keep one view across the whole menu. Mixing angles in one grid makes the dogs look unequal. Seven breeds come in several colors: French Bulldog, Golden Retriever, German Shepherd, Dachshund, Doberman, Poodle and Great Dane. A store rarely needs the color in a menu. Use the breed's `default_variant` there and offer colors on the landing page, where a shopper may choose. ## Breed landing pages A landing page for one breed can hold a larger picture, a short guide to fitting products and links to filtered product lists. Use `front-left` as the main image. It shows the whole dog, so it works at the top of a page. Add `head` as a small badge near the title if you want a second image. Write the page text yourself: the sizes you stock, fit notes from your returns, care advice from your staff. A picture and a summary do not make a landing page. For the full approach, see [build dog breed pages for a pet website](/guides/build-dog-breed-pages-for-a-pet-website). Put the credit link next to the image when you use previews: ```html Illustration of a French Bulldog French Bulldog image: Dog Image API ``` ## Size guides A size guide shows how a product fits a dog's body. The `left` and `right` views give a clear side profile, and `front` and `rear` show width. A guide for coats or harnesses can show the side view with your own measurement lines drawn over it. Because the background is transparent, you can place the dog on your guide's own color or grid. Be careful with scale. Each dog fills its own frame, so two breeds in the same grid are not drawn to scale against each other. Do not use the pictures to imply that one breed is a given size relative to another. State real measurements in text, from your own sizing data. The [left side gallery](/views/left) shows every breed from one angle if you want to see how the frames compare. ## Emails Email clients differ, so keep it simple. Use the 512 pixel preview or a full-size file you have resized and hosted yourself. Do not link the full-size API endpoint from an email. Previews can be hot-linked, and they must carry the credit link in the message. If you do not want a credit link in a template, download full-size files and host them yourself. ## Which views fit which place | Place | View | Why | |---|---|---| | Menu tile | `head` | Readable small | | Landing page header | `front-left` | Shows the whole dog | | Size guide | `left` or `right` | Clear body profile | | Back of pack or print | `front` | Symmetric, easy to place | ## The plan arithmetic One image is one view of one set at full size. A plan counts distinct images per calendar month (UTC), and fetching the same one again in the same month is free. - One view of every breed's default set is 298 images. Starter allows 200 a month and Pro allows 700, so Pro covers it in one month. On Starter it takes two months. - Three views of every default set (head, front-left, left) is 894 images. That is over Pro, so you spread it across two months or take All images. - If your store covers 40 breeds with three views each, you need 120 images and Starter has room. Free previews cost nothing in images, so start there. See the [pricing page](/pricing) for the plans. ## License points for commerce - Previews, at 512 and 1024 pixels, can be used commercially with a visible credit link next to the image or on the same page. - Full-size files can go into your own products: sites, documents, print and packaging. No credit is needed. - You may not resell the images, sublicense them, or republish them as a collection or library. A "breed pack" download is out. A shop that uses them in its own pages is fine. The license is marked as a draft not yet reviewed by a lawyer. Read the [license page](/license) and have your own adviser check it before you rely on it for packaging or large print runs. ## Questions **Can I put these images on product pages of items I sell?** Yes. Using the images in your own products and sites is allowed. Previews need a visible credit link, and full-size files do not. You may not resell the images or republish them as a collection. **Are these photos I can show as the actual product?** No. They are AI-generated illustrations of a dog breed. Do not present one as a photo of a product, a customer or a real animal. **Which size should I use for a category tile?** The 512 pixel preview is enough for most tiles. Use the 1024 pixel preview for large landing page images, and full-size files for print or packaging. **Do I need all 298 breeds?** No. Fetch only the breeds your catalog sells for. A store focused on 40 breeds needs 40 images per view. --- Source: https://dogimage.dev/guides/dog-breed-images-for-pet-insurance-apps # Dog breed images for pet insurance apps How to use dog breed images in a pet insurance quote flow and on policy cards: which views to show where, how to wire the API, and which plan size fits. A quote form that asks for a breed gets better answers when the customer sees one. Show the head view next to the breed picker, then the front three-quarter view on the policy card. Use the free previews first. Move to full-size files only for print and documents. ## Why show the breed in a quote flow Breed lists are long and many names look alike on a small screen. A picture next to the selected name lets the customer catch a wrong tap before it reaches underwriting. It is a confirmation step, not a verification step. You still ask the questions your product needs, and the image never replaces them. Say what the picture is. Under it, write a short label such as "Illustration of a Beagle". The images are AI-generated, show no real dog, and are not a breed standard. A customer should never read the picture as a statement about their own pet. ## Which view goes where All nine views come from the same set, so they match each other. A reasonable split: - **Breed picker and confirmation chip:** `head`. It reads well at a small size and takes little room next to a dropdown. - **Quote summary and policy card:** `front-left`. This three-quarter view shows the whole dog and is the one most apps pick for a card. - **Policy documents and emails:** `front-left` again, so the dog looks the same everywhere. Change views only when the layout needs it. Keep one rule across the product: one view per place, never mixed within a list. Every image in the catalog uses a square canvas with a transparent background and the dog filling at most 80% of it, so cards line up without cropping. A tinted card color shows through the transparent background. ## Mixed breeds and colors Each breed record has a `crossbreed` boolean. Use it to label the entry in the picker, for example with a "Mix" tag, so a customer choosing a doodle or a cavapoo sees that it is a crossbreed entry. If the customer's dog is a mix that is not in the catalog, do not force a near match. Offer your own "Other or unknown" row and show a neutral placeholder. Seven breeds have several colors. If your rating does not depend on color, use the breed's `default_variant`. If you do ask, the variant list gives a name and a `kind` of `color`, `pattern` or `coat` that you can use as the second step of the picker. ## How to wire it The breed list needs no key, and browsers on other sites may read it. Build the picker from `GET /api/v1/breeds` at load time, so new breeds appear without a deploy: ```js const BASE = "https://dogimage.dev"; async function loadBreeds() { const res = await fetch(`${BASE}/api/v1/breeds`); if (!res.ok) throw new Error(`Breed list failed: ${res.status}`); const { breeds } = await res.json(); return breeds.map((b) => ({ slug: b.slug, name: b.name, isMix: b.crossbreed, headUrl: `${BASE}/api/v1/images/${b.slug}/${b.default_variant}/head?size=512`, })); } ``` The image URL answers with a redirect to a WebP preview, which an `` tag follows. Put the credit link in the quote summary next to the image: ```html Illustration of a Beagle

    Illustration of a Beagle. Beagle image: Dog Image API

    ``` For documents, download full-size files on your server or in a build step, never in app code that ships to customers. The key comes from an environment variable: ```bash curl -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ -o beagle-front-left.png \ "https://dogimage.dev/api/v1/images/beagle/default/front-left?size=full" ``` Store the file in your own bucket and serve it from there. Do not link the full-size endpoint from a page. The full reference is in the [docs](/docs). ## Which plan fits One image is one view of one set at full size. Fetching the same image again in the same month is free. The numbers for an insurer: - One view of every breed's default set: 298 images. Starter covers 200 a month, so you finish in two months. Pro covers all 298 in a single month, with 402 to spare. - Two views of every default set (head and front-left): 596 images, which fits in Pro's 700. - Three views of every default set: 894 images, which is over Pro. You would spread it across two months or take All images. You rarely need the whole catalog on day one. If your quote flow lists the 30 most requested breeds, one view of each is 30 images and Starter has plenty of room. Plans and prices are on the [pricing page](/pricing). ## License points that matter here - Previews can be used commercially with a visible link crediting Dog Image API next to the image or on the same page. A quote summary satisfies that with the snippet above. - Full-size files can go into your own products, including apps, sites, documents and print, with no credit needed. Files you fetched during an active plan stay usable in what you built. - You cannot resell the images, republish them as a collection or library, or use them to build a competing image service. A claims portal that embeds them is fine. A "download our pet image pack" page is not. The license page is marked as a draft not yet reviewed by a lawyer. Have your own counsel read [the license](/license) before you ship a regulated product. ## Questions **Can the image prove which breed a customer's dog is?** No. The images are AI-generated illustrations of one adult dog per set. They help a customer confirm the breed they picked, but they are not evidence of breed and not a breed standard. **Do I need a paid plan for a quote flow?** Not to start. Previews are free for commercial use with a visible credit link. A paid plan is for full-size 2048 pixel PNG files, for example on printed policy documents. **What should I store in the quote record, the breed name or the slug?** Store the slug, such as golden-retriever, plus the variant slug when the customer picks a color. Names can be reworded; slugs are what the image URLs use. **What do I show for a puppy?** The catalog has one life stage, adult. For a puppy quote, show the adult illustration and label it as the breed, not as the customer's dog. --- Source: https://dogimage.dev/guides/dog-breed-images-for-veterinary-software # Dog breed images for veterinary software Use standardized dog breed illustrations in vet software: patient records without a photo, breed pickers at intake, and client reminders, plus plan sizing. Veterinary software needs a breed on screen in many places and a patient photo in almost none of them. A single standard set of breed illustrations fills that gap. Use the head view in lists and the front three-quarter view in records, and label it as an illustration so staff never mistake it for the patient. ## The gap in a patient record A new patient record often starts with a name, a species, a breed and a weight. The photo comes later, if ever. Without a fallback, the record shows an empty circle. Across a day list of thirty appointments, thirty empty circles tell the front desk nothing. A breed illustration gives each row a quick visual anchor. It does not say which animal is in the room. It says which breed the record claims, and that is useful in its own right: a receptionist who sees a Dachshund icon beside a record that says "Great Dane" has just caught a data entry error. ## Why one standard image beats a random stock photo A stock photo shows one particular dog. That dog may differ from the patient in size, coat and age, and the license terms depend on where you found it. Ten stock photos from ten sources also look like ten different products. The catalog is built to one standard. Every set shows one adult dog in the same nine views, on a transparent background, in a square canvas where the dog fills at most 80% of the frame. Standing views share one ground line. That consistency is the reason to use it in a clinical tool: the interface looks calm and uniform, and the image carries no extra claim about the animal. State the limits to your own users. The images are AI-generated illustrations, not medical references and not a breed standard. Each set passes an automatic check of its nine views and is reviewed by a person over time, but nobody should read anatomy off them. Anything that touches care decisions comes from your clinical sources. ## Where each view helps - **Intake breed picker:** `head`, small, beside each option. Staff scan names and faces together. - **Patient list and appointment board:** `head` again, in a fixed square slot. - **Patient record header:** `front-left`, larger, with a caption such as "Breed illustration". - **Client reminders:** `front-left` at 512 pixels, with the credit line under the image. - **Intake kiosks or printed forms:** `front` or `left`, depending on the layout. The `left` side view suits a narrow column. The summary text from `GET /api/v1/breeds/{breed}` comes from Wikipedia and may be null. If you show it, show its source link too, because that text is CC BY-SA 4.0. Keep it out of clinical screens where it could be mistaken for guidance. ## How to wire it Sync the breed list on a schedule from your server and store the slug beside your own breed record: ```python import json import urllib.request BASE = "https://dogimage.dev" def fetch_breeds(): with urllib.request.urlopen(f"{BASE}/api/v1/breeds", timeout=20) as res: return json.load(res)["breeds"] for b in fetch_breeds(): row = { "slug": b["slug"], "name": b["name"], "is_mix": b["crossbreed"], "default_variant": b["default_variant"], } print(json.dumps(row)) # replace with your upsert by slug ``` In the UI, build the image address from those three stored fields. The `default` variant word also works in place of a color slug: ```html Dachshund breed illustration ``` Previews are served with a one-year immutable cache header, so repeat views cost your users no extra download. A browser app may read the breed list and previews directly. Full-size files, if you want them for printed forms, are downloaded once from a server with your key in `DOG_IMAGE_API_KEY` and stored on your side. Details are in the [docs](/docs). ## Which plan fits One image is one view of one set at full size. - A head view of every default set: 298 images. Starter allows 200 a month, so spread it over two months. Pro allows 700 and covers it with room left. - Head plus front-left for every default set: 596 images, which Pro covers. - A clinic group that only lists the 40 breeds it sees most often needs 40 full-size images for one view. Starter is enough. If your software only shows 512 or 1024 pixel previews on screen, you may not need a plan at all. The free tier requires a visible credit link on the same page as the image. A footer line on the patient list and the record page does that. Check [pricing](/pricing) before you decide. ## License points for a clinical product Full-size files are for your own product: apps, sites, documents and print, with no credit needed. You may not resell them, republish them as an image library, or use them to build a competing image service. A practice management system that displays them in its own screens is the intended use. A "breed icon pack" sold to other software vendors is not. The license page is a draft not yet reviewed by a lawyer, so confirm the terms in [the license](/license) with your own counsel if you sell to regulated customers. ## Questions **Are these images safe to use as clinical references?** No. They are AI-generated illustrations that show no real dog and are not a breed standard. Use them as a recognition aid in the interface, never for diagnosis, body condition scoring or breed identification. **Can I show the image to clients in reminder emails?** Yes. Preview files may be used commercially with a visible link crediting Dog Image API next to the image or on the same page. Put the credit line right under the image in the email. **Should the image replace a photo of the actual patient?** No. Keep it as a fallback that is replaced when a clinic uploads a real photo. The illustration shows a typical adult standing dog, not the patient. **How do I keep breed data in sync when the catalog grows?** Read GET /api/v1/breeds on a schedule from your server and upsert by slug. The live count changes, so do not hardcode it. --- Source: https://dogimage.dev/guides/django-dog-breed-images # Dog breed images in a Django app Add dog breed images to Django: a management command stores full-size PNGs in MEDIA_ROOT, a template shows them, and a keyless preview with credit as fallback. To show dog breed images in Django, run a management command once that downloads full-size PNGs into `MEDIA_ROOT`, record each file in a small model, and render the stored file in a template. Until a file exists, fall back to the free 512 pixel preview and its credit link. ## What you are building The images come from Dog Image API. They are AI-generated illustrations, one dog per set, shown from nine views on a transparent background. Full-size files are 2048 x 2048 PNGs and need a key and a plan. Previews are WebP files and need neither. The plan for the app has three parts: 1. A `DogImage` model that remembers which breed, variant and view you stored. 2. A command, `fetch_dog_images`, that talks to the API and writes files. 3. A view and template that prefer your stored file and fall back to the preview. This was checked on Django 6.1 with `requests` installed. Install the library with `pip install requests` and export the key in the shell that runs the command: ```bash export DOG_IMAGE_API_KEY="paste-your-key-here" ``` ## Settings Add the app to `INSTALLED_APPS` and point Django at a media folder. ```python # settings.py INSTALLED_APPS += ["dogs"] MEDIA_URL = "media/" MEDIA_ROOT = BASE_DIR / "media" ``` Do not read the key in `settings.py`. Only the command needs it, so only the command reads it. ## The model One row per stored image is enough. The unique constraint keeps you from saving the same file twice. ```python # dogs/models.py from django.db import models class DogImage(models.Model): breed = models.SlugField() variant = models.SlugField(default="default") view = models.SlugField() file = models.FileField(upload_to="dogs/") class Meta: constraints = [ models.UniqueConstraint( fields=["breed", "variant", "view"], name="one_file_per_image" ) ] ``` Run `python manage.py makemigrations dogs` and `python manage.py migrate`. A `FileField` avoids the Pillow requirement that `ImageField` brings. ## The management command Create `dogs/management/commands/fetch_dog_images.py`, and add empty `__init__.py` files in `management` and `commands`. ```python import os import time import requests from django.core.files.base import ContentFile from django.core.management.base import BaseCommand, CommandError from dogs.models import DogImage BASE = "https://dogimage.dev" class QuotaUsedUp(Exception): pass def error_text(resp): try: err = resp.json()["error"] return f"{resp.status_code} {err['code']}: {err['message']}" except (ValueError, KeyError): return f"{resp.status_code} {resp.reason}" def fetch_full(key, breed, variant, view, attempts=4): url = f"{BASE}/api/v1/images/{breed}/{variant}/{view}" for attempt in range(1, attempts + 1): resp = requests.get( url, params={"size": "full"}, headers={"Authorization": f"Bearer {key}"}, timeout=60, ) if resp.status_code == 200: return resp.content, resp.headers.get("X-Quota-Remaining") if resp.status_code == 429: raise QuotaUsedUp(error_text(resp)) if resp.status_code in (502, 503) and attempt < attempts: time.sleep(2**attempt) continue raise CommandError(error_text(resp)) class Command(BaseCommand): help = "Download full-size dog breed PNGs into MEDIA_ROOT, once each." def add_arguments(self, parser): parser.add_argument("breeds", nargs="+", help="Breed slugs, such as pug") parser.add_argument("--views", nargs="+", default=["front", "left", "head"]) parser.add_argument("--variant", default="default") def handle(self, *args, **options): key = os.environ.get("DOG_IMAGE_API_KEY") if not key: raise CommandError("Set DOG_IMAGE_API_KEY in the environment.") for breed in options["breeds"]: for view in options["views"]: variant = options["variant"] if DogImage.objects.filter(breed=breed, variant=variant, view=view).exists(): self.stdout.write(f"skip {breed}/{variant}/{view} (already stored)") continue try: data, remaining = fetch_full(key, breed, variant, view) except QuotaUsedUp as err: self.stderr.write(f"Stopping: {err}") return image = DogImage(breed=breed, variant=variant, view=view) image.file.save(f"{breed}/{variant}/{view}.png", ContentFile(data)) self.stdout.write(f"saved {image.file.name}, remaining: {remaining}") ``` Run it with `python manage.py fetch_dog_images pug beagle --views front left`. ### What the command does on errors A `429` with the code `quota_used_up` means the monthly count is spent. The command prints the message and returns, so files saved so far stay in place and the exit code is clean. Retrying would not help. A `502` (`file_unavailable`) or `503` (`plan_check_failed`) is retried after 2, 4 and 8 seconds. Every other failure, such as `401` for a bad key or `404` for a wrong slug, raises `CommandError` with the API's own message. The [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) lists each code. ### Watching your quota Every full-size reply carries `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. The command prints the remaining value after each save. On the All images plan that header reads `unlimited`, which is why the command prints it as text and never casts it to an integer. ## The view and template The view looks for a stored file. If it finds none, it builds the preview URL and the credit URL. ```python # dogs/views.py from django.shortcuts import render from .models import DogImage BASE = "https://dogimage.dev" def breed_detail(request, breed, view="front"): stored = DogImage.objects.filter(breed=breed, variant="default", view=view).first() return render(request, "dogs/breed_detail.html", { "name": breed.replace("-", " ").title(), "stored": stored, "preview_url": f"{BASE}/api/v1/images/{breed}/default/{view}?size=512", "credit_url": f"{BASE}/dogs/{breed}", }) ``` ```html

    {{ name }}

    {% if stored %} {{ name }}, AI-generated illustration {% else %} {{ name }}, AI-generated illustration

    {{ name }} image: Dog Image API

    {% endif %} ``` The preview URL answers with a redirect to a WebP file, and browsers follow it. Previews may be hot-linked, but only with the visible credit link, as the template shows. Your own stored PNGs need no credit under the full-size terms on the [license page](/license). In development, add `static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)` to your URL patterns so `/media/` is served. In production, let your web server or object storage serve that folder. ## Choosing breeds Look up slugs on the [breed index](/dogs) or on a page such as [/dogs/pug](/dogs/pug). Seven breeds have several colors. For those, `default` is the main set, and a color slug picks another one. Check [pricing](/pricing) before a big run, because each distinct full-size image counts against your monthly plan. ## Questions **Why store the PNGs in MEDIA_ROOT instead of calling the API from a view?** Full-size files are meant to be downloaded once and kept on your side, and the endpoint needs a key that must stay on the server. A stored file also loads faster and keeps working if you later change plans. **Can the template show a dog before I have downloaded it?** Yes. The 512 pixel preview needs no key and may be hot-linked. Show it with the visible credit link to Dog Image API next to the image, and switch to your stored file once the command has run. **Where should DOG_IMAGE_API_KEY live in a Django project?** In the process environment of the machine that runs the command, set by your shell, your deploy tool or a secrets file outside version control. Do not put it in settings.py, a template or any JavaScript. **What happens when I run the command twice?** It skips every image that already has a database row and a stored file, so it asks the API only for what is missing. The API also counts a distinct image once per month, so a repeat fetch would not cost more quota. --- Source: https://dogimage.dev/guides/laravel-dog-breed-images # Dog breed images in a Laravel app Add dog breed images to Laravel: an Artisan command saves full-size PNGs with the Http client and Storage, and a Blade component shows the preview with credit. In Laravel, an Artisan command can download full-size dog breed PNGs with the `Http` client, save them with the `Storage` facade, and a Blade component can show the stored file or the free preview with its credit link. The key lives in `.env` and is read through config. ## Why download first Dog Image API serves AI-generated illustrations of one dog per set, in nine views, on a transparent background. Full-size files are 2048 x 2048 PNGs. They need a key and a plan, and they are meant to be downloaded once and served from your own disk. So you fetch them with a command, not on page load. This guide targets current Laravel with the default `public` disk. Before you use a real key, run the command once with a throwaway value to see the expected `401` and confirm the wiring. ## Configuration Put the key in `.env`, and keep that file out of version control. ```bash DOG_IMAGE_API_KEY=paste-your-key-here ``` Expose it through `config/services.php`: ```php 'dog_image' => [ 'key' => env('DOG_IMAGE_API_KEY'), ], ``` Then link the public disk once, so stored files are reachable at `/storage`: ```bash php artisan storage:link php artisan make:command FetchDogImages ``` ## The Artisan command Open `app/Console/Commands/FetchDogImages.php` and replace it with this. ```php error('Set DOG_IMAGE_API_KEY in .env.'); return self::FAILURE; } $disk = Storage::disk('public'); $variant = $this->option('variant'); $views = explode(',', $this->option('views')); foreach ($this->argument('breeds') as $breed) { foreach ($views as $view) { $path = "dogs/{$breed}/{$variant}/{$view}.png"; if ($disk->exists($path)) { $this->line("skip {$path}"); continue; } $response = Http::baseUrl('https://dogimage.dev') ->withToken($key) ->timeout(60) ->retry(3, 2000, function ($exception) { return $exception instanceof RequestException && in_array($exception->response->status(), [502, 503], true); }, throw: false) ->get("/api/v1/images/{$breed}/{$variant}/{$view}", ['size' => 'full']); if ($response->status() === 429) { $this->warn('Stopping: '.$response->json('error.message', 'quota_used_up')); return self::SUCCESS; } if (! $response->ok()) { $this->error(sprintf( '%d %s: %s', $response->status(), $response->json('error.code', 'error'), $response->json('error.message', 'Request failed') )); return self::FAILURE; } $disk->put($path, $response->body()); $this->info("saved {$path}, remaining this month: ".$response->header('X-Quota-Remaining')); } } return self::SUCCESS; } } ``` Run it with `php artisan dogs:fetch pug beagle --views=front,left`. ### What each part does `withToken($key)` sends `Authorization: Bearer `. The `retry()` call tries up to three times, waits two seconds between tries, and only repeats when the reply is a `502` (`file_unavailable`) or a `503` (`plan_check_failed`). Passing `throw: false` returns the last response instead of an exception, so the code below it can branch on the status. A `429` is handled before anything else. Its code is `quota_used_up`, and the command prints the API's message and returns success, because running out of quota is an expected state and not a crash. Every other non-200 status, such as `401`, `402` or `404`, prints the code and message and fails. The [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) lists each one. ### Quota headers `$response->header('X-Quota-Remaining')` returns a string. On the All images plan it is `unlimited`. If you want a warning, check before comparing: ```php $remaining = $response->header('X-Quota-Remaining'); if (ctype_digit($remaining) && (int) $remaining < 10) { $this->warn('Fewer than 10 images left this month.'); } ``` The `X-Quota-Limit` and `X-Quota-Used` headers sit beside it. ## A Blade component Generate a class-based component: ```bash php artisan make:component DogImage ``` ```php name = Str::headline($breed); if ($disk->exists($path)) { $this->src = $disk->url($path); } else { $this->src = "{$site}/api/v1/images/{$breed}/{$variant}/{$view}?size=512"; $this->creditUrl = "{$site}/dogs/{$breed}"; } } public function render(): View { return view('components.dog-image'); } } ``` And the template, `resources/views/components/dog-image.blade.php`: ```html
    {{ $name }}, AI-generated illustration @if ($creditUrl)
    {{ $name }} image: Dog Image API
    @endif
    ``` Use it anywhere in a Blade view: ```html ``` When your stored file exists, the component shows it and skips the credit, since full-size files need none. When it does not, the component falls back to the keyless preview and prints the visible credit link the preview terms require. Check the [license](/license) for the full terms, including no reselling the images as a collection. ## Picking breeds and views Slugs are on the [breed index](/dogs). Views are `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right`, `front-right` and `head`. See every breed from one angle on [/views/left](/views/left). Each distinct full-size image counts once per month, so check [pricing](/pricing) before you request all nine views for many breeds. ## Questions **Why read the key through config() and not env() in the command?** Laravel only guarantees env() inside config files. Once you cache the configuration in production, env() calls elsewhere return null. Read DOG_IMAGE_API_KEY in config/services.php and use config() in the command. **Can I use another Storage disk, such as S3?** Yes. The command uses the public disk, but any disk you configure works, because it only calls exists() and put(). Change the disk name in the command and in the component. **Does the component need a key?** No. The preview is a keyless 512 pixel WebP and the stored file is already on your disk. The key is used only by the Artisan command, which should run on a server or in a deploy step. **What should the command do when the quota runs out?** Stop. A 429 with the code quota_used_up means the monthly count is spent, and retrying does not help. The command prints the message and exits cleanly, leaving the files it already saved. --- Source: https://dogimage.dev/guides/rails-dog-breed-images # Dog breed images in a Rails app Use dog breed images in Rails: a rake task downloads full-size PNGs with Net::HTTP into Active Storage, and a helper shows the free preview with credit. To use dog breed images in Rails, write a small `Net::HTTP` client for the full-size endpoint, call it from a rake task that attaches each PNG to an Active Storage record, and add a view helper that renders the free preview with its credit link. The key stays in an environment variable. ## The idea Dog Image API serves AI-generated dog illustrations, one dog per set, in nine views on a transparent background. Previews are WebP and keyless. Full-size 2048 x 2048 PNGs need a key and a plan, and you download each one once. So the app has two paths. A rake task fills your storage with full-size files ahead of time. A helper shows the preview for any breed you have not stored yet. Before you use a real key, run the task once with a throwaway value to see the expected `401` and confirm the wiring. Set the key where the task runs: ```bash export DOG_IMAGE_API_KEY="paste-your-key-here" ``` ## The client Put this in `lib/dog_image_client.rb`. It uses only the standard library. ```ruby require "net/http" require "json" # Minimal client for the full-size endpoint. Stays on the server. class DogImageClient BASE = URI("https://dogimage.dev") class QuotaUsedUp < StandardError; end class ApiError < StandardError; end Download = Struct.new(:body, :remaining) def initialize(key = ENV.fetch("DOG_IMAGE_API_KEY")) @key = key end def full(breed, variant, view, attempts: 4) uri = URI("#{BASE}/api/v1/images/#{breed}/#{variant}/#{view}?size=full") attempt = 0 loop do attempt += 1 res = get(uri) case res.code.to_i when 200 return Download.new(res.body, res["X-Quota-Remaining"]) when 429 raise QuotaUsedUp, describe(res) when 502, 503 raise ApiError, describe(res) if attempt >= attempts sleep 2**attempt else raise ApiError, describe(res) end end end private def get(uri) request = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{@key}") Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 60) do |http| http.request(request) end end def describe(res) error = JSON.parse(res.body).fetch("error") "#{res.code} #{error['code']}: #{error['message']}" rescue JSON::ParserError, KeyError "#{res.code} #{res.message}" end end ``` `ENV.fetch` raises a `KeyError` if the variable is missing, so a forgotten key fails at once instead of sending an empty header. Three outcomes are handled on purpose: - `200` returns the PNG bytes and the `X-Quota-Remaining` header. - `429` raises `QuotaUsedUp`. The code is `quota_used_up`, and retrying cannot help. - `502` (`file_unavailable`) and `503` (`plan_check_failed`) wait 2, 4 and 8 seconds, then raise `ApiError` if they keep failing. Anything else, such as `401` or `404`, raises `ApiError` with the code and message from the JSON body. The [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) lists them all. ## The model and the migration Add Active Storage if the app does not have it yet, with `bin/rails active_storage:install`. Then generate a small model: ```bash bin/rails generate model DogImage breed:string variant:string view:string ``` Edit the model and the migration so that one row means one stored image. ```ruby # app/models/dog_image.rb class DogImage < ApplicationRecord has_one_attached :file validates :breed, :variant, :view, presence: true end ``` In the migration, add `add_index :dog_images, [:breed, :variant, :view], unique: true`, then run `bin/rails db:migrate`. ## The rake task ```ruby # lib/tasks/dogs.rake require Rails.root.join("lib/dog_image_client") namespace :dogs do desc "Download full-size dog PNGs once and attach them. BREEDS=pug,beagle VIEWS=front,left" task fetch: :environment do breeds = ENV.fetch("BREEDS", "pug,beagle,samoyed").split(",") views = ENV.fetch("VIEWS", "front,left,head").split(",") client = DogImageClient.new begin breeds.each do |breed| views.each do |view| record = DogImage.find_or_initialize_by(breed: breed, variant: "default", view: view) if record.file.attached? puts "skip #{breed}/#{view}" next end download = client.full(breed, "default", view) record.file.attach( io: StringIO.new(download.body), filename: "#{breed}-default-#{view}.png", content_type: "image/png" ) record.save! puts "saved #{breed}/#{view}, remaining this month: #{download.remaining}" end end rescue DogImageClient::QuotaUsedUp => e puts "Stopping: #{e.message}" end end end ``` Run it with `BREEDS=pug,beagle bin/rails dogs:fetch`. The `rescue` sits outside both loops, so a 429 ends the whole run and keeps what is saved. Any other error propagates and shows a full trace. To skip Active Storage, replace the attach block with `File.binwrite(Rails.root.join("storage", "#{breed}-#{view}.png"), download.body)`. ### Reading the quota header `download.remaining` is a string. It is `unlimited` on the All images plan, so check before you do arithmetic: ```ruby if download.remaining =~ /\A\d+\z/ && download.remaining.to_i < 10 puts "Fewer than 10 images left this month." end ``` ## The preview helper For breeds you have not stored, link the free 512 pixel preview and print the credit next to it. ```ruby # app/helpers/dog_images_helper.rb module DogImagesHelper DOG_IMAGE_SITE = "https://dogimage.dev".freeze def dog_preview_url(breed, view: "front", variant: "default", size: 512) "#{DOG_IMAGE_SITE}/api/v1/images/#{breed}/#{variant}/#{view}?size=#{size}" end def dog_preview(breed, view: "front") name = breed.tr("-", " ").titleize safe_join([ image_tag(dog_preview_url(breed, view: view), alt: "#{name}, AI-generated illustration", width: 512, height: 512, loading: "lazy"), tag.p(link_to("#{name} image: Dog Image API", "#{DOG_IMAGE_SITE}/dogs/#{breed}")) ]) end end ``` In a view, call `<%= dog_preview("golden-retriever", view: "left") %>`. The URL redirects to a WebP file served with a one-year cache header, and previews may be hot-linked with the credit. For a stored record, use `url_for(record.file)` instead and skip the credit, which full-size files do not need. Read the [license](/license) for the exact limits, such as no reselling the images as a collection. ## Where to go next Browse slugs on [/dogs](/dogs), and compare plans on the [pricing page](/pricing). Each distinct full-size image counts once per calendar month, so a second run of the task costs nothing for files it already holds. ## Questions **Do I have to use Active Storage?** No. The client class below only returns bytes and the quota header. You can write them to storage/ or public/ with File.binwrite instead of attaching them, and the rest of the code stays the same. **Can I call the full-size endpoint from a controller on each request?** You should not. Full-size files are meant to be downloaded once and stored on your side, and the key must stay on the server. Run the rake task from a deploy step or by hand, then serve your stored copy. **How do I know how much quota is left?** Read the X-Quota-Remaining header on each full-size reply. It is a number, or the word unlimited on the All images plan. The usage endpoint also reports the month, the plan, the limit, the count used and the remaining count. **Is the preview helper allowed in a commercial app?** Yes. Previews may be used commercially with a visible link crediting Dog Image API next to the image or on the same page. The helper renders that link for you. The license page is the reference. --- Source: https://dogimage.dev/guides/android-compose-dog-breed-images # Dog breed images in Jetpack Compose Show dog breed images in Jetpack Compose: fetch the catalog with Ktor or Retrofit, parse it with kotlinx.serialization, and load keyless previews with Coil. Request `GET /api/v1/breeds/{breed}` with Ktor or Retrofit, parse it with `kotlinx.serialization`, and hand a `preview_512` URL to Coil's `AsyncImage`. Previews need no key. The app never holds one: full-size files are your backend's job. The images are AI-generated illustrations of one adult dog, drawn from nine views on a transparent background. In Compose that transparency lets you place the dog on any surface color of your theme. ## What stays out of the APK An APK can be unpacked, and `BuildConfig` fields are plain text inside it. So the app calls only keyless URLs: the breed list, the breed detail and the preview files. Full-size PNG files need a key. Your server downloads them once, stores them and serves them to the app. [Cache and self-host dog breed images](/guides/cache-and-self-host-dog-images) describes the job. ## Dependencies The versions below were the ones shown in the Ktor and Coil documentation when this guide was written. Check the release notes before you copy them. ```kotlin // build.gradle.kts (module) plugins { // Use the same version as your Kotlin Gradle plugin. kotlin("plugin.serialization") version "" } dependencies { implementation("io.ktor:ktor-client-okhttp:3.6.1") implementation("io.ktor:ktor-client-content-negotiation:3.6.1") implementation("io.ktor:ktor-serialization-kotlinx-json:3.6.1") implementation("io.coil-kt.coil3:coil-compose:3.6.3") implementation("io.coil-kt.coil3:coil-network-okhttp:3.6.3") } ``` Coil 3 needs a network artifact next to `coil-compose`, as the [Coil network documentation](https://coil-kt.github.io/coil/network/) explains. Add the internet permission to the manifest too: ```xml ``` ## About WebP Android decodes WebP itself, and the [Android supported formats page](https://developer.android.com/media/platform/supported-formats) lists transparency support from Android 4.2.1. You add no WebP library for the previews. If a preview does not show, test it on an emulator or device first, then check the Coil error callback. ## Models Two `@Serializable` classes cover what this screen reads. Names with underscores map through `@SerialName`. ```kotlin import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class Catalog(val breeds: List) @Serializable data class BreedSummary( val slug: String, val name: String, @SerialName("default_variant") val defaultVariant: String, ) @Serializable data class BreedDetail( val slug: String, val name: String, @SerialName("default_variant") val defaultVariant: String, val variants: List, ) @Serializable data class Variant(val slug: String, val name: String, val views: Map) @Serializable data class ViewImages( @SerialName("preview_512") val preview512: String, @SerialName("preview_1024") val preview1024: String, ) ``` The responses hold more fields, such as `summary` and `full`. The client below ignores unknown keys, so the models stay small. ## The client ```kotlin import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.get import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json class DogImageApi { private val base = "https://dogimage.dev/api/v1" private val client = HttpClient(OkHttp) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } } suspend fun breeds(): List = client.get("$base/breeds").body().breeds suspend fun breed(slug: String): BreedDetail = client.get("$base/breeds/$slug").body() } ``` If your project already uses Retrofit, the same models work with the kotlinx.serialization converter: ```kotlin interface DogImageService { @GET("breeds") suspend fun breeds(): Catalog @GET("breeds/{slug}") suspend fun breed(@Path("slug") slug: String): BreedDetail } ``` Build it with `Retrofit.Builder().baseUrl("https://dogimage.dev/api/v1/")` and a JSON converter factory. ## A view model ```kotlin import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch data class BreedUiState( val breeds: List = emptyList(), val selected: BreedDetail? = null, val viewId: String = "front", val error: String? = null, ) class BreedViewModel(private val api: DogImageApi = DogImageApi()) : ViewModel() { private val _state = MutableStateFlow(BreedUiState()) val state: StateFlow = _state init { viewModelScope.launch { runCatching { api.breeds() } .onSuccess { list -> _state.value = _state.value.copy(breeds = list) } .onFailure { _state.value = _state.value.copy(error = "Could not load breeds.") } } } fun select(slug: String) = viewModelScope.launch { runCatching { api.breed(slug) } .onSuccess { _state.value = _state.value.copy(selected = it, error = null) } .onFailure { _state.value = _state.value.copy(error = "Could not load that breed.") } } fun showView(id: String) { _state.value = _state.value.copy(viewId = id) } } ``` ## The Compose screen Pick a breed from a list, then switch views with chips. The image uses the breed's default variant. ```kotlin import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import coil3.compose.AsyncImage @Composable fun BreedScreen(vm: BreedViewModel = viewModel()) { val state by vm.state.collectAsStateWithLifecycle() val uriHandler = LocalUriHandler.current val detail = state.selected val variant = detail?.variants?.firstOrNull { it.slug == detail.defaultVariant } ?: detail?.variants?.firstOrNull() Column(Modifier.fillMaxSize().padding(16.dp)) { if (detail != null && variant != null) { AsyncImage( model = variant.views[state.viewId]?.preview512, contentDescription = "${detail.name}, ${state.viewId} view", contentScale = ContentScale.Fit, modifier = Modifier.fillMaxWidth().height(260.dp), ) TextButton(onClick = { uriHandler.openUri("https://dogimage.dev/dogs/${detail.slug}") }) { Text("${detail.name} image: Dog Image API") } LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { items(variant.views.keys.toList()) { id -> FilterChip(selected = id == state.viewId, onClick = { vm.showView(id) }, label = { Text(id) }) } } } state.error?.let { Text(it, color = MaterialTheme.colorScheme.error) } LazyColumn(Modifier.weight(1f)) { items(state.breeds, key = { it.slug }) { breed -> Text(breed.name, Modifier.fillMaxWidth().clickable { vm.select(breed.slug) }.padding(vertical = 12.dp)) } } } } ``` `AsyncImage` takes a nullable model, so the image area stays empty until the data arrives. The credit button sits right under the image, and its text follows the form the [Samoyed page](/dogs/samoyed) uses for its snippet. ## Next steps The head view makes a good avatar, and a side view such as `left` suits a header. If the app needs files bigger than 1024 pixels, serve them from your own storage. The [license](/license) and [pricing](/pricing) pages explain what the full-size files allow and cost. ## Questions **Does Android decode the WebP previews?** Yes. The Android platform decodes WebP, including transparency from Android 4.2.1. You add no WebP library to load the previews. **Should I put the API key in BuildConfig?** No. BuildConfig values ship inside the APK and can be read. Previews need no key. Full-size files should be downloaded by your backend and served from your own storage. **Can I use Retrofit instead of Ktor?** Yes. Both work with kotlinx.serialization. The models are the same, and only the request code changes. **Which preview size fits a Compose list row?** The 512 pixel preview suits a thumbnail or an avatar. Use the 1024 pixel one for a header image that spans the screen. --- Source: https://dogimage.dev/guides/react-native-dog-breed-images # Dog breed images in React Native Pick a dog breed from a FlatList and show its image in React Native with the Image component and a keyless WebP preview URL, with the credit link and key rules. Fetch `GET /api/v1/breeds`, render the names in a `FlatList`, and when the user taps one, fetch `GET /api/v1/breeds/{slug}` and give the `preview_512` URL of a view to the `Image` component. No key is needed, and the credit link goes under the picture. The images are AI-generated illustrations of one adult dog from nine fixed views, on a transparent background. They are not photos, and you should not present them as photos of a real animal. ## The key stays on your server React Native bundles your JavaScript into the app, so a key placed in it is public. This screen uses three keyless things: the catalog, the breed detail and the preview files. Full-size PNG files, at 2048 pixels, need a key. Let your backend download them once, store them, and serve them to the app. If you want that setup, read [Cache and self-host dog breed images](/guides/cache-and-self-host-dog-images). ## About WebP The previews are WebP. The [React Native Image documentation](https://reactnative.dev/docs/image) carries notes about WebP on iOS and on custom Android builds. For a current project, WebP previews are expected to render on iOS 14 and later and on Android, because both platforms decode WebP themselves. Test one preview on a real iOS and a real Android device before you build the screen around it. If a device shows nothing, check the `onError` callback first. ## Types and fetch helpers ```tsx const API = 'https://dogimage.dev/api/v1'; type Breed = { slug: string; name: string; default_variant: string }; type ViewImages = { preview_512: string; preview_1024: string }; type BreedDetail = { slug: string; name: string; default_variant: string; variants: { slug: string; name: string; views: Record }[]; }; async function getJson(url: string): Promise { const response = await fetch(url); if (!response.ok) { throw new Error(`Request failed with ${response.status}`); } return response.json() as Promise; } export const fetchBreeds = () => getJson<{ breeds: Breed[] }>(`${API}/breeds`).then((data) => data.breeds); export const fetchBreed = (slug: string) => getJson(`${API}/breeds/${slug}`); ``` Errors come back as JSON with an `error.code`. For this screen a plain status check is enough. ## The picker and the image The list and the picture live in one screen. The `FlatList` shows every breed from the catalog, so you add no names by hand. ```tsx import { useEffect, useState } from 'react'; import { ActivityIndicator, FlatList, Image, Linking, Pressable, StyleSheet, Text, View, } from 'react-native'; export default function BreedScreen() { const [breeds, setBreeds] = useState([]); const [selected, setSelected] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { fetchBreeds().then(setBreeds).catch(() => setError('Could not load breeds.')); }, []); async function choose(slug: string) { setLoading(true); setError(null); try { setSelected(await fetchBreed(slug)); } catch { setError('Could not load that breed.'); } finally { setLoading(false); } } const variant = selected ? selected.variants.find((v) => v.slug === selected.default_variant) ?? selected.variants[0] : null; const side = variant?.views['right']; return ( {loading && } {!loading && side && ( )} {selected && ( Linking.openURL(`https://dogimage.dev/dogs/${selected.slug}`)}> {selected.name} image: Dog Image API )} {error && {error}} item.slug} renderItem={({ item }) => ( choose(item.slug)}> {item.name} )} /> ); } const styles = StyleSheet.create({ screen: { flex: 1, padding: 16 }, preview: { height: 260, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eee' }, image: { width: 256, height: 256 }, credit: { color: '#0a58ca', marginVertical: 8 }, error: { color: '#b00020' }, row: { paddingVertical: 12, borderBottomWidth: StyleSheet.hairlineWidth }, }); ``` Three points about this code: - The `Image` has a fixed width and height. Network images need both. - The gray box behind the image is deliberate. The files are transparent, so the box shows where the canvas is. - The credit text is the breed name, then "image: Dog Image API", linked to that breed's page. That is the same form the [Pug page](/dogs/pug) shows. ## A thumbnail in each row A row can show a small image too, but that is one request per breed. The catalog does not include preview URLs, so the detail call is needed for each. Load a thumbnail only for the rows the user is looking at, or for a short list of favorites. For a long list, a text row is the better choice. ## Choosing a view `right` and `left` are side views, `front` faces the camera and `head` is a portrait. Pick one view for the whole list so the pictures read as one set. The [front view gallery](/views/front) shows every breed at once if you want to compare before choosing. ## When you need bigger files A 1024 pixel preview covers most phone screens. For a poster, a printed card or a 2048 pixel hero, use the full-size PNG from your own backend. The [license](/license) explains where each file type can be used. ## Questions **Can I put the API key in a React Native app with an environment variable?** No. A value bundled into the app can be read by anyone who has the app. Previews need no key, and full-size files should be fetched by your backend. **Do I need width and height on the Image?** Yes. React Native cannot size a network image before it loads, so set a width and height, or a style that gives it a size. **Which preview URL should I use?** Use preview_512 for list rows and avatars and preview_1024 for a large image. Both are transparent WebP files. **Does the API have a random dog endpoint?** No. You choose a breed slug, a variant and a view, or read them from the catalog. --- Source: https://dogimage.dev/guides/astro-dog-breed-pages # Dog breed pages in Astro Generate one static Astro page per dog breed from the Dog Image API catalog at build time, with all nine views in a grid and the required credit link. Astro builds one static page per breed when you fetch the Dog Image API catalog inside `getStaticPaths`. Each page then prints the nine views from the same breed in a grid, with the credit link below. There is no client JavaScript and no key. This guide uses Astro 5 with TypeScript in the frontmatter, and the result is a folder of plain HTML files you can host anywhere. ## Create the project ```bash npm create astro@latest dog-pages -- --template minimal cd dog-pages ``` Accept the defaults. You will add three files: a small catalog helper, an index page and a breed page. ## Fetch the catalog once The catalog lists every breed and the nine view ids. Both pages need it, so put the request in a helper that remembers its result. Create `src/lib/catalog.ts`: ```ts const API = "https://dogimage.dev"; export interface View { id: string; label: string; } export interface Breed { slug: string; name: string; crossbreed: boolean; default_variant: string; } export interface Catalog { views: View[]; breeds: Breed[]; } let cached: Promise | undefined; export function getCatalog(): Promise { cached ??= fetch(`${API}/api/v1/breeds`).then((res) => { if (!res.ok) { throw new Error(`Catalog request failed with status ${res.status}`); } return res.json() as Promise; }); return cached; } export function previewUrl(slug: string, view: string, size = 512): string { return `${API}/api/v1/images/${slug}/default/${view}?size=${size}`; } export function creditLink(slug: string): string { return `${API}/dogs/${slug}`; } ``` If the request fails, the build stops with a clear message. That is what you want: a half-empty site is worse than a failed build. ## The index page Create `src/pages/index.astro`. It lists every breed and links to its page: ```astro --- import { getCatalog } from "../lib/catalog"; const { breeds } = await getCatalog(); --- Dog breeds

    Dog breeds

    {breeds.length} breeds, from the Dog Image API catalog.

    ``` The count comes from the array length, so it stays correct without edits. ## One page per breed Create `src/pages/breeds/[slug].astro`. A dynamic route in a static build must export `getStaticPaths`, which returns one entry for each page: ```astro --- import type { GetStaticPaths } from "astro"; import { getCatalog, previewUrl, creditLink } from "../../lib/catalog"; import type { Breed, View } from "../../lib/catalog"; export const getStaticPaths = (async () => { const { breeds, views } = await getCatalog(); return breeds.map((breed) => ({ params: { slug: breed.slug }, props: { breed, views }, })); }) satisfies GetStaticPaths; const { breed, views } = Astro.props as { breed: Breed; views: View[] }; --- {breed.name} images from nine angles

    All breeds

    {breed.name}

    Nine views of one adult {breed.name}, AI-generated illustrations on a transparent background.

      {views.map((view) => (
    • {`${breed.name},
      {view.label}
    • ))}

    {breed.name} image: Dog Image API

    ``` Two things are worth a closer look. First, `getStaticPaths` runs once, at build time, and passes `breed` and `views` to the page as props. The page body does not fetch again. Second, the grid uses `auto-fill` with a 200 pixel minimum, so it shows one column on a narrow phone and four or five on a wide screen without media queries. ## Build and check ```bash npm run dev ``` Open `/breeds/pug/` and you should see nine tiles: Front; Front three-quarter, left; Left side; and so on through Head portrait. Then run `npm run build`. The output folder `dist/breeds/` holds one directory for each breed. ## The credit link Free previews are allowed for any use, commercial included, when a visible link credits Dog Image API next to the image or on the same page. The link at the bottom of each page has the form each breed page on the site gives you. Because the code builds it from the slug, every generated page carries it. See the [license](/license) for the exact wording. ## Things to adjust - **Fewer builds, fewer requests.** The previews are loaded by your visitors' browsers, not by the build. A build makes one catalog request. - **Breed summaries.** `GET /api/v1/breeds/{breed}` returns a short Wikipedia-based summary and its source link. You can fetch it inside the page frontmatter, but that is one request per breed, so cache it if you rebuild often. Credit Wikipedia where you show the text. - **Other angles on one page.** The [left side gallery](/views/left) shows one view across every breed, which is a good model for a second route. - **Full-size files.** They need a key and belong in a build step that stores them on your side. The [Next.js guide](/guides/nextjs-dog-breed-images) shows that pattern, and the [docs](/docs) list the endpoints. ## Questions **Does the build need an API key?** No. The breed catalog and the 512 pixel previews are keyless. A key is only needed for full-size PNG files, which you would download in a separate build or server step. **How many pages will the build create?** One per breed in the live catalog, plus an index page. The code reads the list at build time, so you never hardcode the number and new breeds appear on the next build. **How do I add colors such as fawn or black?** Breeds with several colors list them as variants in the catalog. Replace the word default in the image URL with a variant slug, or add a nested route that reads each breed's variants. --- Source: https://dogimage.dev/guides/dog-drawing-reference-nine-angles # Dog drawing reference: one dog from nine angles Use a turnaround of one dog from nine angles to practice drawing. Learn how to study it, and where AI-generated illustrations fall short of real animals. A turnaround is one subject drawn from several sides. Here you get one dog from nine angles, in the same standing pose, on a transparent background. It helps you practice consistency. It does not replace studying real animals, because these images are AI-generated illustrations. ## Why a turnaround helps When you draw a dog from memory, the front looks fine and the side looks like a different animal. A turnaround exposes that. If your drawing of the front view cannot become the side view, something in your mental model is missing. A set on this site has these views of one dog: front, four three-quarter angles, left and right profile, rear, and a head portrait. The dog is an adult in a standing pose in every view except the head portrait. The standing views share one ground line and the same framing, so the sizes stay consistent as you move around the dog. ## Ways to practice - **Match the angle.** Pick one breed page, for example [Beagle](/dogs/beagle), and draw the dog from the front. Then draw it from the side without looking, and compare. - **Rotate a simple form.** Block the dog out as a box for the chest, a smaller box for the hips and a ball for the head. Turn those forms through the nine views and see where each one moves. - **Landmarks on the ground line.** Mark where the feet touch the ground in each view, and where the back line sits relative to the elbow. Your own drawing should keep those relationships. - **Timed sketches.** Give each view two minutes. The goal is to get the main shapes, not the fur. - **Silhouette check.** Fill your drawing with one color. If the shape does not read as the same dog as the reference, the proportions are off. - **Change the breed.** Draw a Dachshund, then a Great Dane, and look at the ratio of leg length to body length. ## The honest limits These images are illustrations made by AI. They show no real dog. - Anatomy can be wrong. A leg may bend oddly, a paw may have the wrong number of toes, or a joint may sit in the wrong place. The site runs an automatic check on each set and a person reviews sets over time, but that does not make every detail correct. - They are not a breed standard. A club standard describes how a breed should look, and these images do not. - Proportions are shown in a shared frame. Each dog is fitted to its canvas, so size differences between breeds are not true to life. - There is one life stage and one pose. All dogs are adults and standing, which means you will not learn puppies, running, or sleeping from this set. Use the set to learn how a viewpoint changes a shape. Learn anatomy from real dogs, from your own photos taken with permission, from anatomy books and from the written standard of the breed you draw. When the illustration and a real animal disagree, trust the animal. ## License terms for reference use You can use the free previews, 512 and 1024 pixels, for any use, including commercial work, with a visible link that credits Dog Image API next to the image or on the same page. If you publish a sketchbook page, a lesson or a blog post that shows a preview, add the credit: ```html Poodle image: Dog Image API ``` Full-size 2048 pixel PNG files come with a paid plan and need no credit when used in your own products. You may not resell or republish the images as a collection or image library. The [license page](/license) is the reference, and it is marked as a draft not yet reviewed by a lawyer, so read it before you rely on it for anything large. ## Where to start Browse [all breeds](/dogs) and choose a dog you can also find in real life, such as a neighbor's or a park regular. Draw from the reference first, then from the real dog, and compare the two. The differences you find teach more than the reference does. ## A simple weekly routine Pick one breed a week. On day one, draw the front, side and rear views from the reference. On day two, redo them from memory and compare. On day three, add the three-quarter views. Finish by drawing the same breed from a real dog or from photos you took yourself. ## Questions **Can these images teach me real dog anatomy?** Not on their own. They are AI-generated illustrations, so legs, joints or paws can be wrong. Study real animals, photos from reliable sources and a breed standard as well. **Can I trace the images?** The license covers use of the files, and each kind of file has its own conditions. Read the license page, and for practice, drawing from the reference by eye teaches more than tracing. **Do I need to pay to use them as a reference?** No. Free previews at 512 and 1024 pixels are enough for study. They need a visible credit link wherever you publish them. **Why nine angles of the same dog?** A turnaround shows one animal in the same pose from many sides, so you can check that your drawing holds up when the viewpoint changes. --- Source: https://dogimage.dev/guides/dog-front-and-rear-view-reference # Dog front view and rear view reference images Front, rear and four three-quarter views of dog breeds on a transparent background, with what each angle is used for and where to find it on the site. Each dog on this site comes as nine views. Two of them are the straight-on ones: the front view and the rear view. Four more are three-quarter angles between front and side. All of them are AI-generated illustrations on a transparent background. ## The views between the profiles Besides the left and right profiles, a set has these angles: | View | Label on the site | |---|---| | `front` | Front | | `front-left` | Front three-quarter, left | | `rear-left` | Rear three-quarter, left | | `rear` | Rear | | `rear-right` | Rear three-quarter, right | | `front-right` | Front three-quarter, right | The ninth view is the head portrait. The two profiles are covered in the [side view guide](/guides/dog-side-view-reference). ## Front view: what it is for The front view shows the dog facing you, with the chest, the two front legs and the face centered. People use it for: - **Width and chest.** Compare how broad the chest is, how far apart the front legs stand, and how wide the head is against the shoulders. - **Face on mascots and icons.** A symmetrical front view is the base for an avatar, a pet shop sign or a game character select screen. - **Packaging and posters.** A dog looking straight out at the viewer holds attention in a hero spot. - **Ear and muzzle shape.** Ear carriage and muzzle length read well from the front. Every breed from this angle is at [/views/front](/views/front). ## Rear view: what it is for The rear view is the one reference sites often skip, and it is useful for a few jobs: - **Characters walking away.** A dog trotting off into a scene, in a comic panel or a storyboard, needs a rear view. - **Tail and hindquarters.** The tail set, the width of the hips and the shape of the coat at the back are visible here. - **Layered scenes.** A dog seen from behind fits a foreground spot, with a person or a landscape ahead of it. Every breed from this angle is at [/views/rear](/views/rear). ## Three-quarter views: what they are for A three-quarter view turns the dog partway, so you see the front or the rear plus one side. The site has four: front left, front right, rear left and rear right. These angles carry more depth than a straight front or a flat profile, which is why they are common in product shots and character art. A front three-quarter view usually feels livelier than a straight front. A rear three-quarter view shows the back and one flank at once. Pick left or right by the direction the dog should face in your layout. For text next to the dog, a dog turned toward the text pulls the eye to it. ## How the views line up All nine views in a set share a standard. The canvas is square, the dog fills at most 80% of it and is centered, and the standing views share one ground line. That means you can place the front and rear of one dog beside each other and the feet stay level. The head portrait is the exception to the standing pose, so do not use it for body proportions. ## A short workflow 1. Open a breed page such as [French Bulldog](/dogs/french-bulldog) and look at the nine views. 2. Save the angles you need. 3. Keep the credit link beside any free preview you publish. The free files are transparent WebP previews at 512 and 1024 pixels. Full-size 2048 pixel PNG files need a paid plan, listed on [pricing](/pricing). Because the images are generated, treat them as design material and not as proof of how a breed looks in real life. ## Picking an angle for a job Start from what the picture must do. If the dog should greet the viewer, use the front view. If it should lead the eye into a scene, use a rear or rear three-quarter view. If you need a lively product shot, a front three-quarter view is a good first try. If you need to measure the body, go to a profile. Many projects use more than one angle. A character sheet for a game, a set of stickers or a pet profile screen can show the front, a three-quarter view and the head portrait together. Because the framing is shared, the pictures sit well in one row. Keep one rule for mirrored layouts. The left and right three-quarter views are separate illustrations, so use the real one for each direction instead of flipping a single image. Small details, like a marking on one flank, may differ between the two. ## Questions **How many views does each dog have?** Nine: front, front three-quarter left, left side, rear three-quarter left, rear, rear three-quarter right, right side, front three-quarter right and a head portrait. **Where are the front and rear galleries?** At /views/front and /views/rear on the site. Each shows every breed from that one angle. **Is the head portrait part of the same ground line?** No. The standing views share one ground line. The head portrait is the exception to the standing pose. **Are these photos of real dogs?** No. They are AI-generated illustrations and not a breed standard, so check fine details against a real source. --- Source: https://dogimage.dev/guides/dog-image-api-curl-examples # Dog Image API curl examples Copy-paste curl recipes for the Dog Image API: list breeds, filter with jq, follow preview redirects, download full-size PNGs, read headers, see every error. Every Dog Image API call works from a terminal with `curl`. Breed lists and previews need no key. Full-size PNGs and usage need an `Authorization: Bearer` header. This cookbook gives one short command per task, with the output you should expect, and ends with every error reply. Set two variables once, so the commands below stay short. ```bash BASE=https://dogimage.dev export DOG_IMAGE_API_KEY="paste-your-key-here" # only needed from the full-size section on ``` ## List the breeds ```bash curl -s "$BASE/api/v1/breeds" | head -c 300 ``` The reply is JSON with a `views` list and a `breeds` list. No key is needed, and browsers on other sites may call this endpoint too. ### Pipe it to jq Count the breeds, without hardcoding a number anywhere: ```bash curl -s "$BASE/api/v1/breeds" | jq '.breeds | length' ``` Print every slug, one per line: ```bash curl -s "$BASE/api/v1/breeds" | jq -r '.breeds[].slug' ``` Find the breeds that come in more than one color, with how many variants each has: ```bash curl -s "$BASE/api/v1/breeds" \ | jq -r '.breeds[] | select(.variants | length > 1) | "\(.slug) \(.variants | length)"' ``` On the day of writing this prints seven breeds, from `french-bulldog` to `great-dane`. List the nine view ids: ```bash curl -s "$BASE/api/v1/breeds" | jq -r '.views[].id' ``` Print the crossbreeds only: ```bash curl -s "$BASE/api/v1/breeds" | jq -r '.breeds[] | select(.crossbreed) | .name' ``` ## Look at one breed ```bash curl -s "$BASE/api/v1/breeds/pug" | jq '{name, default_variant, summary: .summary.title}' ``` The reply carries a `summary` (which may be null), and for each variant a `views` object with `preview_512`, `preview_1024` and `full` links. Pull out the colors of a breed and the link to one full-size file: ```bash curl -s "$BASE/api/v1/breeds/golden-retriever" | jq -r '.variants[].slug' curl -s "$BASE/api/v1/breeds/pug" | jq -r '.variants[0].views.head.full' ``` The `full` link is a pointer to the full-size endpoint. Opening it still needs your key. ## Download a free preview The preview endpoint redirects. Without `-L`, curl shows the redirect and stops. ```bash curl -s -i "$BASE/api/v1/images/pug/default/front?size=512" | head -n 3 ``` You see `HTTP/2 302` and a `location:` line that points at a `.webp` file. With `-L`, curl follows it: ```bash curl -sSL "$BASE/api/v1/images/pug/default/front?size=512" -o pug-front-512.webp file pug-front-512.webp ``` Sizes are `512` and `1024`, and `1024` is the default. A preview is a transparent WebP, and showing one on a page requires the credit link from the [license](/license). ## Download a full-size PNG ```bash curl -sS --fail-with-body \ -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ "$BASE/api/v1/images/pug/default/front?size=full" \ -o pug-front.png ``` The file is a 2048 x 2048 transparent PNG, about 1 to 7 MB. `--fail-with-body` makes curl exit with an error code on a 4xx or 5xx, so your shell script can tell success from failure. Download all nine views of one breed, using the view ids from the breeds list: ```bash for view in $(curl -s "$BASE/api/v1/breeds" | jq -r '.views[].id'); do curl -sS --fail-with-body \ -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ "$BASE/api/v1/images/beagle/default/$view?size=full" \ -o "beagle-$view.png" || break done ``` The `|| break` stops the loop on the first failure, such as a 429, instead of saving eight more error bodies. ## Check usage ```bash curl -s -H "Authorization: Bearer $DOG_IMAGE_API_KEY" "$BASE/api/v1/usage" | jq ``` ```json { "month": "2026-11", "plan": "starter", "limit": 200, "used": 12, "remaining": 188 } ``` That sample is the documented shape. On the All images plan, `limit` and `remaining` are `null`. ## Read the response headers `-D -` writes the headers to the terminal, and `-o` keeps the binary body out of the way: ```bash curl -sS -D - -o pug-front.png \ -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ "$BASE/api/v1/images/pug/default/front?size=full" ``` A good reply includes `Content-Type: image/png`, a `Content-Disposition` header with a file name such as `pug-fawn-front.png`, and `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. On the All images plan the quota values read `unlimited`. To keep only the quota lines, write the headers to a file and filter it: ```bash curl -sS -D headers.txt -o pug-front.png \ -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ "$BASE/api/v1/images/pug/default/front?size=full" grep -i '^x-quota' headers.txt ``` To get only the status code of a call, use `-w`: ```bash curl -s -o /dev/null -w '%{http_code}\n' "$BASE/api/v1/images/pug/default/front?size=512" ``` That prints `302`, because `-L` was not set. ## See every error Errors are JSON in the shape `{"error": {"code": "...", "message": "..."}}`. These three you can reproduce right now with no key: ```bash curl -s "$BASE/api/v1/images/pug/default/front?size=99" ``` ```json {"error":{"code":"bad_size","message":"size must be one of 512, 1024, full.","choices":["512","1024","full"]}} ``` ```bash curl -s "$BASE/api/v1/images/pug/default/front?size=full" ``` ```json {"error":{"code":"missing_or_invalid_key","message":"Full-size files need an API key: Authorization: Bearer ."}} ``` ```bash curl -s "$BASE/api/v1/images/nope/default/front" ``` ```json {"error":{"code":"unknown_breed","message":"No breed \"nope\". GET /api/v1/breeds lists them."}} ``` The 404 family also includes `unknown_variant` and `unknown_view`, and on the image endpoint each lists the valid choices in the reply. The breed detail endpoint uses the same JSON error: ```bash curl -s -w '\n%{http_code}\n' "$BASE/api/v1/breeds/nope" ``` ```json {"error":{"code":"unknown_breed","message":"No breed \"nope\". GET /api/v1/breeds lists them."}} 404 ``` The other codes need an account state you will meet in real use: | Status | Code | Meaning | |---|---|---| | 402 | `no_active_plan` | The key is valid but has no active plan | | 429 | `quota_used_up` | The monthly full-size count is spent | | 502 | `file_unavailable` | Not counted. Try again | | 503 | `plan_check_failed` | Try again | Pick a retry policy in the [errors and quotas reference](/guides/dog-image-api-errors-and-quotas). The [docs](/docs) list every endpoint. ## Questions **How do I make curl follow the preview redirect?** Add -L. The preview endpoint answers with a 302 to a WebP file, and -L makes curl fetch the file itself. Add -o to name the output. **How do I see the quota headers with curl?** Use -D - to print the response headers, with -o to send the image body to a file. Look for X-Quota-Limit, X-Quota-Used and X-Quota-Remaining. **Why does curl save a small JSON file instead of my PNG?** The request failed and the error body was saved under your file name. Add --fail-with-body so curl returns a non-zero exit code and still prints the JSON error. **Is the preview image safe to use in a project?** Previews are free with a visible credit link next to the image or on the same page. They are AI-generated illustrations, not photos. --- Source: https://dogimage.dev/guides/dog-png-transparent-background # Dog PNG images with a transparent background Where to get dog images with a transparent background: free WebP previews at 512 and 1024 pixels, 2048 pixel PNG files on a paid plan, and how to use them. A transparent dog image is a picture of a dog with no background, so it sits on any color, photo or pattern behind it. This site offers free transparent WebP previews at 512 and 1024 pixels, and transparent PNG files at 2048 pixels on a paid plan. ## What a transparent dog image is good for A flat background is the part of a picture that fights with your layout. When the dog has no background, you choose what goes behind it. - **Mockups.** Put a dog on a pet shop poster, a packaging render or a social post without cutting it out first. - **Cards and invitations.** Place the same dog on a colored card, then try three colors in a minute. - **Apps and sites.** Show a breed picker, an onboarding screen or an empty state where the dog floats over your own interface. - **Stickers and badges.** A clean outline is the starting point for a sticker sheet or a rounded badge. ## What the site offers, honestly There are two kinds of files, and only one is free. | | Free preview | Full-size file | |---|---|---| | Format | WebP | PNG | | Size | 512 x 512 and 1024 x 1024 pixels | 2048 x 2048 pixels | | Transparent | Yes | Yes (RGBA, sRGB) | | Cost | Free | Paid plan, see [pricing](/pricing) | | Credit link | Required | Not needed | The images are AI-generated illustrations of 298 breeds, not photographs. If you need a PNG without paying, you can make one yourself from a preview, as shown below. The [license](/license) is the reference for what you may do with each kind of file. ## Save a preview from a breed page 1. Open [the list of breeds](/dogs) and pick one, for example [Golden Retriever](/dogs/golden-retriever). 2. Find the view you want. Each breed page shows nine views, such as Front, Left side and Head portrait. 3. Save the image from your browser, or use the command the page gives you to write the file to disk. 4. Copy the credit snippet from the same page and put it next to the image. It looks like this: ```html Golden Retriever image: Dog Image API ``` Some breeds have several colors. Golden Retriever has its own pages for the extra colors, so check the breed menu before you save. ## Place the dog on any background with CSS Because the transparency is in the file, the background comes from your own styles. Here the dog sits on a soft green card, with its credit link below. ```html
    Pug seen from the front
    Pug image: Dog Image API
    ``` Change the `background` value and the dog stays the same. A gradient or a photo works too. You can also add a soft shadow with `filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.25))` on the image, which follows the outline of the dog rather than the square box. ## Convert a WebP preview to PNG yourself Some tools still want PNG. The preview already has the alpha channel, so conversion keeps the transparent background. The pixel size stays the same: a 1024 pixel preview becomes a 1024 pixel PNG. It is not a 2048 pixel file, and converting does not add detail. With `dwebp`, a command line tool from the WebP project (see its [manual](https://developers.google.com/speed/webp/docs/dwebp)), PNG is the default output: ```bash dwebp pug-fawn-front.webp -o pug-fawn-front.png ``` With Python and the Pillow library, if your Pillow build includes WebP support: ```python from PIL import Image Image.open("pug-fawn-front.webp").save("pug-fawn-front.png") ``` Open the result in an editor on a checkerboard or colored layer to confirm the background is still clear. If the output has a white or black box, the tool you used flattened the image. Try another one. ## Which file to pick For a web page, a mockup draft or a small app screen, the 1024 pixel preview is enough, and it is free with a credit. For print, packaging or large screens, you need pixels, so the 2048 pixel PNG on a plan is the better fit. The [WebP vs PNG guide](/guides/webp-vs-png-for-transparent-images) explains the format side in more detail. ## Questions **Can I download a dog PNG for free?** No. The free files are transparent WebP previews at 512 and 1024 pixels, and they need a visible credit link. PNG files at 2048 pixels come with a paid plan. **Do the previews really have a transparent background?** Yes. Every image in the catalog is made with a transparent background, and the previews keep it. They are AI-generated illustrations, not photos. **Does the dog fill the whole canvas?** No. The canvas is square and the dog fills at most 80% of it, centered. That leaves room around the dog when you crop or scale. **Can I use a WebP preview in an app or a design tool?** Most current browsers and many design tools read WebP. If one of your tools only accepts PNG, convert the preview to PNG on your own computer. --- Source: https://dogimage.dev/guides/dog-reference-images-for-games-and-3d # Dog reference images for games and 3D modeling Use nine-view dog illustrations as turnaround reference for 3D models and sprites: what the angles give you, the honest limits and the license rules. Each set in this catalog shows one adult dog from nine views: eight body angles around the animal and a head portrait, all on a transparent square canvas. That is a ready turnaround sheet. Use it to block out a 3D model or plan sprite rotations, then check details against real references. ## What the nine views give you The eight body views go around the dog, four straight angles and four three-quarter angles between them: `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right` and `front-right`. The ninth, `head`, is a portrait. For modeling, that means: - **Side views (`left` and `right`)** show the profile line: back, chest, belly tuck, tail carriage and leg angles. Most modelers start here. - **Front and rear** show width, chest depth and leg spacing, and the symmetry you will mirror in your mesh. - **The four three-quarter views** show how the head sits on the neck and how the shoulders and hips turn. They catch things a flat profile hides. - **The head portrait** gives the skull shape, ear set and muzzle length, so you can model the face apart from the body. The standing views share one ground line. That helps when you stack them in a modeling program, because the feet land at the same height from view to view. ## Setting up the views in your tool Download the eight body views at full size. They are 2048 pixel PNGs with a transparent background, so you can put them on image planes without cutting anything out. Run the download on a server or in a build step with your key in `DOG_IMAGE_API_KEY`: ```bash for view in front front-left left rear-left rear rear-right right front-right head; do curl -sS -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ -o "beagle-$view.png" \ "https://dogimage.dev/api/v1/images/beagle/default/$view?size=full" done ``` That is nine images, one set of one breed. To try the layout first, load the free 1024 pixel previews instead: request the same URLs with `size=1024`, which needs no key. For a sprite sheet, treat the eight body views as the eight facing directions of a top-down or isometric character. Redraw them at your target pixel size. Do not try to ship the images as the sprites themselves. See the license points below. Aligning views across planes takes care. Each image is a separate picture of the same animal, and they are not guaranteed to match to the pixel. Use the feet and the ground line to align height, and treat small mismatches as normal. ## Honest limits These matter more here than in most uses. - **AI-generated.** The images show no real dog and are not a breed standard. Details can be wrong: toe counts, ear shape, coat patterns, or how a limb joins the body. A reference sheet that looks right at a glance can still hide an anatomy error. Verify against real photos or a veterinary or breed source before you lock a design. - **No relative scale.** Each dog fills its own frame, up to 80% of the canvas. A Chihuahua and a Great Dane may come out at a similar size on screen. Take real height and length from another source. - **Standing pose only.** There is no sitting, running or lying pose, and no action reference. Rigging and animation need other sources. - **Adult only.** There are no puppies or seniors. Treat the images as a quick, consistent starting point for form, then move to better references for the parts that matter. ## Picking a breed Browse the [breed list](/dogs) and open a breed page to see all nine views at once. The [left side gallery](/views/left) shows every breed from one angle, which helps you choose a body type. For the guide to how the views are built, see [the nine-view standard](/guides/the-nine-view-image-standard). ## License points for reference use - Previews are free for any use, commercial included, with a visible link crediting Dog Image API next to the image or on the same page. Use the credit snippet from the breed page whenever you display a preview. - Full-size files can go into your own products, with no credit. That covers a game or an app that uses them. The license does not cover reselling or sublicensing the images. - You may not republish the images as a collection, dataset or image library. That rules out an asset pack on a marketplace, a sprite pack that is the images with light edits, or a training dataset built from the catalog. - The images may not be used to build a competing image service. So: a model you build using the images as a guide is your work. A bundle that hands the images themselves to other people is not allowed. The license page is marked as a draft not yet reviewed by a lawyer, so read [the license](/license) and check with your own adviser if you plan to ship the images inside a product. ## Questions **Can I use these as texture sources for a model?** They are meant as reference. You may use full-size files inside your own product, but the license does not allow republishing the images as a collection or asset pack. Check the license page before you ship the images themselves inside a game. **Are the proportions accurate for a real breed?** Not reliably. The images are AI-generated illustrations, not a breed standard, and details such as the number of toes or coat markings can be wrong. Check anatomy against real references before you commit to a design. **Can I compare the size of two breeds with these images?** No. Each dog fills its own frame, so the images are not drawn to scale against each other. Take real measurements from another source. **Is there a puppy or a running pose?** No. The catalog has one life stage, adult, and a standing pose. The head portrait is the only view that is not a standing pose. --- Source: https://dogimage.dev/guides/dog-side-view-reference # Dog side view reference: every breed in profile Left and right side views of 298 dog breeds on a transparent background, all on one ground line, for comparing proportions, silhouettes and illustration. A clean side profile shows the whole outline of a dog: back line, chest, legs, tail and head carriage. This site has a left side view and a right side view for every breed, on a transparent background, with the same framing and ground line in each set. ## Why a profile view is useful The side view is the one most people reach for first, because nothing hides behind the dog. - **Size and shape comparison.** Place two breeds next to each other and compare leg length against body length, or the depth of the chest against the height at the shoulder. - **Silhouettes.** A profile reduces to a clear shape. Fill it with one color to get a logo, a sign or an icon base. - **Illustration reference.** Many illustrators start a dog drawing from a profile because the proportions are easiest to read from the side. - **Layout work.** A dog walking across a banner, a sticker in a row, or a mascot on a loading bar all want a profile. ## Where to find the views There are two ways in. - **A gallery for one angle.** [/views/left](/views/left) and [/views/right](/views/right) show every breed from that side. Scroll through them to compare breeds quickly. - **A page for one breed.** Each breed page, for example [Samoyed](/dogs/samoyed) or [Dachshund](/dogs/dachshund), holds the nine views of one dog, including Left side and Right side. Breeds with several colors have an extra page per color. The complete list of breeds is at [/dogs](/dogs). ## Why the proportions are comparable Every set on the site is made to one standard, and that standard is what makes profiles useful side by side: - The canvas is square. - The dog fills at most 80% of the canvas and is centered. - Standing views share one ground line, so feet land at the same height in each of the nine views of a set. - The dog is an adult in a standing pose. Two limits matter here. The framing is shared, but the scale is not absolute. Each dog is fitted to its own canvas, so a tall breed and a small breed do not show their real size difference. Use the images to compare shape and proportion, such as the ratio of leg to body, and not to measure real height. The second limit is the origin. These are AI-generated illustrations of adult dogs, not photos and not a breed standard. If a detail matters, such as ear set or tail carriage, check it against a real source. ## A quick way to compare two breeds 1. Open the left side view of the first breed and save it. 2. Open the left side view of the second breed and save it. 3. Place both on one canvas in your editor, with the feet on one line. 4. Look at the back line, the length of the legs against the body, and the angle of the neck. If you use the free previews, keep the credit link next to the images: ```html Beagle image: Dog Image API ``` Choose the left view for dogs that should face left on your layout, and the right view for dogs that should face right. You can mirror an image in your editor, but a mirrored image is not the same illustration as the real right view, so use the true right view when the small details matter. ## Left and right in a set Each set also has front and rear views and four three-quarter angles. The [front and rear view guide](/guides/dog-front-and-rear-view-reference) covers those. For the free and paid files, see [pricing](/pricing), and for the exact terms, read the [license](/license). ## Using profiles in a design A few habits make profile images easier to work with. - **Keep the feet level.** When you line up several breeds in a row, align the bottom of each canvas, not the bottom of each dog. The shared ground line then does the work for you. - **Crop the canvas, not the dog.** The square canvas leaves empty space around the dog. Trim it in your editor if the layout needs a tighter box, and leave a little room so the tail or ears are not cut. - **Scale as a group.** If you resize one image, resize them all by the same factor. The set stays comparable only while the scale is shared. - **Name your files.** A name such as `samoyed-left.webp` saves time when you come back to a folder of thirty profiles. For a silhouette, a transparent background helps: you can fill the shape with a flat color in one step. ## Questions **Where do I find the left and right side views?** Open /views/left or /views/right for every breed from that angle. You can also open any breed page, where the Left side and Right side images are two of the nine views. **Are the dogs shown to scale against each other?** No. Each dog is framed to fill at most 80% of its square canvas, so a Great Dane and a Pug are not drawn at true relative size. The framing is the same, which makes proportions comparable. **Are these real dogs?** No. They are AI-generated illustrations. They show no real dog and are not a breed standard. **Can I use the side views commercially?** Yes, with conditions. Free previews need a visible credit link, and full-size files from a paid plan do not. The license page has the exact terms. --- Source: https://dogimage.dev/guides/go-download-dog-breed-images # Download dog breed images with Go A Go program using only net/http: list dog breeds, decode the JSON, download full-size PNGs with a key, read the quota header, and handle 429, 502 and 503. In Go, you can download dog breed images with the standard library alone: `net/http` to call the API, `encoding/json` to decode the breed list into structs, and a bearer key for the full-size PNGs. The program below reads the quota header, retries 502 and 503, and stops on 429. ## What the program does Dog Image API is a catalog of AI-generated dog illustrations. Each set shows one dog in nine views on a transparent background. The breed list is public, so decoding it needs no key. A full-size file is a 2048 x 2048 PNG of about 1 to 7 MB, and fetching it needs a key and a plan. Set the key in the environment of the machine that runs the program. It never goes into a client app. ```bash export DOG_IMAGE_API_KEY="paste-your-key-here" ``` Make a folder, run `go mod init dogs`, and save the code below as `main.go`. It needs Go 1.21 or newer and no dependencies. ## The full program The structs at the top match the JSON names, including `default_variant`. Go ignores fields a struct does not name, so you can add more later without breaking the decode. ```go package main import ( "encoding/json" "errors" "fmt" "io" "net/http" "os" "path/filepath" "strconv" "time" ) const base = "https://dogimage.dev" var client = &http.Client{Timeout: 60 * time.Second} type View struct { ID string `json:"id"` Label string `json:"label"` } type Variant struct { Slug string `json:"slug"` Name string `json:"name"` Kind string `json:"kind"` } type Breed struct { Slug string `json:"slug"` Name string `json:"name"` Crossbreed bool `json:"crossbreed"` DefaultVariant string `json:"default_variant"` Variants []Variant `json:"variants"` } type Catalog struct { Views []View `json:"views"` Breeds []Breed `json:"breeds"` } // errQuotaUsedUp means the monthly count is spent. Retrying will not help. var errQuotaUsedUp = errors.New("quota_used_up") // apiError turns a non-200 reply into an error with the API's code and message. func apiError(resp *http.Response) error { body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<16)) var payload struct { Error struct { Code string `json:"code"` Message string `json:"message"` } `json:"error"` } if json.Unmarshal(body, &payload) == nil && payload.Error.Code != "" { return fmt.Errorf("%d %s: %s", resp.StatusCode, payload.Error.Code, payload.Error.Message) } return fmt.Errorf("%d %s", resp.StatusCode, http.StatusText(resp.StatusCode)) } func listBreeds() (*Catalog, error) { resp, err := client.Get(base + "/api/v1/breeds") if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, apiError(resp) } var catalog Catalog if err := json.NewDecoder(resp.Body).Decode(&catalog); err != nil { return nil, err } return &catalog, nil } // downloadFull saves one full-size PNG and returns the X-Quota-Remaining header. func downloadFull(key, breed, variant, view, dir string) (string, error) { url := fmt.Sprintf("%s/api/v1/images/%s/%s/%s?size=full", base, breed, variant, view) const attempts = 4 for attempt := 1; ; attempt++ { req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return "", err } req.Header.Set("Authorization", "Bearer "+key) resp, err := client.Do(req) if err != nil { return "", err } switch { case resp.StatusCode == http.StatusOK: remaining := resp.Header.Get("X-Quota-Remaining") err := savePNG(resp.Body, filepath.Join(dir, fmt.Sprintf("%s-%s-%s.png", breed, variant, view))) resp.Body.Close() return remaining, err case resp.StatusCode == http.StatusTooManyRequests: err := apiError(resp) resp.Body.Close() return "", fmt.Errorf("%w (%v)", errQuotaUsedUp, err) case (resp.StatusCode == http.StatusBadGateway || resp.StatusCode == http.StatusServiceUnavailable) && attempt < attempts: resp.Body.Close() time.Sleep(time.Duration(1< null); const err = body?.error; return err ? `${res.status} ${err.code}: ${err.message}` : `${res.status} ${res.statusText}`; } async function exists(file) { try { await access(file); return true; } catch { return false; } } async function getUsage() { const res = await fetch(`${BASE}/api/v1/usage`, { headers: auth }); if (!res.ok) throw new Error(await describe(res)); return res.json(); } async function fetchFull(breed, view) { const url = `${BASE}/api/v1/images/${breed}/${variant}/${view}?size=full`; for (let attempt = 1; attempt <= 3; attempt++) { const res = await fetch(url, { headers: auth }); if (res.ok) return Buffer.from(await res.arrayBuffer()); if (res.status === 429) return null; // quota is spent, tell the caller to stop if ((res.status === 502 || res.status === 503) && attempt < 3) { await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt)); continue; } throw new Error(await describe(res)); } } // 1. Build the list of files that are not on disk yet. const wanted = breeds.flatMap((breed) => views.map((view) => ({ breed, view }))); const todo = []; for (const job of wanted) { const file = path.join(outDir, job.breed, variant, `${job.view}.png`); if (!(await exists(file))) todo.push({ ...job, file }); } console.log(`${wanted.length - todo.length} already saved, ${todo.length} to fetch.`); // 2. Check the plan before spending anything. const usage = await getUsage(); if (usage.plan === null) { console.error("This key has no active plan. Full-size downloads are not available."); process.exit(1); } const room = usage.remaining ?? Infinity; // remaining is null on the All images plan console.log(`Plan ${usage.plan}, month ${usage.month}: used ${usage.used}, remaining ${usage.remaining ?? "unlimited"}.`); const batch = todo.slice(0, room); if (batch.length < todo.length) { console.warn(`Only ${batch.length} of ${todo.length} fit in this month's plan.`); } // 3. Download one at a time and write each file as soon as it arrives. for (const { breed, view, file } of batch) { const png = await fetchFull(breed, view); if (png === null) { console.error("Quota used up. Stopping. Saved files are kept."); break; } await mkdir(path.dirname(file), { recursive: true }); await writeFile(file, png); console.log("saved", file); } ``` Run it with `node download.mjs`. Run it again and the first line reports that everything is already saved. ## What to notice ### The order of the three steps The script builds the work list first, then reads usage, then downloads. The list is what you would spend, so the usage check compares like with like. A re-run builds a shorter list, and a month with fewer images left trims the batch. ### The usage reply `GET /api/v1/usage` returns `month`, `plan`, `limit`, `used` and `remaining`. On the All images plan, `limit` and `remaining` are `null`, which is why the script falls back to `Infinity`. With no plan, `plan` is `null` and `limit` is 0. A `401` here means the key is missing or wrong. One caveat: the count is of distinct images, and `todo` is made of files missing on disk, not images missing from your quota. If you deleted a file you fetched earlier this month, the new request is free. The check errs on the safe side. ### Sequential on purpose The loop awaits each request before the next. The API states no per-request rate limit, and the quota is the real ceiling, so there is no gain in firing hundreds of requests at once. Sequential code also makes the stop on 429 exact. ### Retry only what can recover A `502` means the file was unavailable and was not counted. A `503` means the plan check failed. Both are worth a short wait and another try. The script gives up after three attempts and throws the readable message. A `404` lists the valid choices in its reply, which makes a typo in a slug easy to fix. See the [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) for every code. ## Check a breed before you ask for it Colors vary by breed. If you want a color other than the default, read `GET /api/v1/breeds/{breed}` first. It needs no key and lists each variant with its views. Compare that with the catalog on the [dogs page](/dogs), or the [left side gallery](/views/left) to see one angle across every breed. ## After the download Store the files and serve them from your own host. The [license](/license) allows use inside your own products and forbids republishing the images as a collection. The [pricing page](/pricing) shows what each plan includes. ## Questions **Which Node.js version do I need?** Node 20 or newer. The script uses the built-in fetch and the node:fs/promises module, so it has no dependencies. **Why check /api/v1/usage before downloading?** It tells you the plan and how many full-size images remain this month. You can then trim a long list before the first request instead of hitting a 429 halfway through. **Can I call the full-size endpoint from browser code?** No. The full-size endpoint needs your key, and a key in browser code is public. Run the download in a Node script, a build step or a server. **Are the dog images photos?** No. They are AI-generated illustrations of a dog in nine standard views on a transparent background. Each set passes an automatic check before it is published. --- Source: https://dogimage.dev/guides/python-download-dog-breed-images # Download dog breed images with Python A Python requests script for the Dog Image API: list breeds, save free previews, download full-size PNGs with a key, read quota headers, and retry safely. You can download dog breed images with Python in one small script built on the `requests` library. Previews need no key and arrive as transparent WebP files. Full-size 2048 x 2048 PNGs need a key and a plan, and the script below reads the quota headers, retries 502 and 503, and stops on 429. ## Set up Install `requests` and put your key in the environment. Create the key on the account page after you sign in. It is shown once, so store it somewhere safe. ```bash pip install requests export DOG_IMAGE_API_KEY="paste-your-key-here" ``` Run the script on a server or your own machine. Never ship the key inside a web page or a mobile app. The images are AI-generated illustrations of one dog per set, in nine views, on a transparent background. ## The whole script Save this as `fetch_dogs.py`. It downloads three previews and three full-size files for each of three breeds. ```python import os import sys import time from pathlib import Path import requests BASE = "https://dogimage.dev" RETRY_CODES = {502, 503} class QuotaUsedUp(Exception): """The monthly full-size count is spent. Retrying will not help.""" def describe(resp): try: err = resp.json()["error"] return f"{resp.status_code} {err['code']}: {err['message']}" except (ValueError, KeyError): return f"{resp.status_code} {resp.reason}" def list_breeds(): resp = requests.get(f"{BASE}/api/v1/breeds", timeout=30) resp.raise_for_status() return resp.json() def download_preview(breed, variant, view, out_dir, size="1024"): url = f"{BASE}/api/v1/images/{breed}/{variant}/{view}" resp = requests.get(url, params={"size": size}, timeout=30) # follows the 302 if resp.status_code != 200: raise RuntimeError(describe(resp)) path = out_dir / f"{breed}-{variant}-{view}-{size}.webp" path.write_bytes(resp.content) return path def download_full(breed, variant, view, out_dir, attempts=4): url = f"{BASE}/api/v1/images/{breed}/{variant}/{view}" headers = {"Authorization": f"Bearer {os.environ['DOG_IMAGE_API_KEY']}"} for attempt in range(1, attempts + 1): resp = requests.get(url, params={"size": "full"}, headers=headers, timeout=60) if resp.status_code == 200: break if resp.status_code == 429: raise QuotaUsedUp(describe(resp)) if resp.status_code in RETRY_CODES and attempt < attempts: time.sleep(2 ** attempt) continue raise RuntimeError(describe(resp)) path = out_dir / f"{breed}-{variant}-{view}.png" path.write_bytes(resp.content) return path, resp.headers.get("X-Quota-Remaining") def main(): breeds = ["pug", "beagle", "samoyed"] views = ["front", "left", "head"] out_dir = Path("dogs") out_dir.mkdir(exist_ok=True) catalog = list_breeds() known = {b["slug"] for b in catalog["breeds"]} print(f"The catalog lists {len(known)} breeds.") for breed in breeds: if breed not in known: sys.exit(f"Unknown breed slug: {breed}") for view in views: print("preview", download_preview(breed, "default", view, out_dir)) for breed in breeds: for view in views: try: path, remaining = download_full(breed, "default", view, out_dir) except QuotaUsedUp as err: print(f"Stopping: {err}") return print("full", path, "remaining this month:", remaining) if __name__ == "__main__": main() ``` Run it with `python fetch_dogs.py`. ## How each part works ### Listing breeds `GET /api/v1/breeds` returns a `views` list and a `breeds` list. Each breed has a `slug`, a `name`, a `default_variant` and its `variants`. The script reads the slugs from that reply instead of hardcoding the catalog size, because the live count changes as sets are added. Seven breeds have several colors. For those, `default` is the breed's main set, and a color slug such as `blue` picks another one. The catalog lists every slug. Browse them on the [breed index](/dogs) or on a single page such as [/dogs/pug](/dogs/pug). ### Previews without a key A preview request answers with a `302` redirect to a WebP file. The `requests` library follows redirects for `GET`, so `resp.content` is the image itself. Valid sizes are `512` and `1024`, and `1024` is the default. If you show a preview on a page, add the credit link the license asks for. Each breed page prints the snippet: ```html Pug image: Dog Image API ``` ### Full-size PNGs with a key Full-size files are PNG, transparent, 2048 x 2048 pixels, and about 1 to 7 MB each. The script sends `Authorization: Bearer ` and writes the bytes to disk. Files you download are meant to be stored on your side, so do not link to the API from a live page. ### Reading the quota headers Every successful full-size reply carries `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. On the All images plan the value is the string `unlimited`, so do not cast it to an integer without a check. If you want to act on the number, do it like this: ```python remaining = resp.headers.get("X-Quota-Remaining") if remaining not in (None, "unlimited") and int(remaining) < 10: print("Fewer than 10 images left this month.") ``` ### Stopping on 429, retrying 502 and 503 A `429` carries the code `quota_used_up`. The script raises `QuotaUsedUp`, prints it, and returns. Files already saved stay on disk. A `502` (`file_unavailable`) and a `503` (`plan_check_failed`) are the two codes to retry. The loop waits 2, 4, then 8 seconds. A 502 is not counted against your quota. Every other error, such as `401`, `402` or `404`, stops with a readable message, because trying again will not change the answer. The full list is in the [errors and quotas reference](/guides/dog-image-api-errors-and-quotas). ## Budget before you loop Each distinct full-size image uses one unit of your plan for the month. Starter allows 200 and Pro allows 700. Check the plan before a large run, as shown in the [Node.js version of this guide](/guides/nodejs-download-dog-images), or read `/api/v1/usage` with the same header. The [pricing page](/pricing) lists the plans, and the [license](/license) says what you may do with the files. ## Questions **Do I need an API key to download dog images with Python?** Not for previews. The breed list, the breed detail and the 512 and 1024 pixel WebP previews are keyless. Only full-size PNGs (size=full) and the usage endpoint need a key. **Does downloading the same full-size image twice use my quota twice?** No. A plan counts distinct full-size images per calendar month (UTC), so fetching the same image again in the same month is free. A failed 502 fetch is not counted either. **What should a Python script do on a 429?** Stop. The 429 quota_used_up error means the monthly count is spent, and retrying does not help. Save what you have and run again after the month rolls over or on a larger plan. **Can I use the preview images without paying?** Yes, with a visible link that credits Dog Image API next to the image or on the same page. The images are AI-generated illustrations, so describe them that way. --- Source: https://dogimage.dev/guides/free-dog-images-for-commercial-use # Free dog images for commercial use: the terms What the free Dog Image API previews allow, what the credit link requires, what the paid full-size license adds, and what is never allowed. Plain words. Yes, you can use the free previews in commercial work. The condition is a visible link that credits Dog Image API, placed next to the image or on the same page. The paid full-size files drop that condition. Reselling the images as a collection is not allowed on any plan. This page explains the terms in plain words. It is not legal advice. The [license page](/license) is the reference. ## The draft notice The license page is marked as a draft. A lawyer has not reviewed it yet. We describe it as it stands today and promise nothing beyond what it says. If your use is large, unusual or risky for your business, ask your own lawyer before you rely on it. ## Free previews The free files are WebP images, transparent, at 512 x 512 and 1024 x 1024 pixels. **Allowed** - Any use, commercial use included: a shop page, a mobile app, a flyer, a slide deck, a client mockup. - Hot-linking the preview files. **Required** - A visible link that credits Dog Image API, next to the image or on the same page. Each [breed page](/dogs/pug) gives the snippet in this form: ```html Pug image: Dog Image API ``` Change the breed slug and name for each breed you show. If you show many breeds on one page, a visible credit on that page covers the page. Make sure a visitor can see it without hunting for it. ## Paid full-size files The full-size files are transparent PNGs at 2048 x 2048 pixels. They come with a paid plan, listed on the [pricing page](/pricing). **Allowed** - Use in your own products: apps, sites, documents, print and packaging. - No credit needed. - Files you fetched while a plan was active stay usable in the things you built with them. **What the plan adds** - Higher resolution and PNG instead of WebP. - No credit link. A plan counts distinct full-size images per month. Download a file once and store it on your side. Do not call the API from a page to show a full-size image to visitors. ## What is not allowed The license lists these limits for the full-size files. The terms of use also say not to copy the site or the API to rebuild the image set. - Reselling the images. - Sublicensing them. - Republishing them as a collection, a dataset or an image library. - Using them to build a competing image service. Putting a dog on a product you make is a different thing from selling the dog. A poster that shows a Beagle is a product. A zip file of 100 Beagle images is a collection. When your idea sits between the two, read the license page and ask your lawyer. ## Where the text around the images comes from Breed names, color names and summaries come from Wikidata (CC0) and Wikipedia (CC BY-SA 4.0). The catalog JSON is shared under CC BY-SA 4.0. If you reuse that text, follow the terms of those licenses. ## About the images themselves The pictures are AI-generated illustrations. Every set shows one dog from nine views, passes an automatic check before it is published, and is reviewed by a person over time. They are not photographs, and they are not a breed standard. If your use needs real photos of real dogs, for example a veterinary claim, use real photos. The guide on [AI-generated dog images](/guides/ai-generated-dog-images-what-to-know) covers this in more detail. ## Common cases - **A shop page with dogs on it.** Previews are fine with the credit link on the page. Paid files are fine without it. - **A client mockup.** A private draft is your working file. Once it is published, the preview rules apply to the published page. - **A mobile app.** Show previews with a visible credit link on the same screen as the images, or use paid files, which need no credit, and download them into your app build. - **A printed poster or packaging.** Use the paid 2048 pixel PNG. A 1024 pixel preview is too small for most print sizes, and print is a place where a credit link is awkward. - **A clip art pack, an image library or a published dataset.** Not allowed, as described above. ## A short checklist 1. Using a preview? Add the credit link on the same page. 2. Printing or shipping a product? Use the paid full-size PNG. 3. Planning an image library, a published dataset or a competing service? That is not allowed. 4. Not sure? Read the [license page](/license), then ask a lawyer. ## Questions **Can I use the free previews in a commercial product?** Yes. The previews can be used for any purpose, commercial use included, as long as a visible link credits Dog Image API next to the image or on the same page. **Where does the credit link go?** Next to the image or on the same page. Each breed page shows the snippet to copy. **Can I sell the images or put them in my own image library?** No. Reselling, sublicensing or republishing the images as a collection, dataset or image library is not allowed, and neither is using them to build a competing image service. **Is this legal advice?** No. This page explains the terms in plain words. The license page is the reference, and it is marked as a draft that a lawyer has not reviewed. **Are these real dog photos?** No. They are AI-generated illustrations. They show no real dog and are not a breed standard. --- Source: https://dogimage.dev/guides/openapi-client-dog-image-api # Generate a typed client from the OpenAPI file Turn the Dog Image API /openapi.json file into TypeScript types with openapi-typescript, then wrap fetch in a small typed client that handles errors well. Run `openapi-typescript` on `https://dogimage.dev/openapi.json` to get TypeScript types for every path, reply and error. Then write a small `fetch` wrapper that uses those types. You end up with a client where a wrong view name or size fails at compile time, with no SDK to install. ## What the file contains Read it before you generate anything, so you know what the types cover. The file is OpenAPI 3.1.0, titled "Dog Image API", version 1.0.0, with one server: the production URL. It has four operations: | operationId | Path | Notes in the file | |---|---|---| | `listBreeds` | `/api/v1/breeds` | `200` returns a `BreedList` | | `getBreed` | `/api/v1/breeds/{breed}` | `200` returns a `BreedDetail`, `404` returns the shared `Error` | | `getImage` | `/api/v1/images/{breed}/{variant}/{view}` | `view` is an enum of the nine ids, `size` is `512`, `1024` or `full` with default `1024`; replies `200`, `302`, `400`, `401`, `402`, `404`, `429`, `502`, `503` | | `getUsage` | `/api/v1/usage` | needs the key; returns a `Usage` | The schemas are `BreedList`, `BreedSummary`, `VariantSummary`, `View`, `BreedDetail`, `VariantDetail`, `ViewLinks`, `Usage` and `Error`. The `Error` schema has an `error` object with a `code`, a `message` and an optional `choices` list for the replies that offer valid values. The full-size `200` reply declares the `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining` headers. One security scheme, `key`, is declared as HTTP bearer. The image path lists security as optional or `key`, which matches the product: previews need no key, and `size=full` does. ## Generate the types ```bash npm install --save-dev openapi-typescript typescript npx openapi-typescript https://dogimage.dev/openapi.json -o schema.d.ts ``` The command reads the live file and writes `schema.d.ts`. Commit it, and run the command again when you want to pick up changes. The generated `paths` interface gives you, for the image route, a `path` object with `breed`, `variant` and a `view` union, and a `query` object whose `size` is `"512" | "1024" | "full"`. The `components` interface gives you `BreedList`, `BreedDetail`, `Usage` and `Error`, and the `operations` interface holds the four operations by id. ## A typed wrapper Save this as `dog.ts` next to `schema.d.ts`. It has no dependencies other than Node 20 or newer, or any runtime with a global `fetch`. Every reply type comes from the generated file. ```ts import type { components, paths } from "./schema"; type ImageGet = paths["/api/v1/images/{breed}/{variant}/{view}"]["get"]; export type ImageParams = ImageGet["parameters"]["path"]; export type View = ImageParams["view"]; export type PreviewSize = "512" | "1024"; type ApiError = components["schemas"]["Error"]; export type BreedList = components["schemas"]["BreedList"]; export type BreedDetail = components["schemas"]["BreedDetail"]; export type Usage = components["schemas"]["Usage"]; export class DogImageApiError extends Error { constructor( readonly status: number, readonly code: string, message: string, ) { super(`${status} ${code}: ${message}`); } } const BASE = "https://dogimage.dev"; async function toError(res: Response): Promise { const body = (await res.json().catch(() => null)) as ApiError | null; return new DogImageApiError( res.status, body?.error?.code ?? "unknown", body?.error?.message ?? res.statusText, ); } async function getJson(path: string, key?: string): Promise { const res = await fetch(BASE + path, { headers: key ? { Authorization: `Bearer ${key}` } : {}, }); if (!res.ok) throw await toError(res); return (await res.json()) as T; } export const listBreeds = () => getJson("/api/v1/breeds"); export const getBreed = (breed: string) => getJson(`/api/v1/breeds/${encodeURIComponent(breed)}`); export const getUsage = (key: string) => getJson("/api/v1/usage", key); export async function fetchFullImage( key: string, { breed, variant, view }: ImageParams, ): Promise<{ bytes: ArrayBuffer; quotaRemaining: string | null }> { const url = `${BASE}/api/v1/images/${breed}/${variant}/${view}?size=full`; const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } }); if (!res.ok) throw await toError(res); return { bytes: await res.arrayBuffer(), // Declared as a header of the 200 reply. "unlimited" on the All images plan. quotaRemaining: res.headers.get("X-Quota-Remaining"), }; } export function previewUrl(params: ImageParams, size: PreviewSize = "1024"): string { const { breed, variant, view } = params; return `${BASE}/api/v1/images/${breed}/${variant}/${view}?size=${size}`; } ``` ### How the types help `ImageParams` is read straight from the generated `paths` type, so it stays in step with the file. If you pass `view: "side"`, the compiler stops you, because the file's enum lists only `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right`, `front-right` and `head`. The `variant` is a plain string, because the file describes it as "a color slug, or default". The `Usage` type has `limit` and `remaining` as `number | null`, which is exactly how the All images plan shows up. `toError` still catches a failed JSON parse. That is good practice for any HTTP client, because a proxy, a captive portal or a network failure can hand you an error page that the API never wrote. When that happens you get the status and the status text instead of a parse error. ## Use it ```ts import { DogImageApiError, fetchFullImage, getBreed, getUsage, listBreeds } from "./dog"; import { writeFile } from "node:fs/promises"; const key = process.env.DOG_IMAGE_API_KEY; if (!key) throw new Error("Set DOG_IMAGE_API_KEY"); const { breeds } = await listBreeds(); console.log(`${breeds.length} breeds`); const pug = await getBreed("pug"); console.log(pug.name, pug.variants.map((v) => v.slug)); console.log(await getUsage(key)); try { const { bytes, quotaRemaining } = await fetchFullImage(key, { breed: "pug", variant: "default", view: "front", }); await writeFile("pug-front.png", Buffer.from(bytes)); console.log("saved pug-front.png, remaining:", quotaRemaining); } catch (err) { if (err instanceof DogImageApiError && err.code === "quota_used_up") { console.error("Monthly limit reached"); } else { throw err; } } ``` Run it with your usual TypeScript setup, such as `tsc`, a bundler, or a runner like `tsx`. It uses top-level `await`, so the module setting needs to allow it. `previewUrl` builds a link that is safe to put in an `` tag. A displayed preview needs the credit link from the [license](/license), and the images are AI-generated illustrations. ## Keep the key off the client The generated types are fine to share, since they carry no secret. Browsers on other sites may call `listBreeds` and `getBreed` directly, and may load preview files, because cross-origin reads are allowed on those. The `fetchFullImage` function is not for a browser bundle, because it needs the key. Run it in a build step or on a server, and save the PNGs to your own storage. The [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) says which codes deserve a retry. ## Other generators `openapi-typescript` is one choice, and the wrapper above is hand-written on top of it. Other generators exist, for example OpenAPI Generator and Orval. Whichever you pick, read its output against the schemas listed above before you rely on it. The [docs page](/docs) lists the endpoints in prose. ## Questions **Where is the Dog Image API OpenAPI file?** At https://dogimage.dev/openapi.json. It is an OpenAPI 3.1.0 document with four operations, a bearer key security scheme and typed schemas for every reply. **Is there an official Dog Image API SDK?** No. There is no SDK. The OpenAPI file and a few lines of fetch are the supported way to build a client, and this guide shows one. **Which parts of the Dog Image API does the OpenAPI file describe?** Every reply: the breed list, one breed, the image endpoint, usage and the shared Error. It also declares the X-Quota headers on the full-size reply, so generated types cover them. **Can I put the generated client in browser code?** The breed list and detail calls can run in a browser. The full-size call needs your key, so keep it in a server or a build step. --- Source: https://dogimage.dev/guides/dog-image-api-errors-and-quotas # Handle errors and quotas in the Dog Image API Every Dog Image API status code with its cause and fix, how the monthly full-size count works, which errors to retry, and how to budget a plan for the catalog. The Dog Image API answers every failed call with an HTTP status and a JSON body, and only two statuses are worth retrying: 502 and 503. The rest, including 429 when the monthly count is spent, need a change in your request or your plan. This page lists each code, the counting rule and a plan budget. ## The shape of an error Failures carry a body like this (the `choices` list is shortened here), and the `code` is the part to switch on: ```json { "error": { "code": "unknown_view", "message": "No view \"side\".", "choices": ["front", "front-left", "left"] } } ``` The `message` is for people and may be reworded, so match on `code`. On the image endpoint, the 400 and 404 replies add a `choices` list with the valid values. The breed detail endpoint, `GET /api/v1/breeds/{breed}`, returns the same JSON error with `unknown_breed` for a slug that does not exist. If your client also parses error bodies from a proxy, a corporate network or a captive portal, expect that some failures are not JSON. Read the status first and guard the parse. ## Status codes | Status | Code | Cause | What to do | |---|---|---|---| | 400 | `bad_size` | `size` is not `512`, `1024` or `full` | Fix the value. The reply lists the choices | | 401 | `missing_or_invalid_key` | No `Authorization` header, or a wrong key | Send `Authorization: Bearer `. Create a new key on the account page if the old one is lost | | 402 | `no_active_plan` | The key is valid but the account has no plan | Buy a plan, or use previews | | 404 | `unknown_breed`, `unknown_variant`, `unknown_view` | A slug does not exist | Read the valid slugs from `/api/v1/breeds` | | 429 | `quota_used_up` | The plan's full-size count for this month is spent | Stop. Wait for the next month or change plans | | 502 | `file_unavailable` | The file could not be served | Retry. It is not counted | | 503 | `plan_check_failed` | The plan could not be checked | Retry | ### 400 and 404 are mistakes in the request They never fix themselves. Log the code and the URL, then correct the slug. A 404 on a variant is common with the seven multi-color breeds: `default` always names the main set, and other color slugs come from the breed's `variants` list. ### 401 and 402 are about the account A 401 can also come from `/api/v1/usage`. A 402 means the plan lapsed. Access lasts until the paid month ends after a cancellation, and files you fetched while a plan was active stay usable in what you built with them. ### 429 ends the job Treat it as a stop signal, not a hiccup. Save your progress, record where you stopped, and finish the list after the count resets. Retrying will return the same 429 and gains nothing. ## How the monthly count works A plan counts distinct full-size images per calendar month, in UTC. One image is one view of one set at full size. - Fetching the same image again in the same month is free. - A 502 is not counted. - Previews are not full-size images, so they are not part of the count. - The `month` field of `/api/v1/usage` names the month in force, such as `2026-11`. The count is the only limit the API states. It does not state a per-request rate limit. Do not hot-link full-size files in pages. Download once, store the file, and serve it yourself. Read the numbers from the usage endpoint and the quota headers. Both are covered in the [Python guide](/guides/python-download-dog-breed-images): ```json { "month": "2026-11", "plan": "starter", "limit": 200, "used": 12, "remaining": 188 } ``` On the All images plan, `limit` and `remaining` are `null`, and the `X-Quota-*` headers read `unlimited`. With no plan, `plan` is `null` and `limit` is 0. ## A retry policy Retry 502 and 503. Never retry 400, 401, 402, 404 or 429. Three or four attempts with a growing pause is enough, since a file that is still unavailable after that needs a person to look at it. This helper follows the policy and leaves the decision about the final status to the caller: ```js const RETRY = new Set([502, 503]); export async function fetchWithPolicy(url, options = {}, tries = 4) { for (let attempt = 1; ; attempt++) { const res = await fetch(url, options); if (res.ok || !RETRY.has(res.status) || attempt === tries) return res; const wait = 500 * 2 ** (attempt - 1) + Math.random() * 250; await new Promise((resolve) => setTimeout(resolve, wait)); } } const res = await fetchWithPolicy( "https://dogimage.dev/api/v1/images/pug/default/front?size=full", { headers: { Authorization: `Bearer ${process.env.DOG_IMAGE_API_KEY}` } }, ); if (res.status === 429) { console.error("Quota used up for this month. Stop the job."); } else if (!res.ok) { const { error } = await res.json(); console.error(`${res.status} ${error.code}: ${error.message}`); } ``` The pause values are a suggestion, not a documented requirement. The jitter keeps parallel jobs from retrying in lockstep. The same rules in Python and Node.js appear in the [Node.js guide](/guides/nodejs-download-dog-images). ## Budget a plan against the catalog The catalog held 298 breeds and 329 sets on 2026-11-14, and every set has nine views. That makes 329 x 9 = 2,961 full-size images in total. Read the live numbers from `GET /api/v1/breeds` before you plan, since the catalog grows. | Plan | Price per month | Full-size images | What it covers | |---|---|---|---| | Starter | $19 | 200 | 22 complete sets (198 images), or one view of 200 sets | | Pro | $49 | 700 | 77 complete sets (693 images), or one view of every set with 371 images left | | All images | $89 | every one | the whole catalog, all 2,961 images | Some ways to plan, using those numbers: - **One portrait per breed.** The `head` view of the default set of each of 298 breeds is 298 images, which does not fit Starter and fits Pro in one month. - **A product with a few breeds.** Ten breeds with nine views each is 90 images, well inside Starter. - **A full mirror for your own app.** Starter or Pro can reach the same goal over several months while the plan stays active, because each month starts a new count and an image you already have never needs a second fetch. The All images plan does it in one month. The [license](/license) allows use inside your products and forbids republishing the files as a collection. Plans are bought on the [pricing page](/pricing) after sign-in and can be cancelled. Estimate your count from the list of files you will request, subtract files already on disk, and compare the result with `remaining`. ## Questions **Which Dog Image API errors should I retry?** Only 502 file_unavailable and 503 plan_check_failed. Every other error describes a problem a retry cannot fix, and a 429 means the monthly count is spent. **Does a failed request use up my quota?** A 502 is not counted. A plan counts distinct full-size images per calendar month, so a repeat of an image you already fetched in the same month is free. **When does the monthly count start over?** The count follows the calendar month in UTC. The usage reply shows the month it describes, for example 2026-11. **Which plan fits downloading the whole catalog?** The All images plan, which covers every full-size image in the catalog. Starter and Pro have fixed monthly counts of 200 and 700 images. --- Source: https://dogimage.dev/guides/how-many-dog-breeds-are-there # How many dog breeds are there? There is no single count of dog breeds. See what the FCI site and Wikipedia state, why the numbers differ, and how many breeds this catalog covers and why. It depends on who counts. Wikipedia's dog breed article gives "upward of 360" in one place and "around 450" in another, and its list page says "more than 400." The FCI, the international kennel federation, shows its breeds in groups but states no total on the pages I read. This catalog covers 298. ## What the FCI site states The Fédération Cynologique Internationale, or FCI, is a federation of national kennel organizations. I opened its [home page](https://www.fci.be) and its [nomenclature page](https://www.fci.be/en/Nomenclature/) on 2026-11-14 to look for a breed count. Neither page states one. The nomenclature page arranges the breeds recognized on a definitive basis in 10 groups, from sheepdogs and cattle dogs to sighthounds, and it lists a separate set of breeds recognized on a provisional basis. Each provisional entry has a date of provisional recognition and a validity date. The newest of those dates is in 2026, so the list moves. A count taken today would be out of date later. So the FCI is a good place to check one breed's status, and a poor place to find a single headline number. ## What Wikipedia states Wikipedia gives several figures, and they do not agree. That is useful in itself, because it shows the problem. On the [dog breed article](https://en.wikipedia.org/wiki/Dog_breed), read on 2026-11-14: - The opening says artificial selection has produced "upward of 360" globally recognized breeds. - A later section says "around 450" globally recognized dog breeds. - The section on purebreds says the FCI, with 98 members and contract partners, recognizes 356 purebreds. - An external link at the bottom points to the FCI nomenclature, described there as listing 339 breeds. That figure sits next to 356 in the same article, which shows how easily counts drift. On the [list of dog breeds](https://en.wikipedia.org/wiki/List_of_dog_breeds), read on the same date, the introduction says there are more than 400 extant modern dog breeds, and cites a 2024 study. The figures come with different citations. Treat each figure as a statement about one source on one date, not as a fact about all dogs. ## Why the numbers differ A breed is a human category, not a biological one. Counts depend on choices like these, which no source here settles for you: 1. **Who must recognize it.** A federation, a national club and a registry each keep their own list. A breed on one list may be absent from another. 2. **How rare breeds are treated.** Regional dogs, landraces and breeds still being developed may be counted, held as provisional, or left out. 3. **What counts as a separate breed.** Some lists treat sizes, coats or colors as one breed, others split them. The result is that "how many breeds" is really "how many breeds does this list include." Ask for the source and the date. ## How many breeds this catalog covers Dog Image API covers 298 breeds in 329 sets on 2026-11-14. Seven breeds come in several colors, which is why the sets outnumber the breeds. The live count is the length of the `breeds` array in [the breed list](/guides/dog-breeds-list-json), so read it from there. The list is drawn from Wikidata and ranked by search interest. That means a breed is in the catalog because people search for it and because the data source describes it, not because a kennel club has approved it. It is not a registry, and it does not decide what a breed is. A breed missing here is not a statement that it does not exist. Every set in the catalog is an AI-generated illustration of one adult dog, so the catalog is a visual reference and not a breed authority. See [what to know about AI-generated dog images](/guides/ai-generated-dog-images-what-to-know) before you use them for anything that depends on accuracy. ## What to write when you need a number If you must quote a count in a product or article: - Name the source, such as "the FCI" or "Wikipedia," and the date you read it. - Prefer a range or the word "hundreds" to a single exact figure unless a source gives one for your exact question. - Link to the source page so a reader can check it. - If you mean "breeds in your app," say that, and read the number from your own data. You can browse every breed in the catalog on the [all breeds page](/dogs). ## Questions **So what is the number of dog breeds?** There is no single number. Wikipedia gives figures from upward of 360 to around 450 globally recognized breeds, and counts of breeds recognized by one organization differ again. **How many breeds does the FCI recognize?** The FCI site, read on 2026-11-14, shows its breeds in groups and lists some as provisional, but it does not state a total on the pages I read. Wikipedia says the FCI recognizes 356 purebreds. **Does this catalog include every breed?** No. It covers 298 breeds, chosen from Wikidata and ranked by search interest. It is not a kennel club registry and makes no claim to be complete. **Why do counts differ between sources?** Each source decides what counts as a breed and who has to recognize it, so a federation, a club and a research study can each give a different total. --- Source: https://dogimage.dev/guides/how-to-get-dog-breed-images-for-an-app # How to get dog breed images for an app or site Compare five ways to source dog breed images: your own photos, stock libraries, photo APIs, user uploads and one illustration set. Includes a decision table. You have five realistic ways to get dog breed images: shoot them yourself, license them from a stock library, call a photo API, collect them from users, or use one standardized illustration set. Pick by what the images must do. Photos suit stories. A matching set suits interfaces that list many breeds. ## Option 1: your own photography **Good for:** a few breeds that matter a lot to your brand, or images of real animals you can name and show with permission. **Cost:** time, a camera, dogs, owners who agree, and editing. Every breed you add is another shoot. Cutting a clean background out of each photo is extra work. For a list of hundreds of breeds this stops being practical. **Choose it when** the picture must show a specific real animal, or when originality is the point of the page. ## Option 2: stock photo libraries **Good for:** a handful of strong editorial photos, such as a header image for an article. **Cost:** you usually license per image, and the terms differ by library and by license tier. Read what each license allows for your use, such as apps, print or resale of products, before you buy. Coverage of rarer breeds can be thin, and pictures from different photographers differ in light, crop and background, so a grid of them looks uneven unless you edit each one. **Choose it when** you need a few good photos and can accept a mixed look. ## Option 3: photo APIs **Good for:** many pictures of real dogs, random images for a demo, or search and filters, depending on the service. **Cost:** often free to start, with limits or terms that differ by provider. You still get varied photos, so a breed selector may need cropping rules of your own. Our [comparison of Dog CEO's Dog API, The Dog API and Dog Image API](/guides/dog-api-alternatives-compared) gives the details we could verify on each service's own pages. **Choose it when** real photos matter more than a uniform look. ## Option 4: user uploads **Good for:** pet profiles, where the owner's own dog is the content. **Cost:** you build upload, storage, resizing and moderation, and you handle the rights and privacy questions that come with other people's pictures. New users have no photo yet, so you still need something to show on day one. **Choose it when** the product is about the owner's own pet. Pair it with a default image for the empty state. ## Option 5: a standardized illustration set **Good for:** interfaces that list or compare many breeds: pickers, quizzes, size charts, cards, printed forms, games. **Cost:** free previews with a visible credit link, or a paid plan for full-size files. This is the option [Dog Image API](/docs) covers. The catalog held 298 breeds and 329 sets on 2026-11-14. Every set shows one adult dog from the same [nine views](/guides/the-nine-view-image-standard) on a square, transparent canvas. Previews are WebP at 512 and 1024 pixels. Full-size files are 2048 pixel PNGs. **Limits you should weigh:** these are AI-generated illustrations. They show no real dog, they are not a breed standard, and each set shows an adult in a standing pose (the head portrait aside). There is no random-image endpoint, no search and no upload. **Choose it when** consistency across breeds matters more than photographic realism. ## A short decision table | You need | Best fit | |---|---| | A real animal, named and shown | Your own photography | | A few editorial photos | Stock library | | Many real photos or random pictures | Photo API | | The owner's own pet | User uploads | | Every breed in the same view and format | Standardized illustration set | | A neutral image before the owner uploads | Illustration set, then user upload | These options combine well. A site can run photo articles, a matching illustration in its breed picker and an upload slot on the profile page. ## How to start with this API in three steps ### 1. Read the breed list The list needs no key and browsers on other sites may call it. Build your picker from it instead of hardcoding slugs, so new breeds appear without a deploy. ```bash curl https://dogimage.dev/api/v1/breeds ``` The reply has `views` and `breeds`. Each breed has a `slug` such as `golden-retriever`, a `default_variant` and a `crossbreed` flag. ### 2. Show a preview with its credit An image URL redirects to a WebP preview, which an `` tag follows. Pick a view id such as `front-left`, and use `default` or the breed's variant slug. ```html Illustration of a Samoyed Samoyed image: Dog Image API ``` Previews can be used commercially as long as that credit link sits next to the image or on the same page. ### 3. Download full-size files only if you need them Full-size PNGs need a key and a plan. Run the download on a server or in a build step, store the file yourself and read the key from `DOG_IMAGE_API_KEY`. ```bash curl -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ -o samoyed-front-left.png \ "https://dogimage.dev/api/v1/images/samoyed/default/front-left?size=full" ``` A plan counts distinct full-size images each month, and one image is one view of one set. The [pricing page](/pricing) lists the plans, and the [license](/license) says what you may do with the files. Browse the catalog on the [breeds page](/dogs) before you decide. ## Questions **What is the cheapest way to get dog breed images?** It depends on what you count. Free previews from this API cost no money but need a visible credit link. Your own photography costs no license fees but a lot of time. Stock libraries and photo APIs sit in between, and their terms vary by provider. **Can I mix photos and illustrations on one site?** Yes. Many sites use photos on article pages and illustrations in the interface. Label each so a reader can tell them apart. **Do I need an API key to try Dog Image API?** No. The breed list, the breed details and the 512 and 1024 pixel previews need no key. A key is only for full-size PNG files, and it must stay on a server. **Are the images photos of real dogs?** No. They are AI-generated illustrations of one adult dog per set, not real dogs and not a breed standard. --- Source: https://dogimage.dev/guides/dog-silhouettes-from-transparent-images # Make a dog silhouette for any breed Turn the transparent dog previews into clean silhouettes with one CSS filter or a canvas fill, pick any color, and export the result as a PNG. Runnable HTML. A dog silhouette is the filled outline of a dog in one flat color. Because every preview on this site has a transparent background, you can get one in a browser with a single CSS filter, or with a canvas fill that lets you pick any color and save a PNG. ## What you need - A preview URL for one breed and one view. You get it from `GET /api/v1/breeds/{breed}`, which needs no key. - A browser. Preview files allow cross-origin reads, so a page on any domain can draw them to a canvas. - The side view for the classic look. Its id is `left` (Left side) or `right` (Right side). The images are AI-generated illustrations, so the silhouette is an illustration too, not a trace of a real dog. ## Method 1: a CSS filter `filter: brightness(0)` sets every color channel to zero. Opaque pixels turn black and transparent pixels stay transparent. That is a silhouette. ```html Pug silhouette
    Pug, left side view Pug silhouette
    Pug image: Dog Image API ``` Save the file as `silhouette.html` and open it in a browser. The left image is the preview, the right one is the silhouette. The filter gives you black. To get another color with CSS alone you have to chain filters and tune them by eye, which is fiddly. The canvas method below is exact. ## Method 2: canvas and source-in Draw the preview on a canvas, then set `globalCompositeOperation` to `"source-in"` and fill a rectangle. With `source-in`, the new fill only shows where something already exists on the canvas. The transparent area stays empty and the dog's shape takes the new color. ```html Colored silhouette Samoyed image: Dog Image API ``` The canvas CSS background is only for your eyes. The exported PNG keeps a transparent background, because `toBlob` writes the canvas pixels and not the page styles. ## Why crossOrigin matters Drawing an image from another origin taints the canvas unless the server allows it and the image asks for it. This site allows cross-origin reads on preview files, and `img.crossOrigin = "anonymous"` is the request. Without it, `toBlob` throws a security error. Set the property before `src`, or the browser loads the image the first time without permission. Use the preview URLs from the JSON, not a redirected API URL, so the file you draw is the file the browser loads. ## Other views and breeds Change the two arguments to `load("beagle", "right")` or `load("dachshund", "left")`. Each dog fills at most 80% of a square canvas, so silhouettes come out centered with room around them. The head view makes a good round badge. See [the side view reference](/guides/dog-side-view-reference) for what the side view shows. ## Credit when you publish The exported PNG is a derivative of a preview. If you publish it, keep a visible link crediting Dog Image API on the same page. The [license](/license) is the reference. If you want a silhouette for print, the paid 2048 pixel PNGs give you a larger source. See [pricing](/pricing). ## Questions **Why does a silhouette work so well with these images?** The background is transparent, so only the dog's pixels carry color. A filter or a canvas fill that repaints those pixels gives you the outline of the dog and nothing else. **Why does my canvas export fail with a security error?** The image was loaded without cross-origin permission, so the browser marks the canvas as tainted. Set crossOrigin to anonymous on the image before you set its src. **Do I need to credit Dog Image API for a silhouette?** If you publish a page that shows or is built from a preview, keep the credit link on that page. The breed page gives you the exact snippet. **Which view makes the best silhouette?** The left side view (id left) or right side view (id right). The outline shows legs, tail, ears and snout at once. --- Source: https://dogimage.dev/guides/flutter-dog-breed-images # Show dog breed images in Flutter Load dog breed images in Flutter with the http package: model the JSON, show a preview with Image.network, and build a grid of the nine standard views. Add the `http` package, request `GET /api/v1/breeds/{breed}`, read each view's `preview_512` URL and pass it to `Image.network`. The nine views of one breed fit a three by three `GridView`. Previews need no key, and you show a credit link next to them. The images are AI-generated illustrations of one adult dog on a transparent square canvas. Because every view uses the same canvas, a grid of the nine looks like a turnaround sheet without any layout tricks. ## Keep the key out of the app A Flutter binary can be unpacked, so treat everything inside it as public. This app calls only keyless URLs: the catalog, the breed detail and the preview files. Full-size PNG files, 2048 pixels square, need a key. Fetch them from your backend, store them, and let the app load them from your storage. The [caching guide](/guides/cache-and-self-host-dog-images) covers that job. ## Set up ```bash flutter pub add http url_launcher ``` `http` makes the requests. `url_launcher` opens the credit link. ## Model classes The breed detail has one entry per variant, and each variant maps a view id to its URLs. Two small classes are enough. ```dart class ViewImages { ViewImages({required this.preview512, required this.preview1024}); final String preview512; final String preview1024; factory ViewImages.fromJson(Map json) => ViewImages( preview512: json['preview_512'] as String, preview1024: json['preview_1024'] as String, ); } class BreedSheet { BreedSheet({required this.slug, required this.name, required this.views}); final String slug; final String name; final Map views; // view id -> preview URLs factory BreedSheet.fromJson(Map json) { final variants = json['variants'] as List; final defaultSlug = json['default_variant'] as String?; final variant = variants.cast>().firstWhere( (v) => v['slug'] == defaultSlug, orElse: () => variants.first as Map, ); final rawViews = variant['views'] as Map; return BreedSheet( slug: json['slug'] as String, name: json['name'] as String, views: rawViews.map( (id, value) => MapEntry(id, ViewImages.fromJson(value as Map)), ), ); } } ``` The factory picks the breed's default variant, which is the main color set. If you want a color switcher, keep the whole `variants` list instead. ## Fetch one breed ```dart import 'dart:convert'; import 'package:http/http.dart' as http; const apiBase = 'https://dogimage.dev/api/v1'; Future fetchBreed(String slug) async { final response = await http.get(Uri.parse('$apiBase/breeds/$slug')); if (response.statusCode != 200) { // 404 unknown_breed lists the valid choices in the JSON body. throw Exception('Breed request failed: ${response.statusCode}'); } return BreedSheet.fromJson(jsonDecode(response.body) as Map); } ``` Slugs look like `pug`, `golden-retriever` or `german-shepherd`. Pass one from your own data, or read the list from `GET /api/v1/breeds` and let the user choose. ## Show one preview `Image.network` decodes WebP, as the [Flutter Image documentation](https://api.flutter.dev/flutter/widgets/Image-class.html) lists. Add a loading and an error builder so a slow connection does not leave a hole. ```dart Widget previewImage(String url) { return Image.network( url, fit: BoxFit.contain, loadingBuilder: (context, child, progress) => progress == null ? child : const Center(child: CircularProgressIndicator()), errorBuilder: (context, error, stack) => const Icon(Icons.pets), ); } ``` ## A grid of the nine views The catalog call also returns the nine view ids with their labels, in a stable order. For a quick screen you can list the ids yourself, as below. ```dart import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; const viewOrder = [ 'front', 'front-left', 'left', 'rear-left', 'rear', 'rear-right', 'right', 'front-right', 'head', ]; class BreedGridPage extends StatelessWidget { const BreedGridPage({super.key, required this.slug}); final String slug; @override Widget build(BuildContext context) { return FutureBuilder( future: fetchBreed(slug), builder: (context, snapshot) { if (snapshot.hasError) return const Center(child: Text('Could not load this breed.')); if (!snapshot.hasData) return const Center(child: CircularProgressIndicator()); final breed = snapshot.data!; return Scaffold( appBar: AppBar(title: Text(breed.name)), body: Column( children: [ Expanded( child: GridView.count( crossAxisCount: 3, padding: const EdgeInsets.all(8), mainAxisSpacing: 8, crossAxisSpacing: 8, children: [ for (final id in viewOrder) if (breed.views[id] != null) ColoredBox( color: Colors.black12, child: previewImage(breed.views[id]!.preview512), ), ], ), ), TextButton( onPressed: () => launchUrl( Uri.parse('https://dogimage.dev/dogs/${breed.slug}'), ), child: Text('${breed.name} image: Dog Image API'), ), ], ), ); }, ); } } ``` Open it with `BreedGridPage(slug: 'samoyed')`. The `ColoredBox` matters because the background of every image is transparent. Without it, a white dog on a white page disappears. ## Details worth knowing - Use `preview_512` in a grid cell and `preview_1024` when one image fills the screen width. - The preview files carry a one-year immutable cache header, so repeat views are cheap.- The credit is a link to the breed page, shown near the image. The text form is the breed name, then "image: Dog Image API". - A `404` with `unknown_view` means a view id is wrong. The nine ids are the ones in `viewOrder`, and the [docs](/docs) describe the API. If you later need print-quality files or a 2048 pixel hero, that is a paid, server-side step. See [pricing](/pricing) for the plans. ## Questions **Does Flutter's Image widget decode WebP?** Yes. The Flutter Image class lists WebP among its supported formats, so the 512 and 1024 pixel previews load with Image.network. **Where does the API key go in a Flutter app?** Nowhere. The catalog and the previews need no key. Full-size PNG files need one, and a server of yours should fetch those and hand them to the app. **Can the Flutter web build call the API?** Yes for the breed list, the breed detail and the preview files, because cross-origin reads are allowed. The full-size endpoint is for servers. **Do the nine views share a size?** Yes. Every view is a square canvas with a transparent background, so a three by three grid lines up without extra cropping. --- Source: https://dogimage.dev/guides/sync-dog-breed-images-to-s3 # Sync dog breed images to your own S3 bucket A Python and boto3 script that downloads each full-size dog breed PNG once, uploads it to S3 by breed, variant and view, skips existing keys, and reports usage. Sync dog breed images to S3 with a short Python script: for each wanted breed and view, check whether the key already exists in your bucket, download the full-size PNG once if it does not, upload it under `breed/variant/view.png`, and print your usage at the end. ## Why keep the files yourself Dog Image API sells full-size images by a monthly count of distinct images. One image is one view of one set at 2048 x 2048 pixels. Full-size files are meant to be downloaded once and stored on your side, and you should not link your pages to the API for them. That shapes the design: - Your app reads from your bucket, so it keeps working if you later change plans. - The script asks the API only for keys the bucket does not hold yet. - The key never leaves the machine that runs the script. It is not in a browser or an app. The images are AI-generated illustrations of one dog per set, in nine views, on a transparent background. If your app labels where an image came from, say so. ## Prepare Install the libraries and set three environment variables. The AWS credentials come from the usual boto3 chain, such as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, a profile, or a role. ```bash pip install boto3 requests export DOG_IMAGE_API_KEY="paste-your-key-here" export S3_BUCKET="my-dog-images" export AWS_DEFAULT_REGION="us-east-1" ``` The bucket should be private to start. Put a CDN or signed URLs in front of it when you serve the files. ## The script Save it as `sync_dog_images.py`. ```python import os import sys import time import boto3 import requests from botocore.exceptions import ClientError BASE = "https://dogimage.dev" BUCKET = os.environ["S3_BUCKET"] API_KEY = os.environ["DOG_IMAGE_API_KEY"] PREFIX = "dogs" # What you want to keep. The breed's main set is used for every breed. BREEDS = ["pug", "beagle", "samoyed"] VIEWS = ["front", "left", "head"] s3 = boto3.client("s3") class QuotaUsedUp(Exception): pass def error_text(resp): try: err = resp.json()["error"] return f"{resp.status_code} {err['code']}: {err['message']}" except (ValueError, KeyError): return f"{resp.status_code} {resp.reason}" def already_stored(key): try: s3.head_object(Bucket=BUCKET, Key=key) return True except ClientError as err: if err.response["Error"]["Code"] in ("404", "NoSuchKey", "NotFound"): return False raise def fetch_full(breed, variant, view, attempts=4): url = f"{BASE}/api/v1/images/{breed}/{variant}/{view}" for attempt in range(1, attempts + 1): resp = requests.get( url, params={"size": "full"}, headers={"Authorization": f"Bearer {API_KEY}"}, timeout=60, ) if resp.status_code == 200: return resp.content, resp.headers.get("X-Quota-Remaining") if resp.status_code == 429: raise QuotaUsedUp(error_text(resp)) if resp.status_code in (502, 503) and attempt < attempts: time.sleep(2**attempt) continue raise RuntimeError(error_text(resp)) def print_usage(): resp = requests.get( f"{BASE}/api/v1/usage", headers={"Authorization": f"Bearer {API_KEY}"}, timeout=30, ) if resp.status_code != 200: print("Usage check failed:", error_text(resp)) return u = resp.json() limit = u["limit"] if u["limit"] is not None else "no limit" print(f"Usage for {u['month']} on plan {u['plan']}: {u['used']} used, limit {limit}.") def main(): catalog = requests.get(f"{BASE}/api/v1/breeds", timeout=30).json() by_slug = {b["slug"]: b for b in catalog["breeds"]} uploaded = skipped = 0 remaining = None failed = False try: for breed in BREEDS: if breed not in by_slug: sys.exit(f"Unknown breed slug: {breed}") variant = by_slug[breed]["default_variant"] for view in VIEWS: key = f"{PREFIX}/{breed}/{variant}/{view}.png" if already_stored(key): skipped += 1 continue data, remaining = fetch_full(breed, variant, view) s3.put_object( Bucket=BUCKET, Key=key, Body=data, ContentType="image/png", CacheControl="public, max-age=31536000, immutable", ) uploaded += 1 print(f"uploaded s3://{BUCKET}/{key}") except QuotaUsedUp as err: print(f"Stopping early: {err}") except RuntimeError as err: print(f"Stopped on an error: {err}") failed = True print(f"{uploaded} uploaded, {skipped} already in the bucket.") if remaining is not None: print(f"X-Quota-Remaining on the last download: {remaining}") print_usage() sys.exit(1 if failed else 0) if __name__ == "__main__": main() ``` Run it with `python sync_dog_images.py`. ## How it decides what to fetch ### The key layout Each file lands at `dogs///.png`, for example `dogs/pug/fawn/front.png`. The script reads `default_variant` from the breed list and uses that real color slug, so the name in your bucket does not depend on what "default" means later. The API accepts the color slug in the same position as `default`. ### Skipping what you hold `head_object` is a cheap check that returns a not-found error when the key is missing. Only that error means "download it". Any other error, such as a permission problem, is raised so you see it instead of re-downloading everything. ### Quota, 429 and retries Every full-size reply carries `X-Quota-Limit`, `X-Quota-Used` and `X-Quota-Remaining`. The script keeps the last remaining value and prints it. On the All images plan the header reads `unlimited`, which is why it is printed as text. A `429 quota_used_up` ends the loop without a traceback. Files already uploaded stay, and the final report still runs. A `502` or `503` is retried after 2, 4 and 8 seconds. The [errors and quotas reference](/guides/dog-image-api-errors-and-quotas) covers the rest. ### The usage report `GET /api/v1/usage` needs the key and returns `month`, `plan`, `limit`, `used` and `remaining`. On the All images plan, `limit` is `null`, and the script prints "no limit". This call is the number to watch before you add more breeds. Check [pricing](/pricing) for the plan sizes. ## Serving from the bucket Your own copy of a full-size file needs no credit in your product. The [license](/license) still forbids reselling or republishing the images as a collection, a dataset or an image library, so do not make the bucket public as a download source. For caching ideas around previews and stored files, see [cache and self-host dog images](/guides/cache-and-self-host-dog-images). ## Questions **Can I serve the S3 files straight to my users?** Yes, in your own product. Full-size files may be used in apps, sites, documents, print and packaging, without credit. You may not resell or republish them as a collection, dataset or image library, so keep the bucket for your own app's use. **What if the script stops halfway?** Run it again. It checks every key before it downloads, so finished uploads are skipped. The API counts a distinct image once per month, so re-fetching something it already served that month is also free. **Does a failed download use quota?** A 502 file_unavailable reply is not counted, and the script retries it. A 503 plan_check_failed reply is also retried. Only a successful full-size fetch of a new image in the month adds to the count. **Do I need S3 specifically?** No. Any object store with a put and a head call works the same way. Swap the two boto3 calls for your provider's client and keep the key layout and the skip logic. --- Source: https://dogimage.dev/guides/the-nine-view-image-standard # The nine-view standard for dog breed images How every dog breed image set is built: nine named views, a square transparent canvas, an 80% fill limit, one ground line, file formats and API naming. Every set in Dog Image API shows one adult dog from nine fixed views on a square, transparent canvas. The dog fills at most 80% of the canvas and is centered, and standing views share one ground line. Every breed follows the same rules, so any two sets can sit side by side. The images are AI-generated illustrations, not photos of real dogs. The standard describes how they are framed, not what a real dog of the breed must look like. ## The nine views Each view has an id, which you use in the API, and a label. | Id | Label | |---|---| | `front` | Front | | `front-left` | Front three-quarter, left | | `left` | Left side | | `rear-left` | Rear three-quarter, left | | `rear` | Rear | | `rear-right` | Rear three-quarter, right | | `right` | Right side | | `front-right` | Front three-quarter, right | | `head` | Head portrait | Eight of the views go around the dog: front, three-quarter, side and rear. The ninth is a head portrait. Check one image of your first breed before you build a layout that depends on left and right. You can see every breed from one angle on a [view gallery](/views/left). ## The canvas and framing - The canvas is square. - The background is transparent. - The dog is centered. - The dog fills at most 80% of the canvas, which leaves a margin on every side. - Standing views share one ground line, so the paws of a dog in the `left` view and in the `rear-right` view rest at the same height. - The life stage is adult, and the pose is standing, except for the head portrait. The margin is a practical choice. A dog that never touches the edge can be scaled, rotated a few degrees or given a shadow without clipping. The shared ground line means a row of different breeds looks like it stands on one floor, with no manual alignment. ## File formats and sizes | Use | Format | Size | Cost | |---|---|---|---| | Preview | WebP, transparent | 512 x 512 and 1024 x 1024 pixels | Free, with a visible credit link | | Full size | PNG, transparent (RGBA, sRGB) | 2048 x 2048 pixels, about 1 to 7 MB | Paid plan | Preview files are served under `/img/...` with a one-year immutable cache header, so browsers and CDNs can keep them. Full-size files are meant to be downloaded once and stored on your side. Do not hot-link the API from a page. Previews may be hot-linked. See [pricing](/pricing) and the [license](/license) for the terms. ## Naming in the API An image is addressed by three parts: breed, variant and view. ```text /api/v1/images/{breed}/{variant}/{view} ``` - `{breed}` is a slug, such as `golden-retriever` or `samoyed`. - `{variant}` is a color slug, such as `fawn`, or the word `default` for the breed's main set. A variant's `kind` is `color`, `pattern` or `coat`. - `{view}` is one of the nine ids above. A preview request redirects to the WebP file: ```bash curl -I "https://dogimage.dev/api/v1/images/pug/fawn/front?size=512" ``` That returns a `302` redirect. The size can be `512` or `1024`, and 1024 is the default. The full-size PNG is requested with `size=full` and needs a key, so run it from a server, never from browser code: ```bash curl -s -H "Authorization: Bearer $DOG_IMAGE_API_KEY" \ "https://dogimage.dev/api/v1/images/pug/fawn/front?size=full" \ -o pug-fawn-front.png ``` To find every valid breed and variant slug, read [the breed list](/guides/dog-breeds-list-json). Asking for an unknown breed, variant or view returns a `404` that lists the valid choices. ## Why one standard helps A product that shows many breeds has a consistency problem before it has a data problem. Pictures from many sources differ in crop, angle, light and background, and each difference becomes work for you. With a fixed standard: - **Layouts are predictable.** Every image is square, so a grid has no per-item sizing rules. - **Backgrounds are not your problem.** The transparency lets you place a dog on a card, a map or a print layout in any color. - **Comparison is easier.** Two breeds shown from the same view, with the same framing rules, are simpler to compare than photos taken at random angles. - **Code is simple.** The nine ids are fixed, so a viewer can loop over them and never ask which angles a breed has. - **Every set is checked.** Each set passes an automatic check of its nine views before it is published. The standard does not give you puppies, action poses or real photographs, and it is not a breed standard from a kennel club. If you need those, look at [other dog APIs](/guides/dog-api-alternatives-compared). ## Questions **How many views does each set have?** Nine: front, front-left, left, rear-left, rear, rear-right, right, front-right and head. Every breed set has all nine. **What size are the images?** Free previews are WebP at 512 and 1024 pixels. Paid full-size files are PNG at 2048 by 2048 pixels. All are square with a transparent background. **Are the dogs puppies or adults?** The standard has one life stage, adult. Dogs are shown in a standing pose, except in the head portrait. **Why does a breed have more than one set?** Seven breeds come in several colors, and each color is its own set with the same nine views. Variants are named by color, pattern or coat. --- Source: https://dogimage.dev/guides/dog-images-in-design-mockups # Transparent dog images in design mockups How designers use transparent dog previews in mockup tools: drag in the WebP, place on brand colors, match views across breeds, credit, and upgrade for print. Transparent dog previews drop into a mockup the way a sticker does. Drag the WebP onto your artboard, put it on a brand color, and match the view across breeds so the set looks consistent. Credit the source when the mockup is published, and switch to the paid PNG for print. ## Why a designer would use these Placeholder photos come with backgrounds, lighting and crops that fight your layout. These images are AI-generated illustrations of one dog, with a transparent background, on a square canvas. You get a pet shop poster, an app onboarding screen or a packaging render with a dog that fits any color you place behind it. They are illustrations, not photographs. Say so if your client expects real photos, and use real photos when the real thing matters. ## Get the file 1. Pick a breed on [the breeds page](/dogs), for example [Samoyed](/dogs/samoyed). 2. Choose the view you want and save the 1024 pixel preview. 3. Repeat for each breed in your set. The preview is a transparent WebP. For a mockup, 1024 is the right size: it stays sharp when you scale it up on a big artboard. ## Drag it into your tool Most current design and mockup tools accept WebP and keep its transparency. Drop the file onto the canvas, and the dog sits with its background transparent. If your tool rejects WebP, convert the file to PNG on your computer. Open it in an image editor, or use a converter, then export as PNG with transparency on. Check the result on a colored background to make sure the edges are clean. The guide on [WebP and PNG for transparent images](/guides/webp-vs-png-for-transparent-images) covers the difference. ## Place the dog on brand colors Because the background is transparent, you can test colors fast. - Duplicate the artboard and change only the background fill. - Test the lightest and the darkest dog in your set. A white Samoyed disappears on a pale background, and a black dog disappears on a dark one. - Give the dog a shape behind it, such as a circle or a rounded square, in a color from your palette. The dog then reads as part of the layout. Each dog fills at most 80% of a square canvas, centered. That leaves room around the dog, so cropping and scaling are predictable. ## Build a consistent set across breeds A set looks professional when only the breed changes. Make these choices once and apply them to every dog: - **One view.** Use the left side view for all of them, or the head portrait for all of them. The nine views are `front`, `front-left`, `left`, `rear-left`, `rear`, `rear-right`, `right`, `front-right` and `head`. - **One size.** Scale every dog to the same width on the artboard. - **One ground line.** Standing views share one ground line, so side views line up when you align them by the bottom of the frame. - **One mirror rule.** The `left` and `right` views exist for every breed. Use `right` for dogs that should face the other way instead of flipping a file. The [nine-view standard](/guides/the-nine-view-image-standard) explains how every set is built to the same rules, which is why this works. Remember that each dog fills its own frame. A Pug and a Great Dane come out at about the same size in the frame, so scale them by hand if your mockup needs to suggest the real difference. ## Credit when you publish A private mockup in a design file is a draft. When it becomes public, for example in a case study, a pitch posted online or a live page, the previews need a visible link that credits Dog Image API next to the image or on the same page. Each breed page gives the snippet. Ask the person who publishes the mockup to carry it over. ```html Samoyed image: Dog Image API ``` ## Move to print Previews are 512 and 1024 pixels. They suit screens. For packaging, posters or anything printed, switch to the paid full-size files: PNG, transparent, 2048 x 2048 pixels, with no credit needed. Download each file once and store it with your design files. See [pricing](/pricing) for plans and the [license](/license) for what the files may be used for. When the layout is final, replace each preview with its full-size file. Same view, same breed, same placement, higher resolution. ## Questions **Can I drag the WebP file into Figma or Sketch?** Many design tools read WebP, but not all of them. If yours does not, convert the preview to PNG on your own computer first. **Do I need to credit Dog Image API in a mockup I show to a client?** A private mockup is a draft. When the mockup is published, the credit link must be visible next to the image or on the same page. **Should I use 512 or 1024 pixel previews?** Use 1024 for mockups. It holds up when you scale a dog to fill a hero area, and the 512 file is better for small icons. **Can I use the same dog in print?** For print, use the paid 2048 pixel PNG. The previews are too small for most print sizes. --- Source: https://dogimage.dev/guides/webp-vs-png-for-transparent-images # WebP vs PNG for transparent images Both WebP and PNG keep an alpha channel. Compare file size, browser support and when to choose each, plus what this dog image site serves in each format. Both WebP and PNG store a transparent background. PNG is lossless and works almost everywhere, including design and print tools. WebP is usually smaller on the web and is supported by current browsers. On this site, the free previews are WebP and the paid full-size files are PNG. ## What each format is PNG is a lossless format with full alpha transparency. MDN's [image file type guide](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types) describes it as widely supported by all major browsers and as a good choice when you need transparency or a precise copy of a source image. WebP was made for the web and can be lossless or lossy. The WebP project's [overview page](https://developers.google.com/speed/webp) says both its lossless and lossy modes support transparency. Lossless WebP stores the image with no change. Lossy WebP gives up some detail to save bytes. ## Transparency in both You do not have to choose a format to keep a transparent background. Both carry an alpha channel, which stores how opaque each pixel is. That is what lets a soft edge, such as fur, blend into any color behind it. JPEG is the one to avoid for transparent images, because it has no alpha channel. MDN states this directly in the guide linked above. ## File size The WebP project's overview gives these figures from its own tests: - Lossless WebP files are 26% smaller than PNG files. - Lossless WebP supports transparency at a cost of 22% additional bytes. - Lossy WebP with transparency is typically about three times smaller than PNG. These are the project's numbers for its test images. They are a guide, not a promise for your image. An illustration with flat colors and an image with fine texture compress differently, so compress a few of your own files and measure. A trade-off follows. Lossy WebP is the smallest, but the loss is permanent. If you edit an image many times, start from a lossless file, and export a small WebP only at the end. ## Browser support The caniuse page for [WebP](https://caniuse.com/webp) lists first full support in Chrome 32, Firefox 65, Edge 18 and Safari 16.0, with partial support in earlier Chrome versions (9 to 31) and in Safari 14 to 15.6. It reported global usage of about 97% when this guide was written. MDN's guide notes that Safari on macOS needs Safari 14 or later and macOS Big Sur or newer for WebP. PNG is supported by all major browsers. If you must serve very old browsers, use a fallback with the `picture` element, as MDN's [`picture` page](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/picture) shows: ```html Dog seen from the side ``` A browser that cannot read WebP skips the first line and loads the PNG. ## Outside the browser Support is thinner outside web pages. Some print tools, older office software, and some design or game tools expect PNG, and some do not open WebP at all. Check the format list of the tool you need. If a WebP file does not open, convert it, for example with the WebP project's `dwebp` command, whose [manual](https://developers.google.com/speed/webp/docs/dwebp) shows PNG as the default output: ```bash dwebp dog.webp -o dog.png ``` ## When to pick which | Situation | Pick | |---|---| | A web page or app screen, where load time matters | WebP | | A file you will edit again | PNG | | A tool, printer or store that lists PNG | PNG | | A site that must work in very old browsers | PNG, or WebP with a PNG fallback | | Many small images on one page | WebP | ## What this site serves Dog Image API serves both, for different jobs: - **Free previews** are transparent WebP files at 512 x 512 and 1024 x 1024 pixels. They are free with a visible credit link and can be hot-linked. - **Full-size files** are transparent PNG files at 2048 x 2048 pixels, with RGBA color in sRGB. They are about 1 to 7 MB each and come with a paid plan. The previews suit web pages. The PNG suits editing, print and tools that require it. See [pricing](/pricing) for the plans, and the guide to [transparent dog PNG images](/guides/dog-png-transparent-background) for saving and converting files. ## Questions **Does WebP support transparency like PNG?** Yes. Both formats can store an alpha channel. WebP does it in lossless and lossy modes, according to the WebP project pages. **Is WebP always smaller than PNG?** Not always. The WebP project says lossless WebP is 26% smaller than PNG in its test set. The result for your own image depends on its content. **Do all browsers show WebP?** Current versions of the major browsers do. Caniuse lists first support in Chrome 32, Firefox 65, Edge 18 and Safari 16, with partial support in some earlier Chrome and Safari versions. **Which format should I send to a printer?** Ask the printer. Many print workflows expect PNG or another format they list. Check their file requirements before you choose.