Browser Client
Client-side SDK with blob URLs, retries, scrape polling, and event listeners
Installation
npm install @quikturn/logosimport { QuikturnLogos } from "@quikturn/logos/client";Creating a Client
const client = new QuikturnLogos({
token: "pk_live_xxx", // Required — publishable key
baseUrl: undefined, // Optional — override base URL
maxRetries: 2, // Optional — retry count on transient errors
});Only publishable keys (pk_* or qt_*) are accepted. Secret keys will throw an error.
Methods
client.get(domain, options?)
Fetches a logo and returns a blob URL suitable for <img> tags.
const { url, blob, contentType, metadata } = await client.get("stripe.com", {
size: 256,
format: "webp",
greyscale: false,
theme: "light",
});
// Use the blob URL in your UI
document.querySelector("img").src = url;Returns:
interface BrowserLogoResponse {
url: string; // blob: URL — revoked on destroy()
blob: Blob; // Raw image data
contentType: string; // MIME type (e.g., "image/webp")
metadata: LogoMetadata;
}client.getUrl(domain, options?)
Returns a plain URL string without making a network request. Useful for <img> tags where the browser handles the fetch.
const url = client.getUrl("stripe.com", { size: 128 });
// → "https://logos.getquikturn.io/stripe.com?token=pk_live_xxx&size=128"client.destroy()
Revokes all blob URLs and cleans up event listeners. Call this when unmounting or when the client is no longer needed.
client.destroy();Blob URLs consume memory. Always call destroy() to prevent leaks, especially in SPAs.
Scrape Polling
When the API doesn't have a logo cached, it can scrape the company's website in the background. The SDK polls automatically and fires a progress callback:
const { url } = await client.get("new-startup.com", {
scrapeTimeout: 15000, // Wait up to 15s for scrape
onScrapeProgress: (event) => {
console.log(event.status, event.progress); // "pending", 42
},
});The onScrapeProgress callback receives:
interface ScrapeProgressEvent {
status: "pending" | "complete" | "failed";
progress?: number; // 0–100
logo?: { id: number; url: string; companyId: number; companyName: string };
error?: string;
}If the scrape completes within the timeout, the logo is returned. If it times out, a ScrapeTimeoutError is thrown.
Event Listeners
Monitor rate limits and quota usage to show warnings before hitting limits:
client.on("rateLimitWarning", (remaining, limit) => {
console.warn(`Rate limit: ${remaining}/${limit} remaining`);
});
client.on("quotaWarning", (remaining, limit) => {
console.warn(`Monthly quota: ${remaining}/${limit} remaining`);
});
// Remove listeners
client.off("rateLimitWarning", handler);Response Metadata
Every response includes metadata about caching, rate limits, and transformations:
interface LogoMetadata {
cache: { status: "HIT" | "MISS" };
rateLimit: { remaining: number; reset: Date };
quota: { remaining: number; limit: number };
transformation: {
applied: boolean;
width?: number;
greyscale?: boolean;
gamma?: number;
};
}Error Handling
The browser client throws typed errors that you can handle exhaustively:
import { LogoError } from "@quikturn/logos";
try {
const { url } = await client.get("example.com");
} catch (err) {
if (err instanceof LogoError) {
switch (err.code) {
case "RATE_LIMIT_ERROR":
// err.retryAfter, err.remaining, err.resetAt
break;
case "NOT_FOUND_ERROR":
// Show fallback
break;
case "AUTHENTICATION_ERROR":
// Check API key
break;
case "DOMAIN_VALIDATION_ERROR":
// Invalid domain format
break;
}
}
}See Error Handling for the full error reference.
Full Example
import { QuikturnLogos, LogoError } from "@quikturn/logos/client";
const client = new QuikturnLogos({ token: "pk_live_xxx" });
// Monitor usage
client.on("rateLimitWarning", (remaining) => {
if (remaining < 10) showRateLimitBanner();
});
async function displayLogo(domain: string, imgEl: HTMLImageElement) {
try {
const { url } = await client.get(domain, { size: 128, format: "webp" });
imgEl.src = url;
} catch (err) {
if (err instanceof LogoError && err.code === "NOT_FOUND_ERROR") {
imgEl.src = "/fallback-logo.svg";
} else {
throw err;
}
}
}
// Clean up on page unload
window.addEventListener("beforeunload", () => client.destroy());