Quikturnv1.0
SDKs

Browser Client

Client-side SDK with blob URLs, retries, scrape polling, and event listeners

Installation

npm install @quikturn/logos
import { 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());

On this page