img.logo.dev) returns an image on success; the REST APIs (api.logo.dev) return JSON. If a logo loads with a 200 but looks wrong (a white box, a monogram, or a blurry image), see troubleshooting.
Branch on the status code, not the response body. Error bodies are JSON with a short, human-readable message, under a
msg key for auth, plan, and rate-limit errors, or an err key for bad requests and lookups. The key and the text can change, so don’t parse them.Status codes
Authentication errors (401)
The Logo API (img.logo.dev) takes a publishable key (pk_…) in the token query parameter. A request with no token returns 401:
api.logo.dev) take a secret key (sk_…) in the Authorization header. Missing it returns 401:
Plan errors
Some endpoints require a paid plan:- Describe API: on a free account, returns
401{ "msg": "api not available for free accounts…" }.
402, not a 403.
Not found vs. still indexing (202)
For the Describe and Brand APIs, a domain we haven’t indexed yet returns 202, not 404. We start fetching it, and you can retry shortly:
404 { "err": "not found" } means the domain is known but has no data (or is blocked).
Each call counts as one request, including the 202 and any retries. Back off a few seconds between tries so you don’t spend extra requests on repeated 202s. See rate limits.
Missing logos
By default, an image request for a domain with no logo returns200 with a generated monogram, so images never break in your UI. To detect and handle missing logos yourself, request fallback=404:
Default: monogram fallback (200)
Opt into 404 for missing logos
Out of credits (402)
The Brand API is prepaid: each request uses 5 credits. When your balance drops below that cost, requests return 402 { "msg": "…" } until your plan’s monthly credit grant renews or you top up. This is a different condition from a 429: it’s about your credit balance, not a request limit.
What to do next depends on your plan. On Pro, Enterprise, or a custom contract, buy credits on the billing page. On every other plan, upgrade to Pro: it raises the monthly grant from 500 credits to 15,000 and unlocks credit purchases. See credits.
Rate limits (429)
If you exceed your plan’s request limit, the API returns 429 { "msg": "…" }. This applies to the monthly request pool on the free tier. On paid plans, pool enforcement is soft. We email you before acting, so you generally won’t hit a 429 unexpectedly. Running out of the Brand API’s prepaid credits returns a 402 instead. See rate limits for details.
Response headers
Logo.dev returns noRetry-After, X-RateLimit-*, or quota headers on any endpoint. There’s nothing to read for a server-suggested backoff or remaining requests, so set your own policy:
202(still indexing): there’s no fixed retry count to read from the response. Back off a few seconds between tries, and cap it at a handful of attempts rather than looping indefinitely if a domain never resolves.402(out of credits): retrying doesn’t help; requests resume once you top up on the billing page or your monthly credits renew.429(over limit): see rate limits for how enforcement differs by plan.
FAQs
Why am I getting a 401?
Why am I getting a 401?
Your request is missing a token or using the wrong key. The Logo API needs a
publishable key (
pk_) as ?token=; the REST APIs need a secret key (sk_)
as Authorization: Bearer. Check the API keys guide.Why does a brand-data request return 202 instead of the data?
Why does a brand-data request return 202 instead of the data?
The domain isn’t in our index yet. The
202 kicks off a fetch. Retry the
request in a few seconds and it’ll resolve once indexed.Why does an unknown company still return an image?
Why does an unknown company still return an image?
By design. Missing logos return a
200 monogram so your UI never shows a
broken image. Add fallback=404 if you’d rather handle missing logos
yourself.When do I get a 429?
When do I get a 429?
When you exceed your plan’s monthly request pool on the free tier. On paid
plans, pool limits are soft and you’re emailed before any enforcement.
The Brand API’s credits are metered separately from the request pool: running
out of credits returns a
402 rather than a pool 429.
See rate limits.Should I parse the error message?
Should I parse the error message?
No. Branch on the HTTP status code. The JSON body is for debugging. The key
(
msg or err) and the wording can change.