Guides / Reference

Handle errors and quotas in the Dog Image API

By the Dog Image API team. Updated .

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:

{ "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 <key>. 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:

{ "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:

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.

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 allows use inside your products and forbids republishing the files as a collection.

Plans are bought on the pricing page 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.

Next steps