Error Handling
Status codes, edge enforcement, and recovery patterns
Response Format
Success responses are 302 redirects. Error responses are plain text with the appropriate HTTP status code.
HTTP/1.1 404 Not Found
Content-Type: text/plain
Logo not foundStatus Codes
| Status | Meaning | Typical Causes |
|---|---|---|
| 302 | Redirect to logo asset | Successful lookup |
| 400 | Bad request | Missing/invalid domain |
| 401 | Unauthorized | Missing token or token not starting with pk_ |
| 403 | Forbidden | Domain restriction failure or attribution not satisfied |
| 404 | Not found | No logo stored for that domain |
| 429 | Too many requests | Per-minute limit reached |
| 500 | Internal error | Backend lookup failure |
Common Errors
401 Unauthorized
Body: Authentication failed
Fixes:
- Ensure
tokenquery param is present and starts withpk_. - Verify the key is active in the portal (not revoked/suspended).
403 Forbidden
Body can be Domain not allowed or Access denied.
Causes:
- Origin/Referer not on the allowlist for this key.
- Free key without valid attribution status.
Fixes:
- Add your domain(s) to the key allowlist or disable restrictions.
- Complete attribution for the Free tier or upgrade.
404 Not Found
Body: Logo not found
Fixes:
- Double-check the domain spelling.
- Provide a local fallback image in your UI.
429 Too Many Requests
Headers include Retry-After and X-RateLimit-Reset.
Fixes:
- Wait for
Retry-Afterseconds and retry with exponential backoff. - Reduce duplicate requests; cache logos locally.
500 Internal Server Error
Rare, but can occur during backend lookups.
Fixes:
- Retry with backoff.
- If repeated, contact support with timestamp and domain.
Handling Errors in Code
async function getLogo(domain, apiKey) {
const res = await fetch(`https://logos.getquikturn.io/${domain}?token=${apiKey}`);
if (res.status === 404) return '/images/fallback-logo.png';
if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After') || 1);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
return getLogo(domain, apiKey);
}
if (!res.ok) throw new Error(`Logo fetch failed: ${res.status}`);
return URL.createObjectURL(await res.blob());
}import time, requests
def get_logo(domain: str, api_key: str) -> bytes:
res = requests.get(f"https://logos.getquikturn.io/{domain}?token={api_key}")
if res.status_code == 404:
raise FileNotFoundError("Logo not found")
if res.status_code == 429:
retry_after = int(res.headers.get("Retry-After", "1"))
time.sleep(retry_after)
return get_logo(domain, api_key)
res.raise_for_status()
return res.contentThe SDK provides typed error classes for exhaustive handling. Retries are built in — you only need to handle final failures:
import { QuikturnLogos } from "@quikturn/logos/client";
import { LogoError } from "@quikturn/logos";
const client = new QuikturnLogos({ token: "pk_live_xxx" });
try {
const { url } = await client.get("example.com");
} catch (err) {
if (err instanceof LogoError) {
switch (err.code) {
case "NOT_FOUND_ERROR":
// err.domain — show fallback
break;
case "RATE_LIMIT_ERROR":
// err.retryAfter, err.remaining, err.resetAt
break;
case "QUOTA_EXCEEDED_ERROR":
// err.retryAfter, err.limit, err.used
break;
case "AUTHENTICATION_ERROR":
// Check API key
break;
case "FORBIDDEN_ERROR":
// err.reason (e.g., "tier_too_low")
break;
case "DOMAIN_VALIDATION_ERROR":
// err.domain — invalid format
break;
case "SCRAPE_TIMEOUT_ERROR":
// err.jobId, err.elapsed
break;
case "BAD_REQUEST_ERROR":
case "NETWORK_ERROR":
case "SERVER_ERROR":
break;
}
}
}SDK Error Classes
| Error Class | Code | HTTP Status | Extra Properties |
|---|---|---|---|
DomainValidationError | DOMAIN_VALIDATION_ERROR | — | domain |
RateLimitError | RATE_LIMIT_ERROR | 429 | retryAfter, remaining, resetAt |
QuotaExceededError | QUOTA_EXCEEDED_ERROR | 429 | retryAfter, limit, used |
AuthenticationError | AUTHENTICATION_ERROR | 401 | — |
ForbiddenError | FORBIDDEN_ERROR | 403 | reason |
NotFoundError | NOT_FOUND_ERROR | 404 | domain |
BadRequestError | BAD_REQUEST_ERROR | 400 | — |
ScrapeTimeoutError | SCRAPE_TIMEOUT_ERROR | — | jobId, elapsed |
All errors extend LogoError and include a code property for exhaustive switch statements.
When to Contact Support
- Persistent 403s despite correct domains and attribution.
- Repeated 500s for the same domain.
- Unexpected 401s for keys confirmed active in the portal.